Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
NWWriter_OpenDrive.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2011-2024 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
19// Exporter writing networks using the openDRIVE format
20/****************************************************************************/
21#include <config.h>
22
23#include <ctime>
24#include "NWWriter_OpenDrive.h"
27#include <netbuild/NBEdgeCont.h>
28#include <netbuild/NBNode.h>
29#include <netbuild/NBNodeCont.h>
38#include <regex>
39
40#define INVALID_ID -1
41
42//#define DEBUG_SMOOTH_GEOM
43#define DEBUGCOND true
44
45#define MIN_TURN_DIAMETER 2.0
46
47#define ROAD_OBJECTS "roadObjects"
48
49// ===========================================================================
50// static members
51// ===========================================================================
53bool NWWriter_OpenDrive::LHLL(false);
54bool NWWriter_OpenDrive::LHRL(false);
55
56// ===========================================================================
57// method definitions
58// ===========================================================================
59// ---------------------------------------------------------------------------
60// static methods
61// ---------------------------------------------------------------------------
62void
64 // check whether an opendrive-file shall be generated
65 if (!oc.isSet("opendrive-output")) {
66 return;
67 }
68 const NBNodeCont& nc = nb.getNodeCont();
69 const NBEdgeCont& ec = nb.getEdgeCont();
70 const bool origNames = oc.getBool("output.original-names");
71 lefthand = oc.getBool("lefthand");
72 LHLL = lefthand && oc.getBool("opendrive-output.lefthand-left");
73 LHRL = lefthand && !LHLL;
74 const double straightThresh = DEG2RAD(oc.getFloat("opendrive-output.straight-threshold"));
75 // some internal mapping containers
76 int nodeID = 1;
77 int edgeID = nc.size() * 10; // distinct from node ids
80 //
81 OutputDevice& device = OutputDevice::getDevice(oc.getString("opendrive-output"));
82 OutputDevice::createDeviceByOption("opendrive-output", "OpenDRIVE");
83 time_t now = time(nullptr);
84 std::string dstr(ctime(&now));
86 // write header
87 device.openTag("header");
88 device.writeAttr("revMajor", "1");
89 device.writeAttr("revMinor", "4");
90 device.writeAttr("name", "");
91 device.writeAttr("version", "1.00");
92 device.writeAttr("date", dstr.substr(0, dstr.length() - 1));
93 device.writeAttr("north", b.ymax());
94 device.writeAttr("south", b.ymin());
95 device.writeAttr("east", b.xmax());
96 device.writeAttr("west", b.xmin());
97 /* @note obsolete in 1.4
98 device.writeAttr("maxRoad", ec.size());
99 device.writeAttr("maxJunc", nc.size());
100 device.writeAttr("maxPrg", 0);
101 */
102 // write optional geo reference
104 if (gch.usingGeoProjection()) {
105 device.openTag("geoReference");
106 device.writePreformattedTag(" <![CDATA[\n "
107 + gch.getProjString()
108 + "\n]]>\n");
109 device.closeTag();
110 if (gch.getOffsetBase() != Position(0, 0)) {
111 device.openTag("offset");
112 device.writeAttr("x", -gch.getOffsetBase().x());
113 device.writeAttr("y", -gch.getOffsetBase().y());
114 device.writeAttr("z", -gch.getOffsetBase().z());
115 device.writeAttr("hdg", 0);
116 device.closeTag();
117 }
118 }
119 device.closeTag();
120
121 SignalLanes signalLanes;
122
124
125 PositionVector crosswalk_shape;
126 std::map<std::string, std::vector<std::string>> crosswalksByEdge;
127 for (auto it = nc.begin(); it != nc.end(); ++it) {
128 NBNode* n = it->second;
129 auto crosswalks = n->getCrossings();
130 for (const auto& cw : n->getCrossings()) {
131 // getting from crosswalk line to a full shape
132 crosswalk_shape = cw->shape;
133 PositionVector rightside = cw->shape;
134 try {
135 crosswalk_shape.move2side(cw->width / 2);
136 rightside.move2side(cw->width / -2);
137 } catch (InvalidArgument&) { }
138 rightside = rightside.reverse();
139 crosswalk_shape.append(rightside);
140 auto crosswalkId = cw->id;
141 nb.getShapeCont().addPolygon(crosswalkId, "crosswalk", RGBColor::DEFAULT_COLOR, 0,
143 crosswalk_shape, false, true, 1, false, crosswalkId);
144 SUMOPolygon* cwp = nb.getShapeCont().getPolygons().get(crosswalkId);
145 cwp->setParameter("length", toString(cw->width));
146 cwp->setParameter("width", toString(cw->shape.length2D()));
147 cwp->setParameter("hdg", "0");
148 crosswalksByEdge[cw->edges[0]->getID()].push_back(crosswalkId);
149 }
150 }
151
152 // write normal edges (road)
153 for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
154 const NBEdge* e = (*i).second;
155 const int fromNodeID = e->getIncomingEdges().size() > 0 ? getID(e->getFromNode()->getID(), nodeMap, nodeID) : INVALID_ID;
156 const int toNodeID = e->getConnections().size() > 0 ? getID(e->getToNode()->getID(), nodeMap, nodeID) : INVALID_ID;
157 writeNormalEdge(device, e,
158 getID(e->getID(), edgeMap, edgeID),
159 fromNodeID, toNodeID,
160 origNames, straightThresh,
161 nb.getShapeCont(),
162 signalLanes,
163 crosswalksByEdge[e->getID()]);
164 }
165 device.lf();
166
167 // write junction-internal edges (road). In OpenDRIVE these are called 'paths' or 'connecting roads'
168 OutputDevice_String junctionOSS(3);
169 for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
170 NBNode* n = (*i).second;
171 int connectionID = 0; // unique within a junction
172 const int nID = getID(n->getID(), nodeMap, nodeID);
173 if (n->numNormalConnections() > 0) {
174 junctionOSS << " <junction name=\"" << n->getID() << "\" id=\"" << nID << "\">\n";
175 }
176 std::vector<NBEdge*> incoming = (*i).second->getIncomingEdges();
177 if (lefthand) {
178 std::reverse(incoming.begin(), incoming.end());
179 }
180 for (NBEdge* inEdge : incoming) {
181 std::string centerMark = "none";
182 const int inEdgeID = getID(inEdge->getID(), edgeMap, edgeID);
183 // group parallel edges
184 const NBEdge* outEdge = nullptr;
185 bool isOuterEdge = true; // determine where a solid outer border should be drawn
186 int lastFromLane = -1;
187 std::vector<NBEdge::Connection> parallel;
188 std::vector<NBEdge::Connection> connections = inEdge->getConnections();
189 for (const NBEdge::Connection& c : connections) {
190 assert(c.toEdge != 0);
191 if (outEdge != c.toEdge || c.fromLane == lastFromLane) {
192 if (outEdge != nullptr) {
193 if (isOuterEdge) {
194 addPedestrianConnection(inEdge, outEdge, parallel);
195 }
196 connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
197 getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
198 inEdgeID,
199 getID(outEdge->getID(), edgeMap, edgeID),
200 connectionID,
201 parallel, isOuterEdge, straightThresh, centerMark,
202 signalLanes);
203 parallel.clear();
204 isOuterEdge = false;
205 }
206 outEdge = c.toEdge;
207 }
208 lastFromLane = c.fromLane;
209 parallel.push_back(c);
210 }
211 if (isOuterEdge) {
212 addPedestrianConnection(inEdge, outEdge, parallel);
213 }
214 if (!parallel.empty()) {
215 if (!lefthand && (n->geometryLike() || inEdge->isTurningDirectionAt(outEdge))) {
216 centerMark = "solid";
217 }
218 connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
219 getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
220 inEdgeID,
221 getID(outEdge->getID(), edgeMap, edgeID),
222 connectionID,
223 parallel, isOuterEdge, straightThresh, centerMark,
224 signalLanes);
225 parallel.clear();
226 }
227 }
228 if (n->numNormalConnections() > 0) {
229 junctionOSS << " </junction>\n";
230 }
231 }
232 device.lf();
233 // write controllers
234 for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
235 NBNode* n = (*i).second;
236 if (n->isTLControlled()) {
238 std::set<std::string> ids;
239 device.openTag("controller");
240 device.writeAttr("id", tl->getID());
241 for (const NBConnection& c : tl->getControlledLinks()) {
242 const std::string id = tl->getID() + "_" + toString(c.getTLIndex());
243 if (ids.count(id) == 0) {
244 ids.insert(id);
245 device.openTag("control");
246 device.writeAttr("signalId", id);
247 device.closeTag();
248 }
249 }
250 device.closeTag();
251 }
252 }
253 // write junctions (junction)
254 device << junctionOSS.getString();
255
256 device.closeTag();
257 device.close();
258}
259
260
261std::string
263 std::map<std::string, std::string> dividerTypeMapping;
264 dividerTypeMapping["solid_line"] = "solid";
265 dividerTypeMapping["dashed_line"] = "broken";
266 dividerTypeMapping["double_solid_line"] = "solid solid";
267 dividerTypeMapping["no"] = "none";
268
269 // defaulting to solid as in the original code
270 std::string dividerType = "solid";
271
272 if (e->getParametersMap().count("divider") > 0) {
273 std::string divider = e->getParametersMap().find("divider")->second;
274 if (dividerTypeMapping.count(divider) > 0) {
275 dividerType = dividerTypeMapping.find(divider)->second;
276 }
277 }
278 return dividerType;
279}
280
281
282void
284 int edgeID, int fromNodeID, int toNodeID,
285 const bool origNames,
286 const double straightThresh,
287 const ShapeContainer& shc,
288 SignalLanes& signalLanes,
289 const std::vector<std::string>& crossings) {
290 // buffer output because some fields are computed out of order
291 OutputDevice_String elevationOSS(3);
292 elevationOSS.setPrecision(8);
293 OutputDevice_String planViewOSS(2);
294 planViewOSS.setPrecision(8);
295 double length = 0;
296
297 planViewOSS.openTag("planView");
298 // for the shape we need to use the leftmost border of the leftmost lane
299 const std::vector<NBEdge::Lane>& lanes = e->getLanes();
301#ifdef DEBUG_SMOOTH_GEOM
302 if (DEBUGCOND) {
303 std::cout << "write planview for edge " << e->getID() << "\n";
304 }
305#endif
306
307 if (ls.size() == 2 || e->getPermissions() == SVC_PEDESTRIAN) {
308 // foot paths may contain sharp angles
309 length = writeGeomLines(ls, planViewOSS, elevationOSS);
310 } else {
311 bool ok = writeGeomSmooth(ls, e->getSpeed(), planViewOSS, elevationOSS, straightThresh, length);
312 if (!ok) {
313 WRITE_WARNINGF(TL("Could not compute smooth shape for edge '%'."), e->getID());
314 }
315 }
316 planViewOSS.closeTag();
317
318 device.openTag("road");
319 device.writeAttr("name", StringUtils::escapeXML(e->getStreetName()));
320 device.setPrecision(8); // length requires higher precision
321 device.writeAttr("length", MAX2(POSITION_EPS, length));
322 device.setPrecision(gPrecision);
323 device.writeAttr("id", edgeID);
324 device.writeAttr("junction", -1);
325 if (fromNodeID != INVALID_ID || toNodeID != INVALID_ID) {
326 device.openTag("link");
327 if (fromNodeID != INVALID_ID) {
328 device.openTag("predecessor");
329 device.writeAttr("elementType", "junction");
330 device.writeAttr("elementId", fromNodeID);
331 device.closeTag();
332 }
333 if (toNodeID != INVALID_ID) {
334 device.openTag("successor");
335 device.writeAttr("elementType", "junction");
336 device.writeAttr("elementId", toNodeID);
337 device.closeTag();
338 }
339 device.closeTag();
340 }
341 device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
342 device << planViewOSS.getString();
343 writeElevationProfile(ls, device, elevationOSS);
344 device << " <lateralProfile/>\n";
345 device << " <lanes>\n";
346 device << " <laneSection s=\"0\">\n";
347 const std::string centerMark = e->getPermissions(e->getNumLanes() - 1) == 0 ? "none" : getDividerType(e);
348 if (!LHLL) {
349 writeEmptyCenterLane(device, centerMark, 0.13);
350 }
351 const std::string side = LHLL ? "left" : "right";
352 device << " <" << side << ">\n";
353 const int numLanes = e->getNumLanes();
354 for (int jRH = numLanes; --jRH >= 0;) {
355 // XODR always has the lanes left to right (by default this is
356 // inner-to-outer but in LH networks its outer-to-inner)
357 const int j = lefthand ? numLanes - 1 - jRH : jRH;
358 std::string laneType = e->getLaneStruct(j).type;
359 if (laneType == "") {
360 laneType = getLaneType(e->getPermissions(j));
361 }
362 device << " <lane id=\"" << s2x(j, numLanes) << "\" type=\"" << laneType << "\" level=\"true\">\n";
363 device << " <link/>\n";
364 // this could be used for geometry-link junctions without u-turn,
365 // predecessor and sucessors would be lane indices,
366 // road predecessor / succesfors would be of type 'road' rather than
367 // 'junction'
368 //device << " <predecessor id=\"-1\"/>\n";
369 //device << " <successor id=\"-1\"/>\n";
370 //device << " </link>\n";
371 device << " <width sOffset=\"0\" a=\"" << e->getLaneWidth(j) << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
372 std::string markType = "broken";
373 if (LHRL) {
374 if (j == numLanes - 1) {
375 // solid road mark in the middle of the road
376 markType = "solid";
377 } else if ((e->getPermissions(j) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
378 // solid road mark to the right of sidewalk or bicycle lane
379 markType = "solid";
380 }
381 } else {
382 if (j == 0) {
383 markType = "solid";
384 } else if (j > 0
385 && (e->getPermissions(j - 1) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
386 // solid road mark to the left of sidewalk or bicycle lane
387 markType = "solid";
388 } else if (e->getPermissions(j) == 0) {
389 // solid road mark to the right of a forbidden lane
390 markType = "solid";
391 }
392 }
393 device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
394 device << " <speed sOffset=\"0\" max=\"" << lanes[j].speed << "\"/>\n";
395 device << " </lane>\n";
396 }
397 device << " </" << side << ">\n";
398 if (LHLL) {
399 writeEmptyCenterLane(device, centerMark, 0.13);
400 }
401 device << " </laneSection>\n";
402 device << " </lanes>\n";
403 writeRoadObjects(device, e, shc, crossings);
404 writeSignals(device, e, length, signalLanes, shc);
405 if (origNames) {
406 device << " <userData code=\"sumoId\" value=\"" << e->getID() << "\"/>\n";
407 }
408 device.closeTag();
410}
411
412void
413NWWriter_OpenDrive::addPedestrianConnection(const NBEdge* inEdge, const NBEdge* outEdge, std::vector<NBEdge::Connection>& parallel) {
414 // by default there are no internal lanes for pedestrians. Determine if
415 // one is feasible and does not exist yet.
416 if (outEdge != nullptr
417 && inEdge->getPermissions(0) == SVC_PEDESTRIAN
418 && outEdge->getPermissions(0) == SVC_PEDESTRIAN
419 && (parallel.empty()
420 || parallel.front().fromLane != 0
421 || parallel.front().toLane != 0)) {
422 parallel.insert(parallel.begin(), NBEdge::Connection(0, const_cast<NBEdge*>(outEdge), 0));
423 parallel.front().vmax = (inEdge->getLanes()[0].speed + outEdge->getLanes()[0].speed) / (double) 2.0;
424 }
425}
426
427
428int
429NWWriter_OpenDrive::writeInternalEdge(OutputDevice& device, OutputDevice& junctionDevice, const NBEdge* inEdge, int nodeID,
430 int edgeID, int inEdgeID, int outEdgeID,
431 int connectionID,
432 const std::vector<NBEdge::Connection>& parallel,
433 const bool isOuterEdge,
434 const double straightThresh,
435 const std::string& centerMark,
436 SignalLanes& signalLanes) {
437 assert(parallel.size() != 0);
438 const NBEdge::Connection& cLeft = LHRL ? parallel.front() : parallel.back();
439 const NBEdge* outEdge = cLeft.toEdge;
440 PositionVector begShape = getInnerLaneBorder(inEdge, cLeft.fromLane);
441 PositionVector endShape = getInnerLaneBorder(outEdge, cLeft.toLane);
442 //std::cout << "computing reference line for internal lane " << cLeft.getInternalLaneID() << " begLane=" << inEdge->getLaneShape(cLeft.fromLane) << " endLane=" << outEdge->getLaneShape(cLeft.toLane) << "\n";
443
444 double length;
445 double laneOffset = 0;
446 PositionVector fallBackShape;
447 fallBackShape.push_back(begShape.back());
448 fallBackShape.push_back(endShape.front());
449 const bool turnaround = inEdge->isTurningDirectionAt(outEdge);
450 bool ok = true;
451 PositionVector init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
452 if (init.size() == 0) {
453 length = fallBackShape.length2D();
454 // problem with turnarounds is known, method currently returns 'ok' (#2539)
455 if (!ok) {
456 WRITE_WARNINGF(TL("Could not compute smooth shape from lane '%' to lane '%'. Use option 'junctions.scurve-stretch' or increase radius of junction '%' to fix this."), inEdge->getLaneID(cLeft.fromLane), outEdge->getLaneID(cLeft.toLane), inEdge->getToNode()->getID());
457 } else if (length <= NUMERICAL_EPS) {
458 // left-curving geometry-like edges must use the right
459 // side as reference line and shift
460 begShape = getOuterLaneBorder(inEdge, cLeft.fromLane);
461 endShape = getOuterLaneBorder(outEdge, cLeft.toLane);
462 init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
463 if (init.size() != 0) {
464 length = init.bezier(12).length2D();
465 laneOffset = outEdge->getLaneWidth(cLeft.toLane);
466 //std::cout << " internalLane=" << cLeft.getInternalLaneID() << " length=" << length << "\n";
467 }
468 }
469 } else {
470 length = init.bezier(12).length2D();
471 }
472 double roadLength = MAX2(POSITION_EPS, length);
473
474 junctionDevice << " <connection id=\"" << connectionID << "\" incomingRoad=\"" << inEdgeID << "\" connectingRoad=\"" << edgeID << "\" contactPoint=\"start\">\n";
475 device.openTag("road");
476 device.writeAttr("name", cLeft.id);
477 device.setPrecision(8); // length requires higher precision
478 device.writeAttr("length", roadLength);
479 device.setPrecision(gPrecision);
480 device.writeAttr("id", edgeID);
481 device.writeAttr("junction", nodeID);
482 device.openTag("link");
483 device.openTag("predecessor");
484 device.writeAttr("elementType", "road");
485 device.writeAttr("elementId", inEdgeID);
486 device.writeAttr("contactPoint", "end");
487 device.closeTag();
488 device.openTag("successor");
489 device.writeAttr("elementType", "road");
490 device.writeAttr("elementId", outEdgeID);
491 device.writeAttr("contactPoint", "start");
492 device.closeTag();
493 device.closeTag();
494 device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
495 device.openTag("planView");
496 device.setPrecision(8); // geometry hdg requires higher precision
497 OutputDevice_String elevationOSS(3);
498 elevationOSS.setPrecision(8);
499#ifdef DEBUG_SMOOTH_GEOM
500 if (DEBUGCOND) {
501 std::cout << "write planview for internal edge " << cLeft.id << " init=" << init << " fallback=" << fallBackShape
502 << " begShape=" << begShape << " endShape=" << endShape
503 << "\n";
504 }
505#endif
506 if (init.size() == 0) {
507 writeGeomLines(fallBackShape, device, elevationOSS);
508 } else {
509 writeGeomPP3(device, elevationOSS, init, length);
510 }
511 device.setPrecision(gPrecision);
512 device.closeTag();
513 writeElevationProfile(fallBackShape, device, elevationOSS);
514 device << " <lateralProfile/>\n";
515 device << " <lanes>\n";
516 if (laneOffset != 0) {
517 device << " <laneOffset s=\"0\" a=\"" << laneOffset << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
518 }
519 device << " <laneSection s=\"0\">\n";
520 if (!lefthand) {
521 writeEmptyCenterLane(device, centerMark, 0);
522 }
523 const std::string side = lefthand ? "left" : "right";
524 device << " <" << side << ">\n";
525 const int numLanes = (int)parallel.size();
526 for (int jRH = numLanes; --jRH >= 0;) {
527 const int j = lefthand ? numLanes - 1 - jRH : jRH;
528 const int xJ = s2x(j, numLanes);
529 const NBEdge::Connection& c = parallel[j];
530 const int fromIndex = s2x(c.fromLane, inEdge->getNumLanes());
531 const int toIndex = s2x(c.toLane, outEdge->getNumLanes());
532
533 double inEdgeWidth = inEdge->getLaneWidth(c.fromLane);
534 double outEdgeWidth = outEdge->getLaneWidth(c.toLane);
535 // Ideally a polynomial function of third order would be needed for more precision.
536 // This is obtained by specifying c and d coefficients, keeping it simple and linear
537 // as we only know final and initial width.
538 double bCoefficient = (outEdgeWidth - inEdgeWidth) / roadLength;
539 double cCoefficient = 0;
540 double dCoefficient = 0;
541 device << " <lane id=\"" << xJ << "\" type=\"" << getLaneType(outEdge->getPermissions(c.toLane)) << "\" level=\"true\">\n";
542 device << " <link>\n";
543 device << " <predecessor id=\"" << fromIndex << "\"/>\n";
544 device << " <successor id=\"" << toIndex << "\"/>\n";
545 device << " </link>\n";
546 device << " <width sOffset=\"0\" a=\"" << inEdgeWidth << "\" b=\"" << bCoefficient << "\" c=\"" << cCoefficient << "\" d=\"" << dCoefficient << "\"/>\n";
547 std::string markType = "broken";
548 if (inEdge->isTurningDirectionAt(outEdge)) {
549 markType = "none";
550 } else if (c.fromLane == 0 && c.toLane == 0 && isOuterEdge) {
551 // solid road mark at the outer border
552 markType = "solid";
553 } else if (isOuterEdge && j > 0
554 && (outEdge->getPermissions(parallel[j - 1].toLane) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
555 // solid road mark to the left of sidewalk or bicycle lane
556 markType = "solid";
557 } else if (!inEdge->getToNode()->geometryLike()) {
558 // draw shorter road marks to indicate turning paths
559 LinkDirection dir = inEdge->getToNode()->getDirection(inEdge, outEdge, lefthand);
561 // XXX <type><line/><type> is not rendered by odrViewer so cannot be validated
562 // device << " <type name=\"broken\" width=\"0.13\">\n";
563 // device << " <line length=\"0.5\" space=\"0.5\" tOffset=\"0\" sOffset=\"0\" rule=\"none\"/>\n";
564 // device << " </type>\n";
565 markType = "none";
566 }
567 }
568 device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
569 device << " <speed sOffset=\"0\" max=\"" << c.vmax << "\"/>\n";
570 device << " </lane>\n";
571
572 junctionDevice << " <laneLink from=\"" << fromIndex << "\" to=\"" << xJ << "\"/>\n";
573 connectionID++;
574 }
575 device << " </" << side << ">\n";
576 if (lefthand) {
577 writeEmptyCenterLane(device, centerMark, 0);
578 }
579 device << " </laneSection>\n";
580 device << " </lanes>\n";
581 device << " <objects/>\n";
582 UNUSED_PARAMETER(signalLanes);
583 device << " <signals/>\n";
584 device.closeTag();
585 junctionDevice << " </connection>\n";
586
587 return connectionID;
588}
589
590
591double
592NWWriter_OpenDrive::writeGeomLines(const PositionVector& shape, OutputDevice& device, OutputDevice& elevationDevice, double offset) {
593 for (int j = 0; j < (int)shape.size() - 1; ++j) {
594 const Position& p = shape[j];
595 const Position& p2 = shape[j + 1];
596 const double hdg = shape.angleAt2D(j);
597 const double length = p.distanceTo2D(p2);
598 device.openTag("geometry");
599 device.writeAttr("s", offset);
600 device.writeAttr("x", p.x());
601 device.writeAttr("y", p.y());
602 device.writeAttr("hdg", hdg);
603 device.writeAttr("length", length < 1e-8 ? 1e-8 : length);
604 device.openTag("line").closeTag();
605 device.closeTag();
606 elevationDevice << " <elevation s=\"" << offset << "\" a=\"" << p.z() << "\" b=\"" << (p2.z() - p.z()) / MAX2(POSITION_EPS, length) << "\" c=\"0\" d=\"0\"/>\n";
607 offset += length;
608 }
609 return offset;
610}
611
612
613void
614NWWriter_OpenDrive::writeEmptyCenterLane(OutputDevice& device, const std::string& mark, double markWidth) {
615 device << " <center>\n";
616 device << " <lane id=\"0\" type=\"none\" level=\"true\">\n";
617 device << " <link/>\n";
618 device << " <roadMark sOffset=\"0\" type=\"" << mark << "\" weight=\"standard\" color=\"standard\" width=\"" << markWidth << "\"/>\n";
619 device << " </lane>\n";
620 device << " </center>\n";
621}
622
623
624int
625NWWriter_OpenDrive::getID(const std::string& origID, StringBijection<int>& map, int& lastID) {
626 if (map.hasString(origID)) {
627 return map.get(origID);
628 }
629 map.insert(origID, lastID++);
630 return lastID - 1;
631}
632
633
634std::string
636 switch (permissions) {
637 case SVC_PEDESTRIAN:
638 return "sidewalk";
639 //case (SVC_BICYCLE | SVC_PEDESTRIAN):
640 // WRITE_WARNING("Ambiguous lane type (biking+driving) for road '" + roadID + "'");
641 // return "sidewalk";
642 case SVC_BICYCLE:
643 return "biking";
644 case 0:
645 // ambiguous
646 return "none";
647 case SVC_RAIL:
648 case SVC_RAIL_URBAN:
650 case SVC_RAIL_FAST:
651 return "rail";
652 case SVC_TRAM:
653 return "tram";
654 default: {
655 // complex permissions
656 if (permissions == SVCAll) {
657 return "driving";
658 } else if (isRailway(permissions)) {
659 return "rail";
660 } else if ((permissions & SVC_PASSENGER) != 0) {
661 return "driving";
662 } else {
663 return "restricted";
664 }
665 }
666 }
667}
668
669
671NWWriter_OpenDrive::getInnerLaneBorder(const NBEdge* edge, int laneIndex, double widthOffset) {
672 if (laneIndex == -1) {
673 // innermost lane
674 laneIndex = (int)edge->getNumLanes() - 1;
675 if (LHRL) {
676 laneIndex = 0;
677 }
678 }
679 PositionVector result = edge->getLaneShape(laneIndex);
680 widthOffset -= (LHLL ? -1 : 1) * edge->getLaneWidth(laneIndex) / 2;
681 try {
682 result.move2side(widthOffset);
683 } catch (InvalidArgument&) { }
684 return result;
685}
686
687
690 return getInnerLaneBorder(edge, laneIndex, edge->getLaneWidth(laneIndex));
691}
692
693
694double
696 OutputDevice& device,
697 OutputDevice& elevationDevice,
698 PositionVector init,
699 double length,
700 double offset) {
701 assert(init.size() == 3 || init.size() == 4);
702
703 // avoid division by 0
704 length = MAX2(POSITION_EPS, length);
705
706 const Position p = init.front();
707 const double hdg = init.angleAt2D(0);
708
709 // backup elevation values
710 const PositionVector initZ = init;
711 // translate to u,v coordinates
712 init.add(-p.x(), -p.y(), -p.z());
713 init.rotate2D(-hdg);
714
715 // parametric coefficients
716 double aU, bU, cU, dU;
717 double aV, bV, cV, dV;
718 double aZ, bZ, cZ, dZ;
719
720 // unfactor the Bernstein polynomials of degree 2 (or 3) and collect the coefficients
721 if (init.size() == 3) {
722 //f(x, a, b ,c) = a + (2*b - 2*a)*x + (a - 2*b + c)*x*x
723 aU = init[0].x();
724 bU = 2 * init[1].x() - 2 * init[0].x();
725 cU = init[0].x() - 2 * init[1].x() + init[2].x();
726 dU = 0;
727
728 aV = init[0].y();
729 bV = 2 * init[1].y() - 2 * init[0].y();
730 cV = init[0].y() - 2 * init[1].y() + init[2].y();
731 dV = 0;
732
733 // elevation is not parameteric on [0:1] but on [0:length]
734 aZ = initZ[0].z();
735 bZ = (2 * initZ[1].z() - 2 * initZ[0].z()) / length;
736 cZ = (initZ[0].z() - 2 * initZ[1].z() + initZ[2].z()) / (length * length);
737 dZ = 0;
738
739 } else {
740 // f(x, a, b, c, d) = a + (x*((3*b) - (3*a))) + ((x*x)*((3*a) + (3*c) - (6*b))) + ((x*x*x)*((3*b) - (3*c) - a + d))
741 aU = init[0].x();
742 bU = 3 * init[1].x() - 3 * init[0].x();
743 cU = 3 * init[0].x() - 6 * init[1].x() + 3 * init[2].x();
744 dU = -init[0].x() + 3 * init[1].x() - 3 * init[2].x() + init[3].x();
745
746 aV = init[0].y();
747 bV = 3 * init[1].y() - 3 * init[0].y();
748 cV = 3 * init[0].y() - 6 * init[1].y() + 3 * init[2].y();
749 dV = -init[0].y() + 3 * init[1].y() - 3 * init[2].y() + init[3].y();
750
751 // elevation is not parameteric on [0:1] but on [0:length]
752 aZ = initZ[0].z();
753 bZ = (3 * initZ[1].z() - 3 * initZ[0].z()) / length;
754 cZ = (3 * initZ[0].z() - 6 * initZ[1].z() + 3 * initZ[2].z()) / (length * length);
755 dZ = (-initZ[0].z() + 3 * initZ[1].z() - 3 * initZ[2].z() + initZ[3].z()) / (length * length * length);
756 }
757
758 device.openTag("geometry");
759 device.writeAttr("s", offset);
760 device.writeAttr("x", p.x());
761 device.writeAttr("y", p.y());
762 device.writeAttr("hdg", hdg);
763 device.writeAttr("length", length);
764
765 device.openTag("paramPoly3");
766 device.writeAttr("aU", aU);
767 device.writeAttr("bU", bU);
768 device.writeAttr("cU", cU);
769 device.writeAttr("dU", dU);
770 device.writeAttr("aV", aV);
771 device.writeAttr("bV", bV);
772 device.writeAttr("cV", cV);
773 device.writeAttr("dV", dV);
774 device.writeAttr("pRange", "normalized");
775 device.closeTag();
776 device.closeTag();
777
778 // write elevation
779 elevationDevice.openTag("elevation");
780 elevationDevice.writeAttr("s", offset);
781 elevationDevice.writeAttr("a", aZ);
782 elevationDevice.writeAttr("b", bZ);
783 elevationDevice.writeAttr("c", cZ);
784 elevationDevice.writeAttr("d", dZ);
785 elevationDevice.closeTag();
786
787 return offset + length;
788}
789
790
791bool
792NWWriter_OpenDrive::writeGeomSmooth(const PositionVector& shape, double speed, OutputDevice& device, OutputDevice& elevationDevice, double straightThresh, double& length) {
793#ifdef DEBUG_SMOOTH_GEOM
794 if (DEBUGCOND) {
795 std::cout << "writeGeomSmooth\n n=" << shape.size() << " shape=" << toString(shape) << "\n";
796 }
797#endif
798 bool ok = true;
799 const double longThresh = speed; // 16.0; // make user-configurable (should match the sampling rate of the source data)
800 const double curveCutout = longThresh / 2; // 8.0; // make user-configurable (related to the maximum turning rate)
801 // the length of the segment that is added for cutting a corner can be bounded by 2*curveCutout (prevent the segment to be classified as 'long')
802 assert(longThresh >= 2 * curveCutout);
803 assert(shape.size() > 2);
804 // add intermediate points wherever there is a strong angular change between long segments
805 // assume the geometry is simplified so as not to contain consecutive colinear points
806 PositionVector shape2 = shape;
807 double maxAngleDiff = 0;
808 double offset = 0;
809 for (int j = 1; j < (int)shape.size() - 1; ++j) {
810 //const double hdg = shape.angleAt2D(j);
811 const Position& p0 = shape[j - 1];
812 const Position& p1 = shape[j];
813 const Position& p2 = shape[j + 1];
814 const double dAngle = fabs(GeomHelper::angleDiff(p0.angleTo2D(p1), p1.angleTo2D(p2)));
815 const double length1 = p0.distanceTo2D(p1);
816 const double length2 = p1.distanceTo2D(p2);
817 maxAngleDiff = MAX2(maxAngleDiff, dAngle);
818#ifdef DEBUG_SMOOTH_GEOM
819 if (DEBUGCOND) {
820 std::cout << " j=" << j << " dAngle=" << RAD2DEG(dAngle) << " length1=" << length1 << " length2=" << length2 << "\n";
821 }
822#endif
823 if (dAngle > straightThresh
824 && (length1 > longThresh || j == 1)
825 && (length2 > longThresh || j == (int)shape.size() - 2)) {
826 // NBNode::bezierControlPoints checks for minimum length of POSITION_EPS so we make sure there is no instability
827 shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 - MIN2(length1 - 2 * POSITION_EPS, curveCutout)), false);
828 shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 + MIN2(length2 - 2 * POSITION_EPS, curveCutout)), false);
829 shape2.removeClosest(p1);
830 }
831 offset += length1;
832 }
833 const int numPoints = (int)shape2.size();
834#ifdef DEBUG_SMOOTH_GEOM
835 if (DEBUGCOND) {
836 std::cout << " n=" << numPoints << " shape2=" << toString(shape2) << "\n";
837 }
838#endif
839
840 if (maxAngleDiff < straightThresh) {
841 length = writeGeomLines(shape2, device, elevationDevice, 0);
842#ifdef DEBUG_SMOOTH_GEOM
843 if (DEBUGCOND) {
844 std::cout << " special case: all lines. maxAngleDiff=" << maxAngleDiff << "\n";
845 }
846#endif
847 return ok;
848 }
849
850 // write the long segments as lines, short segments as curves
851 offset = 0;
852 for (int j = 0; j < numPoints - 1; ++j) {
853 const Position& p0 = shape2[j];
854 const Position& p1 = shape2[j + 1];
855 PositionVector line;
856 line.push_back(p0);
857 line.push_back(p1);
858 const double lineLength = line.length2D();
859 if (lineLength >= longThresh) {
860 offset = writeGeomLines(line, device, elevationDevice, offset);
861#ifdef DEBUG_SMOOTH_GEOM
862 if (DEBUGCOND) {
863 std::cout << " writeLine=" << toString(line) << "\n";
864 }
865#endif
866 } else {
867 // find control points
868 PositionVector begShape;
869 PositionVector endShape;
870 if (j == 0 || j == numPoints - 2) {
871 // keep the angle of the first/last segment but end at the front of the shape
872 begShape = line;
873 begShape.add(p0 - begShape.back());
874 } else if (j == 1 || p0.distanceTo2D(shape2[j - 1]) > longThresh) {
875 // use the previous segment if it is long or the first one
876 begShape.push_back(shape2[j - 1]);
877 begShape.push_back(p0);
878 } else {
879 // end at p0 with mean angle of the previous and current segment
880 begShape.push_back(shape2[j - 1]);
881 begShape.push_back(p1);
882 begShape.add(p0 - begShape.back());
883 }
884
885 if (j == 0 || j == numPoints - 2) {
886 // keep the angle of the first/last segment but start at the end of the shape
887 endShape = line;
888 endShape.add(p1 - endShape.front());
889 } else if (j == numPoints - 3 || p1.distanceTo2D(shape2[j + 2]) > longThresh) {
890 // use the next segment if it is long or the final one
891 endShape.push_back(p1);
892 endShape.push_back(shape2[j + 2]);
893 } else {
894 // start at p1 with mean angle of the current and next segment
895 endShape.push_back(p0);
896 endShape.push_back(shape2[j + 2]);
897 endShape.add(p1 - endShape.front());
898 }
899 const double extrapolateLength = MIN2((double)25, lineLength / 4);
900 PositionVector init = NBNode::bezierControlPoints(begShape, endShape, false, extrapolateLength, extrapolateLength, ok, nullptr, straightThresh);
901 if (init.size() == 0) {
902 // could not compute control points, write line
903 offset = writeGeomLines(line, device, elevationDevice, offset);
904#ifdef DEBUG_SMOOTH_GEOM
905 if (DEBUGCOND) {
906 std::cout << " writeLine lineLength=" << lineLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
907 }
908#endif
909 } else {
910 // write bezier
911 const double curveLength = init.bezier(12).length2D();
912 offset = writeGeomPP3(device, elevationDevice, init, curveLength, offset);
913#ifdef DEBUG_SMOOTH_GEOM
914 if (DEBUGCOND) {
915 std::cout << " writeCurve lineLength=" << lineLength << " curveLength=" << curveLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
916 }
917#endif
918 }
919 }
920 }
921 length = offset;
922 return ok;
923}
924
925
926void
928 // check if the shape is flat
929 bool flat = true;
930 double z = shape.size() == 0 ? 0 : shape[0].z();
931 for (int i = 1; i < (int)shape.size(); ++i) {
932 if (fabs(shape[i].z() - z) > NUMERICAL_EPS) {
933 flat = false;
934 break;
935 }
936 }
937 device << " <elevationProfile>\n";
938 if (flat) {
939 device << " <elevation s=\"0\" a=\"" << z << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
940 } else {
941 device << elevationDevice.getString();
942 }
943 device << " </elevationProfile>\n";
944
945}
946
947
948void
950 if (e->getNumLanes() > 1) {
951 // compute 'stop line' of rightmost lane
952 const PositionVector shape0 = e->getLaneShape(0);
953 assert(shape0.size() >= 2);
954 const Position& from = shape0[-2];
955 const Position& to = shape0[-1];
956 PositionVector stopLine;
957 stopLine.push_back(to);
958 stopLine.push_back(to - PositionVector::sideOffset(from, to, lefthand ? 1000 : -1000));
959 // endpoints of all other lanes should be on the stop line
960 for (int lane = 1; lane < e->getNumLanes(); ++lane) {
961 const double dist = stopLine.distance2D(e->getLaneShape(lane)[-1]);
962 if (dist > NUMERICAL_EPS) {
963 WRITE_WARNINGF(TL("Uneven stop line at lane '%' (dist=%) cannot be represented in OpenDRIVE."), e->getLaneID(lane), toString(dist));
964 }
965 }
966 }
967}
968
969void
970NWWriter_OpenDrive::writeRoadObjects(OutputDevice& device, const NBEdge* e, const ShapeContainer& shc, const std::vector<std::string>& crossings) {
971 device.openTag("objects");
972 if (e->hasParameter(ROAD_OBJECTS)) {
973 device.setPrecision(8); // geometry hdg requires higher precision
975 for (std::string id : StringTokenizer(e->getParameter(ROAD_OBJECTS, "")).getVector()) {
976 SUMOPolygon* p = shc.getPolygons().get(id);
977 if (p == nullptr) {
978 PointOfInterest* poi = shc.getPOIs().get(id);
979 if (poi == nullptr) {
980 WRITE_WARNINGF("Road object polygon or POI '%' not found for edge '%'", id, e->getID());
981 } else {
982 writeRoadObjectPOI(device, e, road, poi);
983 }
984 } else {
985 writeRoadObjectPoly(device, e, road, p);
986 }
987 }
988 device.setPrecision(gPrecision);
989 }
990 if (crossings.size() > 0) {
991 device.setPrecision(8); // geometry hdg requires higher precision
993 for (size_t ic = 0; ic < crossings.size(); ic++) {
994 SUMOPolygon* p = shc.getPolygons().get(crossings[ic]);
995 if (p != 0) {
996 writeRoadObjectPoly(device, e, road, p);
997 }
998 }
999 device.setPrecision(gPrecision);
1000 }
1001 device.closeTag();
1002}
1003
1004
1005std::vector<NWWriter_OpenDrive::TrafficSign>
1006NWWriter_OpenDrive::parseTrafficSign(const std::string& trafficSign, PointOfInterest* poi) {
1007 std::vector<TrafficSign> result;
1008 // check for maxspeed, stop, give_way and hazard
1009 if (trafficSign == "maxspeed" && poi->hasParameter("maxspeed")) {
1010 result.push_back(TrafficSign{ "OpenDrive", "maxspeed", "", poi->getParameter("maxspeed")});
1011 } else if (trafficSign == "stop") {
1012 result.push_back(TrafficSign{ "OpenDrive", trafficSign, "", "" });
1013 } else if (trafficSign == "give_way") {
1014 result.push_back(TrafficSign{ "OpenDrive", trafficSign, "", "" });
1015 } else if (trafficSign == "hazard" && poi->hasParameter("hazard")) {
1016 result.push_back(TrafficSign{ "OpenDrive", trafficSign, poi->getParameter("hazard"), "" });
1017 } else {
1018 if (trafficSign.find_first_of(",;") != std::string::npos) {
1019 std::string::size_type colon = trafficSign.find(':');
1020 const std::string country = trafficSign.substr(0, colon);
1021 const std::string remaining = trafficSign.substr(colon + 1);
1022
1023 std::vector<std::string> tokens;
1024 std::string::size_type lastPos = 0;
1025 std::string::size_type pos = remaining.find_first_of(",;");
1026 while (pos != std::string::npos) {
1027 // add country and colon before pushing the token
1028 tokens.push_back(country + ":" + remaining.substr(lastPos, pos - lastPos));
1029 lastPos = pos + 1;
1030 pos = remaining.find_first_of(",;", lastPos);
1031 }
1032 tokens.push_back(country + ":" + remaining.substr(lastPos));
1033 // call for each token the parseTrafficSignId function and fill the result vector
1034 for (std::string token : tokens) {
1035 result.push_back(parseTrafficSignId(token));
1036 }
1037 } else {
1038 result.push_back(parseTrafficSignId(trafficSign));
1039 }
1040 }
1041 return result;
1042}
1043
1044
1046NWWriter_OpenDrive::parseTrafficSignId(const std::string& trafficSign) {
1047 // for OpenDrive 1.4 the country code is specified as ISO 3166-1, alpha-3,
1048 // but OSM uses ISO 3166-1, alpha-2. In order to be OpenDrive 1.4 compliant
1049 // we would need to convert it back to alpha-3. However, newer OpenDrive
1050 // versions support alpha-2, so we use that instead.
1051
1052 //std::regex re("([A-Z]{2}):([0-9]{1,3})(?:\\[([0-9]{1,3})\\])?");
1053
1054 // This regex is used to parse the traffic sign id. It matches the following groups:
1055 // 1. country code (2 letters)
1056 // 2. sign id (1-4 digits) e.g. 1020 or 274.1
1057 // 3. value in brackets e.g. 1020[50] or 274.1[50] -> 50
1058 // 4. value after the hyphen 1020-50 or 274.1-50 -> 50
1059 std::regex re("([A-Z]{2}):([0-9.]{1,6}|[A-Z]{1}[0-9.]{2,6})(?:\\[(.*)\\])?(?:-(.*))?");
1060 std::smatch match;
1061 std::regex_match(trafficSign, match, re);
1062 if (match.size() == 5) {
1063 std::string value;
1064 if (match[3].matched) {
1065 value = match[3].str();
1066 } else if (match[4].matched) {
1067 value = match[4].str();
1068 }
1069 return TrafficSign{ match[1], match[2], "", value};
1070 } else {
1071 return TrafficSign{ "OpenDrive", trafficSign, "", ""};
1072 }
1073}
1074
1075
1076void
1078 SignalLanes& signalLanes, const ShapeContainer& shc) {
1079 device.openTag("signals");
1080 if (e->getToNode()->isTLControlled()) {
1081 // try to faithfully represent the SUMO signal layout
1082 // (if a realistic number of signals is needed, the user should set
1083 // option --tls.group-signals)
1085 std::map<std::string, bool> toWrite;
1086 for (const NBConnection& c : tl->getControlledLinks()) {
1087 if (c.getFrom() == e) {
1088 const std::string id = tl->getID() + "_" + toString(c.getTLIndex());
1089 if (toWrite.count(id) == 0) {
1090 toWrite[id] = signalLanes.count(id) == 0;
1091 }
1092 signalLanes[id].first.insert(c.getFromLane());
1093 signalLanes[id].second.insert(e->getToNode()->getDirection(e, c.getTo()));
1094 }
1095 }
1096 for (auto item : toWrite) {
1097 const std::string id = item.first;
1098 const bool isNew = item.second;
1099 const std::set<LinkDirection>& dirs = signalLanes[id].second;
1100 const bool l = dirs.count(LinkDirection::LEFT) != 0 || dirs.count(LinkDirection::PARTLEFT) != 0;
1101 const bool r = dirs.count(LinkDirection::RIGHT) != 0 || dirs.count(LinkDirection::PARTRIGHT) != 0;
1102 const bool s = dirs.count(LinkDirection::STRAIGHT) != 0;
1103 const std::string tag = isNew ? "signal" : "signalReference";
1104 int firstLane = *signalLanes[id].first.begin();
1105 double t = e->getLaneWidth(firstLane) * 0.5;
1106 for (int i = firstLane + 1; i < e->getNumLanes(); i++) {
1107 t += e->getLaneWidth(i);
1108 }
1109 device.openTag(tag);
1110 device.writeAttr("id", id);
1111 device.setPrecision(8);
1112 device.writeAttr("s", length);
1113 device.writeAttr("t", -t);
1114 device.writeAttr("orientation", "+");
1115 if (isNew) {
1116 int type = 1000001;
1117 int subType = -1;
1118 if (l && !s && !r) {
1119 type = 1000011;
1120 subType = 10;
1121 } else if (!l && !s && r) {
1122 type = 1000011;
1123 subType = 20;
1124 } else if (!l && s && !r) {
1125 type = 1000011;
1126 subType = 30;
1127 } else if (l && s && !r) {
1128 type = 1000011;
1129 subType = 40;
1130 } else if (!l && s && r) {
1131 type = 1000011;
1132 subType = 50;
1133 }
1134 device.writeAttr("dynamic", "yes");
1135 device.writeAttr("zOffset", 5);
1136 device.writeAttr("country", "OpenDRIVE");
1137 device.writeAttr("type", type);
1138 device.writeAttr("subtype", subType);
1139 device.writeAttr("height", 0.78);
1140 device.writeAttr("width", 0.26);
1141 }
1142 for (int lane : signalLanes[id].first) {
1143 device.openTag("validity");
1144 device.writeAttr("fromLane", s2x(lane, e->getNumLanes()));
1145 device.writeAttr("toLane", s2x(lane, e->getNumLanes()));
1146 device.closeTag();
1147 }
1148 device.closeTag();
1149 }
1150 } else if (e->hasParameter(ROAD_OBJECTS)) {
1151 PositionVector roadShape = getInnerLaneBorder(e);
1152 for (std::string id : StringTokenizer(e->getParameter(ROAD_OBJECTS, "")).getVector()) {
1153 PointOfInterest* poi = shc.getPOIs().get(id);
1154 if (poi != nullptr) {
1155 if (poi->getShapeType() == "traffic_sign" && poi->hasParameter("traffic_sign")) {
1156 std::string traffic_sign_type = poi->getParameter("traffic_sign");
1157
1158 std::vector<TrafficSign> trafficSigns = parseTrafficSign(traffic_sign_type, poi);
1159
1160 auto distance = roadShape.nearest_offset_to_point2D(*poi, true);
1161 double t = getRoadSideOffset(e);
1162 double calculatedZOffset = 3.0;
1163 for (auto it = trafficSigns.rbegin(); it != trafficSigns.rend(); ++it) {
1164 TrafficSign trafficSign = *it;
1165 device.openTag("signal");
1166 device.writeAttr("id", id);
1167 device.writeAttr("s", distance);
1168 device.writeAttr("t", t);
1169 device.writeAttr("orientation", "-");
1170 device.writeAttr("dynamic", "no");
1171 device.writeAttr("zOffset", calculatedZOffset);
1172 device.writeAttr("country", trafficSign.country);
1173 device.writeAttr("type", trafficSign.type);
1174 device.writeAttr("subtype", trafficSign.subtype);
1175 device.writeAttr("value", trafficSign.value);
1176 device.writeAttr("height", 0.78);
1177 device.writeAttr("width", 0.78);
1178 device.closeTag();
1179 calculatedZOffset += 0.78;
1180 }
1181 }
1182 }
1183 }
1184 }
1185 device.closeTag();
1186}
1187
1188
1189double
1191 double t = 0.30;
1192 if (!lefthand || LHLL) {
1193 for (int i = 0; i < e->getNumLanes(); i++) {
1194 t += e->getPermissions(i) == SVC_PEDESTRIAN ? e->getLaneWidth(i) * 0.2 : e->getLaneWidth(i);
1195 }
1196 }
1197 t = LHRL ? t : -t;
1198 return t;
1199}
1200
1201
1202int
1203NWWriter_OpenDrive::s2x(int sumoIndex, int numLanes) {
1204 // sumo lanes: 0, 1, 2 (0 being the outermost lane)
1205 // XODR: -3,-2,-1 (written in reverse order)
1206 // LHLL: 3, 2, 1 (written in reverse order)
1207 // lefthand (old):-1,-2,-3
1208 return (lefthand
1209 ? (LHLL
1210 ? numLanes - sumoIndex
1211 : - sumoIndex - 1)
1212 : sumoIndex - numLanes);
1213}
1214
1215
1216void
1218 if (shc.getPolygons().size() == 0 && shc.getPOIs().size() == 0) {
1219 return;
1220 }
1221 const double maxDist = OptionsCont::getOptions().getFloat("opendrive-output.shape-match-dist");
1222 if (maxDist < 0) {
1223 return;
1224 }
1225 // register custom assignements
1226 std::set<std::string> assigned;
1227 for (auto it = ec.begin(); it != ec.end(); ++it) {
1228 NBEdge* e = it->second;
1229 if (e->hasParameter(ROAD_OBJECTS)) {
1230 for (std::string id : StringTokenizer(e->getParameter(ROAD_OBJECTS, "")).getVector()) {
1231 assigned.insert(id);
1232 }
1233 }
1234 }
1235 // build rtree for edges
1236 NamedRTree r;
1237 for (auto it = ec.begin(); it != ec.end(); ++it) {
1238 NBEdge* edge = it->second;
1239 Boundary bound = edge->getGeometry().getBoxBoundary();
1240 bound.grow(maxDist);
1241 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1242 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1243 r.Insert(min, max, edge);
1244 }
1245
1246 for (auto itPoly = shc.getPolygons().begin(); itPoly != shc.getPolygons().end(); itPoly++) {
1247 SUMOPolygon* p = itPoly->second;
1248 Boundary bound = p->getShape().getBoxBoundary();
1249 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1250 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1251 std::set<const Named*> edges;
1252 Named::StoringVisitor visitor(edges);
1253 r.Search(min, max, visitor);
1254 std::vector<std::pair<double, std::string> > nearby;
1255 for (const Named* namedEdge : edges) {
1256 NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
1257 const double distance = VectorHelper<double>::minValue(p->getShape().distances(e->getLaneShape(0), true));
1258 if (distance <= maxDist) {
1259 // sort by distance and ID to stabilize results
1260 nearby.push_back(std::make_pair(distance, e->getID()));
1261 //std::cout << " poly=" << p->getID() << " e=" << e->getID() << " dist=" << distance << "\n";
1262 }
1263 }
1264 if (nearby.size() > 0) {
1265 std::sort(nearby.begin(), nearby.end());
1266 NBEdge* closest = ec.retrieve(nearby.front().second);
1267 std::string objects = closest->getParameter(ROAD_OBJECTS, "");
1268 if (objects != "") {
1269 objects += " ";
1270 }
1271 objects += p->getID();
1272 closest->setParameter(ROAD_OBJECTS, objects);
1273 //std::cout << "poly=" << p->getID() << " closest=" << closest->getID() << "\n";
1274 }
1275 }
1276 for (auto itPoi = shc.getPOIs().begin(); itPoi != shc.getPOIs().end(); itPoi++) {
1277 PointOfInterest* p = itPoi->second;
1278 float min[2] = { static_cast<float>(p->x()), static_cast<float>(p->y()) };
1279 float max[2] = { static_cast<float>(p->x()), static_cast<float>(p->y()) };
1280 std::set<const Named*> edges;
1281 Named::StoringVisitor visitor(edges);
1282 r.Search(min, max, visitor);
1283 std::vector<std::pair<double, std::string> > nearby;
1284 for (const Named* namedEdge : edges) {
1285 NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
1286 const double distance = e->getLaneShape(0).distance2D(*p, true);
1287 if (distance != GeomHelper::INVALID_OFFSET && distance <= maxDist) {
1288 // sort by distance and ID to stabilize results
1289 nearby.push_back(std::make_pair(distance, e->getID()));
1290 //if (p->getID() == "1275468911") {
1291 // std::cout << " poly=" << p->getID() << " e=" << e->getID() << " dist=" << distance << "\n";
1292 //}
1293 }
1294 }
1295 if (nearby.size() > 0) {
1296 std::sort(nearby.begin(), nearby.end());
1297 NBEdge* closest = ec.retrieve(nearby.front().second);
1298 std::string objects = closest->getParameter(ROAD_OBJECTS, "");
1299 if (objects != "") {
1300 objects += " ";
1301 }
1302 objects += p->getID();
1303 closest->setParameter(ROAD_OBJECTS, objects);
1304 //std::cout << "poly=" << p->getID() << " closest=" << closest->getID() << "\n";
1305 }
1306 }
1307}
1308
1309
1310void
1312 Position center = *poi;
1313 const double edgeOffset = roadShape.nearest_offset_to_point2D(center, false);
1314 if (edgeOffset == GeomHelper::INVALID_OFFSET) {
1315 WRITE_WARNINGF("Cannot map road object POI '%' with center % onto edge '%'", poi->getID(), center, e->getID());
1316 return;
1317 }
1318
1319 Position edgePos = roadShape.positionAtOffset2D(edgeOffset);
1320 double sideOffset = center.distanceTo2D(edgePos);
1321 // determine sign of sideOffset
1322 PositionVector tmp = roadShape.getSubpart2D(MAX2(0.0, edgeOffset - 1), MIN2(roadShape.length2D(), edgeOffset + 1));
1323 tmp.move2side(sideOffset);
1324 if (tmp.distance2D(center) < sideOffset) {
1325 sideOffset *= -1;
1326 }
1327 // place traffic signs on appropriate side of the road
1328 std::string type = poi->getShapeType();
1329 std::string name = StringUtils::escapeXML(poi->getParameter("name", ""), true);
1330 if (poi->getShapeType() == "traffic_sign") {
1331 sideOffset = getRoadSideOffset(e);
1332 type = "pole";
1333 name = "pole";
1334 }
1335
1336 device.openTag("object");
1337 device.writeAttr("id", poi->getID());
1338 device.writeAttr("type", type);
1339 device.writeAttr("name", name);
1340 device.writeAttr("s", edgeOffset);
1341 device.writeAttr("t", sideOffset);
1342 device.writeAttr("hdg", 0);
1343 device.closeTag();
1344}
1345
1346void
1348 PositionVector shape = p->getShape();
1349 Position center = shape.getPolygonCenter();
1350
1351 const double edgeOffset = roadShape.nearest_offset_to_point2D(center, false);
1352 if (edgeOffset == GeomHelper::INVALID_OFFSET) {
1353 WRITE_WARNINGF("Cannot map road object polygon '%' with center % onto edge '%'", p->getID(), center, e->getID());
1354 return;
1355 }
1356 Position edgePos = roadShape.positionAtOffset2D(edgeOffset);
1357 const double edgeAngle = roadShape.rotationAtOffset(edgeOffset);
1358 double sideOffset = center.distanceTo2D(edgePos);
1359 // determine sign of sideOffset
1360 PositionVector tmp = roadShape.getSubpart2D(MAX2(0.0, edgeOffset - 1), MIN2(roadShape.length2D(), edgeOffset + 1));
1361 tmp.move2side(sideOffset);
1362 if (tmp.distance2D(center) < sideOffset) {
1363 sideOffset *= -1;
1364 }
1365 //std::cout << " id=" << id
1366 // << " shape=" << shape
1367 // << " center=" << center
1368 // << " edgeOffset=" << edgeOffset
1369 // << "\n";
1370 auto shapeType = p->getShapeType();
1371 device.openTag("object");
1372 device.writeAttr("id", p->getID());
1373 device.writeAttr("type", shapeType);
1374 if (p->hasParameter("name")) {
1375 device.writeAttr("name", StringUtils::escapeXML(p->getParameter("name", ""), true));
1376 }
1377 device.writeAttr("s", edgeOffset);
1378 device.writeAttr("t", shapeType == "crosswalk" && !lefthand ? 0 : sideOffset);
1379 double hdg = -edgeAngle;
1380 if (p->hasParameter("hdg")) {
1381 try {
1382 hdg = StringUtils::toDoubleSecure(p->getParameter("hdg", ""), 0);
1383 } catch (NumberFormatException&) {}
1384 }
1385 device.writeAttr("hdg", hdg);
1386 if (p->hasParameter("length")) {
1387 try {
1388 device.writeAttr("length", StringUtils::toDoubleSecure(p->getParameter("length", ""), 0));
1389 } catch (NumberFormatException&) {}
1390 }
1391 if (p->hasParameter("width")) {
1392 try {
1393 device.writeAttr("width", StringUtils::toDoubleSecure(p->getParameter("width", ""), 0));
1394 } catch (NumberFormatException&) {}
1395 }
1396 double height = 0;
1397 if (p->hasParameter("height")) {
1398 try {
1399 height = StringUtils::toDoubleSecure(p->getParameter("height", ""), 0);
1400 device.writeAttr("height", height);
1401 } catch (NumberFormatException&) {}
1402 }
1403 //device.openTag("outlines");
1404 device.openTag("outline");
1405 device.writeAttr("id", 0);
1406 device.writeAttr("fillType", "concrete");
1407 device.writeAttr("outer", "true");
1408 device.writeAttr("closed", p->getShape().isClosed() ? "true" : "false");
1409 device.writeAttr("laneType", "border");
1410
1411 shape.sub(center);
1412 if (hdg != -edgeAngle) {
1413 shape.rotate2D(-edgeAngle - hdg);
1414 }
1415 int i = 0;
1416 for (Position pos : shape) {
1417 device.openTag("cornerLocal");
1418 device.writeAttr("u", pos.x());
1419 device.writeAttr("v", pos.y());
1420 device.writeAttr("z", MAX2(0.0, p->getShapeLayer()));
1421 device.writeAttr("height", height);
1422 device.writeAttr("id", i++);
1423 device.closeTag();
1424 }
1425
1426
1427 //device.closeTag();
1428 device.closeTag();
1429 device.closeTag();
1430}
1431
1432/****************************************************************************/
#define DEG2RAD(x)
Definition GeomHelper.h:35
#define RAD2DEG(x)
Definition GeomHelper.h:36
#define DEBUGCOND(PED)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define TL(string)
Definition MsgHandler.h:315
#define ROAD_OBJECTS
#define INVALID_ID
const SVCPermissions SVCAll
all VClasses are allowed
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a railway edge.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ SVC_RAIL
vehicle is a not electrified rail
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_RAIL_FAST
vehicle that is allowed to drive on high-speed rail tracks
@ SVC_RAIL_ELECTRIC
rail vehicle that requires electrified tracks
@ SVC_RAIL_URBAN
vehicle is a city rail
@ SVC_TRAM
vehicle is a light rail
@ SVC_PEDESTRIAN
pedestrian
LinkDirection
The different directions a link between two lanes may take (or a stream between two edges)....
@ PARTLEFT
The link is a partial left direction.
@ RIGHT
The link is a (hard) right direction.
@ LEFT
The link is a (hard) left direction.
@ STRAIGHT
The link is a straight direction.
@ PARTRIGHT
The link is a partial right direction.
int gPrecision
the precision for floating point outputs
Definition StdDefs.cpp:26
#define UNUSED_PARAMETER(x)
Definition StdDefs.h:30
T MIN2(T a, T b)
Definition StdDefs.h:76
T MAX2(T a, T b)
Definition StdDefs.h:82
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
A class that stores a 2D geometrical boundary.
Definition Boundary.h:39
double ymin() const
Returns minimum y-coordinate.
Definition Boundary.cpp:130
double xmin() const
Returns minimum x-coordinate.
Definition Boundary.cpp:118
Boundary & grow(double by)
extends the boundary by the given amount
Definition Boundary.cpp:343
double ymax() const
Returns maximum y-coordinate.
Definition Boundary.cpp:136
double xmax() const
Returns maximum x-coordinate.
Definition Boundary.cpp:124
static methods for processing the coordinates conversion for the current net
static const GeoConvHelper & getFinal()
the coordinate transformation for writing the location element and for tracking the original coordina...
const std::string & getProjString() const
Returns the original projection definition.
const Position getOffsetBase() const
Returns the network base.
bool usingGeoProjection() const
Returns whether a transformation from geo to metric coordinates will be performed.
const Boundary & getConvBoundary() const
Returns the converted boundary.
static const double INVALID_OFFSET
a value to signify offsets outside the range of [0, Line.length()]
Definition GeomHelper.h:50
static double angleDiff(const double angle1, const double angle2)
Returns the difference of the second angle to the first angle in radiants.
Storage for edges, including some functionality operating on multiple edges.
Definition NBEdgeCont.h:59
std::map< std::string, NBEdge * >::const_iterator begin() const
Returns the pointer to the begin of the stored edges.
Definition NBEdgeCont.h:171
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition NBEdgeCont.h:178
The representation of a single edge during network building.
Definition NBEdge.h:92
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4379
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition NBEdge.h:1041
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition NBEdge.h:642
NBNode * getToNode() const
Returns the destination node of the edge.
Definition NBEdge.h:546
Lane & getLaneStruct(int lane)
Definition NBEdge.h:1428
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition NBEdge.h:783
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition NBEdge.h:730
double getSpeed() const
Returns the speed allowed on this edge.
Definition NBEdge.h:619
const std::string & getID() const
Definition NBEdge.h:1528
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition NBEdge.cpp:3665
int getNumLanes() const
Returns the number of lanes.
Definition NBEdge.h:520
std::string getLaneID(int lane) const
get lane ID
Definition NBEdge.cpp:4017
const std::string & getStreetName() const
Returns the street name of this edge.
Definition NBEdge.h:669
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition NBEdge.h:539
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition NBEdge.cpp:986
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition NBEdge.cpp:1393
Instance responsible for building networks.
NBNodeCont & getNodeCont()
Returns a reference to the node container.
ShapeContainer & getShapeCont()
NBEdgeCont & getEdgeCont()
Container for nodes during the netbuilding process.
Definition NBNodeCont.h:57
int size() const
Returns the number of nodes stored in this container.
Definition NBNodeCont.h:307
std::map< std::string, NBNode * >::const_iterator begin() const
Returns the pointer to the begin of the stored nodes.
Definition NBNodeCont.h:113
std::map< std::string, NBNode * >::const_iterator end() const
Returns the pointer to the end of the stored nodes.
Definition NBNodeCont.h:118
Represents a single node (junction) during network building.
Definition NBNode.h:66
LinkDirection getDirection(const NBEdge *const incoming, const NBEdge *const outgoing, bool leftHand=false) const
Returns the representation of the described stream's direction.
Definition NBNode.cpp:2409
const std::set< NBTrafficLightDefinition * > & getControllingTLS() const
Returns the traffic lights that were assigned to this node (The set of tls that control this node)
Definition NBNode.h:336
int numNormalConnections() const
return the number of lane-to-lane connections at this junction (excluding crossings)
Definition NBNode.cpp:3989
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition NBNode.cpp:2982
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition NBNode.cpp:3836
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition NBNode.h:331
static PositionVector bezierControlPoints(const PositionVector &begShape, const PositionVector &endShape, bool isTurnaround, double extrapolateBeg, double extrapolateEnd, bool &ok, NBNode *recordError=0, double straightThresh=DEG2RAD(5), int shapeFlag=0)
get bezier control points
Definition NBNode.cpp:575
The base class for traffic light logic definitions.
const NBConnectionVector & getControlledLinks() const
returns the controlled links (depends on previous call to collectLinks)
static PositionVector getInnerLaneBorder(const NBEdge *edge, int laneIndex=-1, double widthOffset=0)
get the lane border that is closer to the reference line (center line of the edge)
static void writeRoadObjectPOI(OutputDevice &device, const NBEdge *e, const PositionVector &roadShape, const PointOfInterest *poi)
static void mapmatchRoadObjects(const ShapeContainer &shc, const NBEdgeCont &ec)
map pois and polygons to the closes edge
static void addPedestrianConnection(const NBEdge *inEdge, const NBEdge *outEdge, std::vector< NBEdge::Connection > &parallel)
static void writeNormalEdge(OutputDevice &device, const NBEdge *e, int edgeID, int fromNodeID, int toNodeID, const bool origNames, const double straightThresh, const ShapeContainer &shc, SignalLanes &signalLanes, const std::vector< std::string > &crossings)
write normal edge to device
static void checkLaneGeometries(const NBEdge *e)
check if the lane geometries are compatible with OpenDRIVE assumptions (colinear stop line)
static std::vector< TrafficSign > parseTrafficSign(const std::string &trafficSign, PointOfInterest *poi)
std::map< std::string, std::pair< std::set< int >, std::set< LinkDirection > > > SignalLanes
signalID -> (lanes, dirs)
static double getRoadSideOffset(const NBEdge *e)
static void writeEmptyCenterLane(OutputDevice &device, const std::string &mark, double markWidth)
static void writeSignals(OutputDevice &device, const NBEdge *e, double length, SignalLanes &signalLanes, const ShapeContainer &shc)
write signal record for traffic light
static void writeRoadObjects(OutputDevice &device, const NBEdge *e, const ShapeContainer &shc, const std::vector< std::string > &crossings)
write road objects referenced as edge parameters
static bool lefthand
whether a lefthand network is being written
static void writeRoadObjectPoly(OutputDevice &device, const NBEdge *e, const PositionVector &roadShape, const SUMOPolygon *p)
static void writeElevationProfile(const PositionVector &shape, OutputDevice &device, const OutputDevice_String &elevationDevice)
static std::string getDividerType(const NBEdge *e)
retrieve divider type
static std::string getLaneType(SVCPermissions permissions)
static bool writeGeomSmooth(const PositionVector &shape, double speed, OutputDevice &device, OutputDevice &elevationDevice, double straightThresh, double &length)
static int s2x(int sumoIndex, int numLanes)
convert sumo lane index to xodr lane index
static void writeNetwork(const OptionsCont &oc, NBNetBuilder &nb)
Writes the network into a openDRIVE-file.
static int getID(const std::string &origID, StringBijection< int > &map, int &lastID)
static double writeGeomPP3(OutputDevice &device, OutputDevice &elevationDevice, PositionVector init, double length, double offset=0)
write geometry as a single bezier curve (paramPoly3)
static int writeInternalEdge(OutputDevice &device, OutputDevice &junctionDevice, const NBEdge *inEdge, int nodeID, int edgeID, int inEdgeID, int outEdgeID, int connectionID, const std::vector< NBEdge::Connection > &parallel, const bool isOuterEdge, const double straightThresh, const std::string &centerMark, SignalLanes &signalLanes)
write internal edge to device, return next connectionID
static TrafficSign parseTrafficSignId(const std::string &trafficSign)
static PositionVector getOuterLaneBorder(const NBEdge *edge, int laneIndex=-1)
get the lane border that is further away from the reference line (center line of the edge)
static double writeGeomLines(const PositionVector &shape, OutputDevice &device, OutputDevice &elevationDevice, double offset=0)
write geometry as sequence of lines (sumo style)
Allows to store the object; used as context while traveling the rtree in TraCI.
Definition Named.h:90
Base class for objects which have an id.
Definition Named.h:54
const std::string & getID() const
Returns the id.
Definition Named.h:74
T get(const std::string &id) const
Retrieves an item.
IDMap::const_iterator begin() const
Returns a reference to the begin iterator for the internal map.
IDMap::const_iterator end() const
Returns a reference to the end iterator for the internal map.
int size() const
Returns the number of stored items within the container.
A RT-tree for efficient storing of SUMO's Named objects.
Definition NamedRTree.h:61
void Insert(const float a_min[2], const float a_max[2], Named *const &a_data)
Insert entry.
Definition NamedRTree.h:79
int Search(const float a_min[2], const float a_max[2], const Named::StoringVisitor &c) const
Find all within search rectangle.
Definition NamedRTree.h:112
A storage for options typed value containers)
Definition OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
An output device that encapsulates an ofstream.
std::string getString() const
Returns the current content as a string.
Static storage of an output device and its base (abstract) implementation.
void lf()
writes a line feed if applicable
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
OutputDevice & writePreformattedTag(const std::string &val)
writes a preformatted tag to the device but ensures that any pending tags are closed
void close()
Closes the device and removes it from the dictionary.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
static bool createDeviceByOption(const std::string &optionName, const std::string &rootElement="", const std::string &schemaFile="")
Creates the device using the output definition stored in the named option.
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
void setPrecision(int precision=gPrecision)
Sets the precision or resets it to default.
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
bool hasParameter(const std::string &key) const
Returns whether the parameter is set.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
A point-of-interest.
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition Position.h:276
double x() const
Returns the x-position.
Definition Position.h:55
double z() const
Returns the z-position.
Definition Position.h:65
double angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position (in radians bet...
Definition Position.h:286
double y() const
Returns the y-position.
Definition Position.h:60
A list of positions.
double length2D() const
Returns the length.
void append(const PositionVector &v, double sameThreshold=2.0)
void rotate2D(double angle)
Position getPolygonCenter() const
Returns the arithmetic of all corner points.
double rotationAtOffset(double pos) const
Returns the rotation at the given length.
void add(double xoff, double yoff, double zoff)
static Position sideOffset(const Position &beg, const Position &end, const double amount)
get a side position of position vector using a offset
double distance2D(const Position &p, bool perpendicular=false) const
closest 2D-distance to point p (or -1 if perpendicular is true and the point is beyond this vector)
double nearest_offset_to_point2D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D
std::vector< double > distances(const PositionVector &s, bool perpendicular=false) const
distances of all my points to s and all of s points to myself
void move2side(double amount, double maxExtension=100)
move position vector to side using certain amount
PositionVector getSubpart2D(double beginOffset, double endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
double angleAt2D(int pos) const
get angle in certain position of position vector (in radians between -M_PI and M_PI)
PositionVector bezier(int numPoints)
return a bezier interpolation
int insertAtClosest(const Position &p, bool interpolateZ)
inserts p between the two closest positions
bool isClosed() const
check if PositionVector is closed
int removeClosest(const Position &p)
removes the point closest to p and return the removal index
PositionVector reverse() const
reverse position vector
Position positionAtOffset2D(double pos, double lateralOffset=0) const
Returns the position at the given length.
void sub(const Position &offset)
static const RGBColor DEFAULT_COLOR
The default color (for vehicle types and vehicles)
Definition RGBColor.h:199
const PositionVector & getShape() const
Returns the shape of the polygon.
Storage for geometrical objects.
const Polygons & getPolygons() const
Returns all polygons.
virtual bool addPolygon(const std::string &id, const std::string &type, const RGBColor &color, double layer, double angle, const std::string &imgFile, bool relativePath, const PositionVector &shape, bool geo, bool fill, double lineWidth, bool ignorePruning=false, const std::string &name=Shape::DEFAULT_NAME)
Builds a polygon using the given values and adds it to the container.
const POIs & getPOIs() const
Returns all pois.
static const bool DEFAULT_RELATIVEPATH
Definition Shape.h:48
static const std::string DEFAULT_IMG_FILE
Definition Shape.h:47
const std::string & getShapeType() const
Returns the (abstract) type of the Shape.
Definition Shape.h:77
static const double DEFAULT_ANGLE
Definition Shape.h:46
double getShapeLayer() const
Returns the layer of the Shape.
Definition Shape.h:91
bool hasString(const std::string &str) const
T get(const std::string &str) const
void insert(const std::string str, const T key, bool checkDuplicates=true)
std::vector< std::string > getVector()
return vector of strings
static double toDoubleSecure(const std::string &sData, const double def)
converts a string into the integer value described by it
static std::string escapeXML(const std::string &orig, const bool maskDoubleHyphen=false)
Replaces the standard escapes by their XML entities.
static T minValue(const std::vector< T > &v)
A structure which describes a connection between edges or lanes.
Definition NBEdge.h:201
int fromLane
The lane the connections starts at.
Definition NBEdge.h:210
int toLane
The lane the connections yields in.
Definition NBEdge.h:216
NBEdge * toEdge
The edge the connections yields in.
Definition NBEdge.h:213
double vmax
maximum velocity
Definition NBEdge.h:273
std::string id
id of Connection
Definition NBEdge.h:267
std::string type
the type of this lane
Definition NBEdge.h:192