Eclipse SUMO - Simulation of Urban MObility
NWWriter_OpenDrive.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3// Copyright (C) 2011-2022 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
39#define INVALID_ID -1
40
41//#define DEBUG_SMOOTH_GEOM
42#define DEBUGCOND true
43
44#define MIN_TURN_DIAMETER 2.0
45
46
47// ===========================================================================
48// method definitions
49// ===========================================================================
50// ---------------------------------------------------------------------------
51// static methods
52// ---------------------------------------------------------------------------
53void
55 // check whether an opendrive-file shall be generated
56 if (!oc.isSet("opendrive-output")) {
57 return;
58 }
59 const NBNodeCont& nc = nb.getNodeCont();
60 const NBEdgeCont& ec = nb.getEdgeCont();
61 const bool origNames = oc.getBool("output.original-names");
62 const bool lefthand = oc.getBool("lefthand");
63 const double straightThresh = DEG2RAD(oc.getFloat("opendrive-output.straight-threshold"));
64 // some internal mapping containers
65 int nodeID = 1;
66 int edgeID = nc.size() * 10; // distinct from node ids
69 //
70 OutputDevice& device = OutputDevice::getDevice(oc.getString("opendrive-output"));
71 OutputDevice::createDeviceByOption("opendrive-output", "OpenDRIVE");
72 time_t now = time(nullptr);
73 std::string dstr(ctime(&now));
75 // write header
76 device.openTag("header");
77 device.writeAttr("revMajor", "1");
78 device.writeAttr("revMinor", "4");
79 device.writeAttr("name", "");
80 device.writeAttr("version", "1.00");
81 device.writeAttr("date", dstr.substr(0, dstr.length() - 1));
82 device.writeAttr("north", b.ymax());
83 device.writeAttr("south", b.ymin());
84 device.writeAttr("east", b.xmax());
85 device.writeAttr("west", b.xmin());
86 /* @note obsolete in 1.4
87 device.writeAttr("maxRoad", ec.size());
88 device.writeAttr("maxJunc", nc.size());
89 device.writeAttr("maxPrg", 0);
90 */
91 // write optional geo reference
93 if (gch.usingGeoProjection()) {
94 device.openTag("geoReference");
95 device.writePreformattedTag(" <![CDATA[\n "
96 + gch.getProjString()
97 + "\n]]>\n");
98 device.closeTag();
99 if (gch.getOffsetBase() != Position(0, 0)) {
100 device.openTag("offset");
101 device.writeAttr("x", gch.getOffsetBase().x());
102 device.writeAttr("y", gch.getOffsetBase().y());
103 device.writeAttr("z", gch.getOffsetBase().z());
104 device.writeAttr("hdg", 0);
105 device.closeTag();
106 }
107 }
108 device.closeTag();
109
110 SignalLanes signalLanes;
111
112 // write normal edges (road)
113 for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
114 const NBEdge* e = (*i).second;
115 const int fromNodeID = e->getIncomingEdges().size() > 0 ? getID(e->getFromNode()->getID(), nodeMap, nodeID) : INVALID_ID;
116 const int toNodeID = e->getConnections().size() > 0 ? getID(e->getToNode()->getID(), nodeMap, nodeID) : INVALID_ID;
117 writeNormalEdge(device, e,
118 getID(e->getID(), edgeMap, edgeID),
119 fromNodeID, toNodeID,
120 origNames, straightThresh,
121 nb.getShapeCont(),
122 signalLanes);
123 }
124 device.lf();
125
126 // write junction-internal edges (road). In OpenDRIVE these are called 'paths' or 'connecting roads'
127 OutputDevice_String junctionOSS(3);
128 for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
129 NBNode* n = (*i).second;
130 int connectionID = 0; // unique within a junction
131 const int nID = getID(n->getID(), nodeMap, nodeID);
132 if (n->numNormalConnections() > 0) {
133 junctionOSS << " <junction name=\"" << n->getID() << "\" id=\"" << nID << "\">\n";
134 }
135 std::vector<NBEdge*> incoming = (*i).second->getIncomingEdges();
136 if (lefthand) {
137 std::reverse(incoming.begin(), incoming.end());
138 }
139 for (NBEdge* inEdge : incoming) {
140 std::string centerMark = "none";
141 const int inEdgeID = getID(inEdge->getID(), edgeMap, edgeID);
142 // group parallel edges
143 const NBEdge* outEdge = nullptr;
144 bool isOuterEdge = true; // determine where a solid outer border should be drawn
145 int lastFromLane = -1;
146 std::vector<NBEdge::Connection> parallel;
147 std::vector<NBEdge::Connection> connections = inEdge->getConnections();
148 if (lefthand) {
149 std::reverse(connections.begin(), connections.end());
150 }
151 for (const NBEdge::Connection& c : connections) {
152 assert(c.toEdge != 0);
153 if (outEdge != c.toEdge || c.fromLane == lastFromLane) {
154 if (outEdge != nullptr) {
155 if (isOuterEdge) {
156 addPedestrianConnection(inEdge, outEdge, parallel);
157 }
158 connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
159 getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
160 inEdgeID,
161 getID(outEdge->getID(), edgeMap, edgeID),
162 connectionID,
163 parallel, isOuterEdge, straightThresh, centerMark, signalLanes);
164 parallel.clear();
165 isOuterEdge = false;
166 }
167 outEdge = c.toEdge;
168 }
169 lastFromLane = c.fromLane;
170 parallel.push_back(c);
171 }
172 if (isOuterEdge) {
173 addPedestrianConnection(inEdge, outEdge, parallel);
174 }
175 if (!parallel.empty()) {
176 if (!lefthand && (n->geometryLike() || inEdge->isTurningDirectionAt(outEdge))) {
177 centerMark = "solid";
178 }
179 connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
180 getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
181 inEdgeID,
182 getID(outEdge->getID(), edgeMap, edgeID),
183 connectionID,
184 parallel, isOuterEdge, straightThresh, centerMark, signalLanes);
185 parallel.clear();
186 }
187 }
188 if (n->numNormalConnections() > 0) {
189 junctionOSS << " </junction>\n";
190 }
191 }
192 device.lf();
193 // write controllers
194 for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
195 NBNode* n = (*i).second;
196 if (n->isTLControlled()) {
198 std::set<std::string> ids;
199 device.openTag("controller");
200 device.writeAttr("id", tl->getID());
201 for (const NBConnection& c : tl->getControlledLinks()) {
202 const std::string id = tl->getID() + "_" + toString(c.getTLIndex());
203 if (ids.count(id) == 0) {
204 ids.insert(id);
205 device.openTag("control");
206 device.writeAttr("signalId", id);
207 device.closeTag();
208 }
209 }
210 device.closeTag();
211 }
212 }
213 // write junctions (junction)
214 device << junctionOSS.getString();
215
216 device.closeTag();
217 device.close();
218}
219
220
221void
223 int edgeID, int fromNodeID, int toNodeID,
224 const bool origNames,
225 const double straightThresh,
226 const ShapeContainer& shc,
227 SignalLanes& signalLanes) {
228 // buffer output because some fields are computed out of order
229 OutputDevice_String elevationOSS(3);
230 elevationOSS.setPrecision(8);
231 OutputDevice_String planViewOSS(2);
232 planViewOSS.setPrecision(8);
233 double length = 0;
234
235 planViewOSS.openTag("planView");
236 // for the shape we need to use the leftmost border of the leftmost lane
237 const std::vector<NBEdge::Lane>& lanes = e->getLanes();
239#ifdef DEBUG_SMOOTH_GEOM
240 if (DEBUGCOND) {
241 std::cout << "write planview for edge " << e->getID() << "\n";
242 }
243#endif
244
245 if (ls.size() == 2 || e->getPermissions() == SVC_PEDESTRIAN) {
246 // foot paths may contain sharp angles
247 length = writeGeomLines(ls, planViewOSS, elevationOSS);
248 } else {
249 bool ok = writeGeomSmooth(ls, e->getSpeed(), planViewOSS, elevationOSS, straightThresh, length);
250 if (!ok) {
251 WRITE_WARNING("Could not compute smooth shape for edge '" + e->getID() + "'.");
252 }
253 }
254 planViewOSS.closeTag();
255
256 device.openTag("road");
257 device.writeAttr("name", StringUtils::escapeXML(e->getStreetName()));
258 device.setPrecision(8); // length requires higher precision
259 device.writeAttr("length", MAX2(POSITION_EPS, length));
260 device.setPrecision(gPrecision);
261 device.writeAttr("id", edgeID);
262 device.writeAttr("junction", -1);
263 if (fromNodeID != INVALID_ID || toNodeID != INVALID_ID) {
264 device.openTag("link");
265 if (fromNodeID != INVALID_ID) {
266 device.openTag("predecessor");
267 device.writeAttr("elementType", "junction");
268 device.writeAttr("elementId", fromNodeID);
269 device.closeTag();
270 }
271 if (toNodeID != INVALID_ID) {
272 device.openTag("successor");
273 device.writeAttr("elementType", "junction");
274 device.writeAttr("elementId", toNodeID);
275 device.closeTag();
276 }
277 device.closeTag();
278 }
279 device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
280 device << planViewOSS.getString();
281 writeElevationProfile(ls, device, elevationOSS);
282 device << " <lateralProfile/>\n";
283 device << " <lanes>\n";
284 device << " <laneSection s=\"0\">\n";
285 const std::string centerMark = e->getPermissions(e->getNumLanes() - 1) == 0 ? "none" : "solid";
286 writeEmptyCenterLane(device, centerMark, 0.13);
287 device << " <right>\n";
288 for (int j = e->getNumLanes(); --j >= 0;) {
289 std::string laneType = e->getLaneStruct(j).type;
290 if (laneType == "") {
291 laneType = getLaneType(e->getPermissions(j));
292 }
293 device << " <lane id=\"-" << e->getNumLanes() - j << "\" type=\"" << laneType << "\" level=\"true\">\n";
294 device << " <link/>\n";
295 // this could be used for geometry-link junctions without u-turn,
296 // predecessor and sucessors would be lane indices,
297 // road predecessor / succesfors would be of type 'road' rather than
298 // 'junction'
299 //device << " <predecessor id=\"-1\"/>\n";
300 //device << " <successor id=\"-1\"/>\n";
301 //device << " </link>\n";
302 device << " <width sOffset=\"0\" a=\"" << e->getLaneWidth(j) << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
303 std::string markType = "broken";
304 if (j == 0) {
305 markType = "solid";
306 } else if (j > 0
307 && (e->getPermissions(j - 1) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
308 // solid road mark to the left of sidewalk or bicycle lane
309 markType = "solid";
310 } else if (e->getPermissions(j) == 0) {
311 // solid road mark to the right of a forbidden lane
312 markType = "solid";
313 }
314 device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
315 device << " <speed sOffset=\"0\" max=\"" << lanes[j].speed << "\"/>\n";
316 device << " </lane>\n";
317 }
318 device << " </right>\n";
319 device << " </laneSection>\n";
320 device << " </lanes>\n";
321 writeRoadObjects(device, e, shc);
322 writeSignals(device, e, length, signalLanes);
323 if (origNames) {
324 device << " <userData code=\"sumoId\" value=\"" << e->getID() << "\"/>\n";
325 }
326 device.closeTag();
328}
329
330void
331NWWriter_OpenDrive::addPedestrianConnection(const NBEdge* inEdge, const NBEdge* outEdge, std::vector<NBEdge::Connection>& parallel) {
332 // by default there are no internal lanes for pedestrians. Determine if
333 // one is feasible and does not exist yet.
334 if (outEdge != nullptr
335 && inEdge->getPermissions(0) == SVC_PEDESTRIAN
336 && outEdge->getPermissions(0) == SVC_PEDESTRIAN
337 && (parallel.empty()
338 || parallel.front().fromLane != 0
339 || parallel.front().toLane != 0)) {
340 parallel.insert(parallel.begin(), NBEdge::Connection(0, const_cast<NBEdge*>(outEdge), 0, false));
341 parallel.front().vmax = (inEdge->getLanes()[0].speed + outEdge->getLanes()[0].speed) / (double) 2.0;
342 }
343}
344
345
346int
347NWWriter_OpenDrive::writeInternalEdge(OutputDevice& device, OutputDevice& junctionDevice, const NBEdge* inEdge, int nodeID,
348 int edgeID, int inEdgeID, int outEdgeID,
349 int connectionID,
350 const std::vector<NBEdge::Connection>& parallel,
351 const bool isOuterEdge,
352 const double straightThresh,
353 const std::string& centerMark,
354 SignalLanes& signalLanes) {
355 assert(parallel.size() != 0);
356 const NBEdge::Connection& cLeft = parallel.back();
357 const NBEdge* outEdge = cLeft.toEdge;
358 PositionVector begShape = getLeftLaneBorder(inEdge, cLeft.fromLane);
359 PositionVector endShape = getLeftLaneBorder(outEdge, cLeft.toLane);
360 //std::cout << "computing reference line for internal lane " << cLeft.getInternalLaneID() << " begLane=" << inEdge->getLaneShape(cLeft.fromLane) << " endLane=" << outEdge->getLaneShape(cLeft.toLane) << "\n";
361
362 double length;
363 double laneOffset = 0;
364 PositionVector fallBackShape;
365 fallBackShape.push_back(begShape.back());
366 fallBackShape.push_back(endShape.front());
367 const bool turnaround = inEdge->isTurningDirectionAt(outEdge);
368 bool ok = true;
369 PositionVector init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
370 if (init.size() == 0) {
371 length = fallBackShape.length2D();
372 // problem with turnarounds is known, method currently returns 'ok' (#2539)
373 if (!ok) {
374 WRITE_WARNING("Could not compute smooth shape from lane '" + inEdge->getLaneID(cLeft.fromLane) + "' to lane '" + outEdge->getLaneID(cLeft.toLane) + "'. Use option 'junctions.scurve-stretch' or increase radius of junction '" + inEdge->getToNode()->getID() + "' to fix this.");
375 } else if (length <= NUMERICAL_EPS) {
376 // left-curving geometry-like edges must use the right
377 // side as reference line and shift
378 begShape = getRightLaneBorder(inEdge, cLeft.fromLane);
379 endShape = getRightLaneBorder(outEdge, cLeft.toLane);
380 init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
381 if (init.size() != 0) {
382 length = init.bezier(12).length2D();
383 laneOffset = outEdge->getLaneWidth(cLeft.toLane);
384 //std::cout << " internalLane=" << cLeft.getInternalLaneID() << " length=" << length << "\n";
385 }
386 }
387 } else {
388 length = init.bezier(12).length2D();
389 }
390
391 junctionDevice << " <connection id=\"" << connectionID << "\" incomingRoad=\"" << inEdgeID << "\" connectingRoad=\"" << edgeID << "\" contactPoint=\"start\">\n";
392 device.openTag("road");
393 device.writeAttr("name", cLeft.id);
394 device.setPrecision(8); // length requires higher precision
395 device.writeAttr("length", MAX2(POSITION_EPS, length));
396 device.setPrecision(gPrecision);
397 device.writeAttr("id", edgeID);
398 device.writeAttr("junction", nodeID);
399 device.openTag("link");
400 device.openTag("predecessor");
401 device.writeAttr("elementType", "road");
402 device.writeAttr("elementId", inEdgeID);
403 device.writeAttr("contactPoint", "end");
404 device.closeTag();
405 device.openTag("successor");
406 device.writeAttr("elementType", "road");
407 device.writeAttr("elementId", outEdgeID);
408 device.writeAttr("contactPoint", "start");
409 device.closeTag();
410 device.closeTag();
411 device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
412 device.openTag("planView");
413 device.setPrecision(8); // geometry hdg requires higher precision
414 OutputDevice_String elevationOSS(3);
415 elevationOSS.setPrecision(8);
416#ifdef DEBUG_SMOOTH_GEOM
417 if (DEBUGCOND) {
418 std::cout << "write planview for internal edge " << cLeft.id << " init=" << init << " fallback=" << fallBackShape
419 << " begShape=" << begShape << " endShape=" << endShape
420 << "\n";
421 }
422#endif
423 if (init.size() == 0) {
424 writeGeomLines(fallBackShape, device, elevationOSS);
425 } else {
426 writeGeomPP3(device, elevationOSS, init, length);
427 }
428 device.setPrecision(gPrecision);
429 device.closeTag();
430 writeElevationProfile(fallBackShape, device, elevationOSS);
431 device << " <lateralProfile/>\n";
432 device << " <lanes>\n";
433 if (laneOffset != 0) {
434 device << " <laneOffset s=\"0\" a=\"" << laneOffset << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
435 }
436 device << " <laneSection s=\"0\">\n";
437 writeEmptyCenterLane(device, centerMark, 0);
438 device << " <right>\n";
439 int toIndexInternal = 0;
440 for (int j = (int)parallel.size(); --j >= 0;) {
441 toIndexInternal--;
442 const NBEdge::Connection& c = parallel[j];
443 const int fromIndex = c.fromLane - inEdge->getNumLanes();
444 const int toIndex = c.toLane - outEdge->getNumLanes();
445 device << " <lane id=\"-" << parallel.size() - j << "\" type=\"" << getLaneType(outEdge->getPermissions(c.toLane)) << "\" level=\"true\">\n";
446 device << " <link>\n";
447 device << " <predecessor id=\"" << fromIndex << "\"/>\n";
448 device << " <successor id=\"" << toIndex << "\"/>\n";
449 device << " </link>\n";
450 device << " <width sOffset=\"0\" a=\"" << outEdge->getLaneWidth(c.toLane) << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
451 std::string markType = "broken";
452 if (inEdge->isTurningDirectionAt(outEdge)) {
453 markType = "none";
454 } else if (c.fromLane == 0 && c.toLane == 0 && isOuterEdge) {
455 // solid road mark at the outer border
456 markType = "solid";
457 } else if (isOuterEdge && j > 0
458 && (outEdge->getPermissions(parallel[j - 1].toLane) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
459 // solid road mark to the left of sidewalk or bicycle lane
460 markType = "solid";
461 } else if (!inEdge->getToNode()->geometryLike()) {
462 // draw shorter road marks to indicate turning paths
463 LinkDirection dir = inEdge->getToNode()->getDirection(inEdge, outEdge, OptionsCont::getOptions().getBool("lefthand"));
465 // XXX <type><line/><type> is not rendered by odrViewer so cannot be validated
466 // device << " <type name=\"broken\" width=\"0.13\">\n";
467 // device << " <line length=\"0.5\" space=\"0.5\" tOffset=\"0\" sOffset=\"0\" rule=\"none\"/>\n";
468 // device << " </type>\n";
469 markType = "none";
470 }
471 }
472 device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
473 device << " <speed sOffset=\"0\" max=\"" << c.vmax << "\"/>\n";
474 device << " </lane>\n";
475
476 junctionDevice << " <laneLink from=\"" << fromIndex << "\" to=\"" << toIndexInternal << "\"/>\n";
477 connectionID++;
478 }
479 device << " </right>\n";
480 device << " </laneSection>\n";
481 device << " </lanes>\n";
482 device << " <objects/>\n";
483 UNUSED_PARAMETER(signalLanes);
484 device << " <signals/>\n";
485 device.closeTag();
486 junctionDevice << " </connection>\n";
487
488 return connectionID;
489}
490
491
492double
493NWWriter_OpenDrive::writeGeomLines(const PositionVector& shape, OutputDevice& device, OutputDevice& elevationDevice, double offset) {
494 for (int j = 0; j < (int)shape.size() - 1; ++j) {
495 const Position& p = shape[j];
496 const Position& p2 = shape[j + 1];
497 const double hdg = shape.angleAt2D(j);
498 const double length = p.distanceTo2D(p2);
499 device.openTag("geometry");
500 device.writeAttr("s", offset);
501 device.writeAttr("x", p.x());
502 device.writeAttr("y", p.y());
503 device.writeAttr("hdg", hdg);
504 device.writeAttr("length", length);
505 device.openTag("line").closeTag();
506 device.closeTag();
507 elevationDevice << " <elevation s=\"" << offset << "\" a=\"" << p.z() << "\" b=\"" << (p2.z() - p.z()) / MAX2(POSITION_EPS, length) << "\" c=\"0\" d=\"0\"/>\n";
508 offset += length;
509 }
510 return offset;
511}
512
513
514void
515NWWriter_OpenDrive::writeEmptyCenterLane(OutputDevice& device, const std::string& mark, double markWidth) {
516 device << " <center>\n";
517 device << " <lane id=\"0\" type=\"none\" level=\"true\">\n";
518 device << " <link/>\n";
519 device << " <roadMark sOffset=\"0\" type=\"" << mark << "\" weight=\"standard\" color=\"standard\" width=\"" << markWidth << "\"/>\n";
520 device << " </lane>\n";
521 device << " </center>\n";
522}
523
524
525int
526NWWriter_OpenDrive::getID(const std::string& origID, StringBijection<int>& map, int& lastID) {
527 if (map.hasString(origID)) {
528 return map.get(origID);
529 }
530 map.insert(origID, lastID++);
531 return lastID - 1;
532}
533
534
535std::string
537 switch (permissions) {
538 case SVC_PEDESTRIAN:
539 return "sidewalk";
540 //case (SVC_BICYCLE | SVC_PEDESTRIAN):
541 // WRITE_WARNING("Ambiguous lane type (biking+driving) for road '" + roadID + "'");
542 // return "sidewalk";
543 case SVC_BICYCLE:
544 return "biking";
545 case 0:
546 // ambiguous
547 return "none";
548 case SVC_RAIL:
549 case SVC_RAIL_URBAN:
551 case SVC_RAIL_FAST:
552 return "rail";
553 case SVC_TRAM:
554 return "tram";
555 default: {
556 // complex permissions
557 if (permissions == SVCAll) {
558 return "driving";
559 } else if (isRailway(permissions)) {
560 return "rail";
561 } else if ((permissions & SVC_PASSENGER) != 0) {
562 return "driving";
563 } else {
564 return "restricted";
565 }
566 }
567 }
568}
569
570
572NWWriter_OpenDrive::getLeftLaneBorder(const NBEdge* edge, int laneIndex, double widthOffset) {
573 const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
574 if (laneIndex == -1) {
575 // leftmost lane
576 laneIndex = lefthand ? 0 : (int)edge->getNumLanes() - 1;
577 }
579 // PositionVector result = edge->getLaneShape(laneIndex);
580 // (and the moveo2side)
581 // However, the lanes in SUMO have a small lateral gap (SUMO_const_laneOffset) to account for markings
582 // In OpenDRIVE this gap does not exists so we have to do all lateral
583 // computations based on the reference line
584 // This assumes that the 'stop line' for all lanes is colinear!
585 const int leftmost = lefthand ? 0 : (int)edge->getNumLanes() - 1;
586 widthOffset -= (edge->getLaneWidth(leftmost) / 2);
587 // collect lane widths from left border of edge to left border of lane to connect to
588 if (lefthand) {
589 for (int i = leftmost; i < laneIndex; i++) {
590 widthOffset += edge->getLaneWidth(i);
591 }
592 } else {
593 for (int i = leftmost; i > laneIndex; i--) {
594 widthOffset += edge->getLaneWidth(i);
595 }
596 }
597 PositionVector result = edge->getLaneShape(leftmost);
598 try {
599 result.move2side(widthOffset);
600 } catch (InvalidArgument&) { }
601 return result;
602}
603
606 return getLeftLaneBorder(edge, laneIndex, edge->getLaneWidth(laneIndex));
607}
608
609
610double
612 OutputDevice& device,
613 OutputDevice& elevationDevice,
614 PositionVector init,
615 double length,
616 double offset) {
617 assert(init.size() == 3 || init.size() == 4);
618
619 // avoid division by 0
620 length = MAX2(POSITION_EPS, length);
621
622 const Position p = init.front();
623 const double hdg = init.angleAt2D(0);
624
625 // backup elevation values
626 const PositionVector initZ = init;
627 // translate to u,v coordinates
628 init.add(-p.x(), -p.y(), -p.z());
629 init.rotate2D(-hdg);
630
631 // parametric coefficients
632 double aU, bU, cU, dU;
633 double aV, bV, cV, dV;
634 double aZ, bZ, cZ, dZ;
635
636 // unfactor the Bernstein polynomials of degree 2 (or 3) and collect the coefficients
637 if (init.size() == 3) {
638 //f(x, a, b ,c) = a + (2*b - 2*a)*x + (a - 2*b + c)*x*x
639 aU = init[0].x();
640 bU = 2 * init[1].x() - 2 * init[0].x();
641 cU = init[0].x() - 2 * init[1].x() + init[2].x();
642 dU = 0;
643
644 aV = init[0].y();
645 bV = 2 * init[1].y() - 2 * init[0].y();
646 cV = init[0].y() - 2 * init[1].y() + init[2].y();
647 dV = 0;
648
649 // elevation is not parameteric on [0:1] but on [0:length]
650 aZ = initZ[0].z();
651 bZ = (2 * initZ[1].z() - 2 * initZ[0].z()) / length;
652 cZ = (initZ[0].z() - 2 * initZ[1].z() + initZ[2].z()) / (length * length);
653 dZ = 0;
654
655 } else {
656 // 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))
657 aU = init[0].x();
658 bU = 3 * init[1].x() - 3 * init[0].x();
659 cU = 3 * init[0].x() - 6 * init[1].x() + 3 * init[2].x();
660 dU = -init[0].x() + 3 * init[1].x() - 3 * init[2].x() + init[3].x();
661
662 aV = init[0].y();
663 bV = 3 * init[1].y() - 3 * init[0].y();
664 cV = 3 * init[0].y() - 6 * init[1].y() + 3 * init[2].y();
665 dV = -init[0].y() + 3 * init[1].y() - 3 * init[2].y() + init[3].y();
666
667 // elevation is not parameteric on [0:1] but on [0:length]
668 aZ = initZ[0].z();
669 bZ = (3 * initZ[1].z() - 3 * initZ[0].z()) / length;
670 cZ = (3 * initZ[0].z() - 6 * initZ[1].z() + 3 * initZ[2].z()) / (length * length);
671 dZ = (-initZ[0].z() + 3 * initZ[1].z() - 3 * initZ[2].z() + initZ[3].z()) / (length * length * length);
672 }
673
674 device.openTag("geometry");
675 device.writeAttr("s", offset);
676 device.writeAttr("x", p.x());
677 device.writeAttr("y", p.y());
678 device.writeAttr("hdg", hdg);
679 device.writeAttr("length", length);
680
681 device.openTag("paramPoly3");
682 device.writeAttr("aU", aU);
683 device.writeAttr("bU", bU);
684 device.writeAttr("cU", cU);
685 device.writeAttr("dU", dU);
686 device.writeAttr("aV", aV);
687 device.writeAttr("bV", bV);
688 device.writeAttr("cV", cV);
689 device.writeAttr("dV", dV);
690 device.writeAttr("pRange", "normalized");
691 device.closeTag();
692 device.closeTag();
693
694 // write elevation
695 elevationDevice.openTag("elevation");
696 elevationDevice.writeAttr("s", offset);
697 elevationDevice.writeAttr("a", aZ);
698 elevationDevice.writeAttr("b", bZ);
699 elevationDevice.writeAttr("c", cZ);
700 elevationDevice.writeAttr("d", dZ);
701 elevationDevice.closeTag();
702
703 return offset + length;
704}
705
706
707bool
708NWWriter_OpenDrive::writeGeomSmooth(const PositionVector& shape, double speed, OutputDevice& device, OutputDevice& elevationDevice, double straightThresh, double& length) {
709#ifdef DEBUG_SMOOTH_GEOM
710 if (DEBUGCOND) {
711 std::cout << "writeGeomSmooth\n n=" << shape.size() << " shape=" << toString(shape) << "\n";
712 }
713#endif
714 bool ok = true;
715 const double longThresh = speed; // 16.0; // make user-configurable (should match the sampling rate of the source data)
716 const double curveCutout = longThresh / 2; // 8.0; // make user-configurable (related to the maximum turning rate)
717 // 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')
718 assert(longThresh >= 2 * curveCutout);
719 assert(shape.size() > 2);
720 // add intermediate points wherever there is a strong angular change between long segments
721 // assume the geometry is simplified so as not to contain consecutive colinear points
722 PositionVector shape2 = shape;
723 double maxAngleDiff = 0;
724 double offset = 0;
725 for (int j = 1; j < (int)shape.size() - 1; ++j) {
726 //const double hdg = shape.angleAt2D(j);
727 const Position& p0 = shape[j - 1];
728 const Position& p1 = shape[j];
729 const Position& p2 = shape[j + 1];
730 const double dAngle = fabs(GeomHelper::angleDiff(p0.angleTo2D(p1), p1.angleTo2D(p2)));
731 const double length1 = p0.distanceTo2D(p1);
732 const double length2 = p1.distanceTo2D(p2);
733 maxAngleDiff = MAX2(maxAngleDiff, dAngle);
734#ifdef DEBUG_SMOOTH_GEOM
735 if (DEBUGCOND) {
736 std::cout << " j=" << j << " dAngle=" << RAD2DEG(dAngle) << " length1=" << length1 << " length2=" << length2 << "\n";
737 }
738#endif
739 if (dAngle > straightThresh
740 && (length1 > longThresh || j == 1)
741 && (length2 > longThresh || j == (int)shape.size() - 2)) {
742 // NBNode::bezierControlPoints checks for minimum length of POSITION_EPS so we make sure there is no instability
743 shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 - MIN2(length1 - 2 * POSITION_EPS, curveCutout)), false);
744 shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 + MIN2(length2 - 2 * POSITION_EPS, curveCutout)), false);
745 shape2.removeClosest(p1);
746 }
747 offset += length1;
748 }
749 const int numPoints = (int)shape2.size();
750#ifdef DEBUG_SMOOTH_GEOM
751 if (DEBUGCOND) {
752 std::cout << " n=" << numPoints << " shape2=" << toString(shape2) << "\n";
753 }
754#endif
755
756 if (maxAngleDiff < straightThresh) {
757 length = writeGeomLines(shape2, device, elevationDevice, 0);
758#ifdef DEBUG_SMOOTH_GEOM
759 if (DEBUGCOND) {
760 std::cout << " special case: all lines. maxAngleDiff=" << maxAngleDiff << "\n";
761 }
762#endif
763 return ok;
764 }
765
766 // write the long segments as lines, short segments as curves
767 offset = 0;
768 for (int j = 0; j < numPoints - 1; ++j) {
769 const Position& p0 = shape2[j];
770 const Position& p1 = shape2[j + 1];
771 PositionVector line;
772 line.push_back(p0);
773 line.push_back(p1);
774 const double lineLength = line.length2D();
775 if (lineLength >= longThresh) {
776 offset = writeGeomLines(line, device, elevationDevice, offset);
777#ifdef DEBUG_SMOOTH_GEOM
778 if (DEBUGCOND) {
779 std::cout << " writeLine=" << toString(line) << "\n";
780 }
781#endif
782 } else {
783 // find control points
784 PositionVector begShape;
785 PositionVector endShape;
786 if (j == 0 || j == numPoints - 2) {
787 // keep the angle of the first/last segment but end at the front of the shape
788 begShape = line;
789 begShape.add(p0 - begShape.back());
790 } else if (j == 1 || p0.distanceTo2D(shape2[j - 1]) > longThresh) {
791 // use the previous segment if it is long or the first one
792 begShape.push_back(shape2[j - 1]);
793 begShape.push_back(p0);
794 } else {
795 // end at p0 with mean angle of the previous and current segment
796 begShape.push_back(shape2[j - 1]);
797 begShape.push_back(p1);
798 begShape.add(p0 - begShape.back());
799 }
800
801 if (j == 0 || j == numPoints - 2) {
802 // keep the angle of the first/last segment but start at the end of the shape
803 endShape = line;
804 endShape.add(p1 - endShape.front());
805 } else if (j == numPoints - 3 || p1.distanceTo2D(shape2[j + 2]) > longThresh) {
806 // use the next segment if it is long or the final one
807 endShape.push_back(p1);
808 endShape.push_back(shape2[j + 2]);
809 } else {
810 // start at p1 with mean angle of the current and next segment
811 endShape.push_back(p0);
812 endShape.push_back(shape2[j + 2]);
813 endShape.add(p1 - endShape.front());
814 }
815 const double extrapolateLength = MIN2((double)25, lineLength / 4);
816 PositionVector init = NBNode::bezierControlPoints(begShape, endShape, false, extrapolateLength, extrapolateLength, ok, nullptr, straightThresh);
817 if (init.size() == 0) {
818 // could not compute control points, write line
819 offset = writeGeomLines(line, device, elevationDevice, offset);
820#ifdef DEBUG_SMOOTH_GEOM
821 if (DEBUGCOND) {
822 std::cout << " writeLine lineLength=" << lineLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
823 }
824#endif
825 } else {
826 // write bezier
827 const double curveLength = init.bezier(12).length2D();
828 offset = writeGeomPP3(device, elevationDevice, init, curveLength, offset);
829#ifdef DEBUG_SMOOTH_GEOM
830 if (DEBUGCOND) {
831 std::cout << " writeCurve lineLength=" << lineLength << " curveLength=" << curveLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
832 }
833#endif
834 }
835 }
836 }
837 length = offset;
838 return ok;
839}
840
841
842void
844 // check if the shape is flat
845 bool flat = true;
846 double z = shape.size() == 0 ? 0 : shape[0].z();
847 for (int i = 1; i < (int)shape.size(); ++i) {
848 if (fabs(shape[i].z() - z) > NUMERICAL_EPS) {
849 flat = false;
850 break;
851 }
852 }
853 device << " <elevationProfile>\n";
854 if (flat) {
855 device << " <elevation s=\"0\" a=\"" << z << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
856 } else {
857 device << elevationDevice.getString();
858 }
859 device << " </elevationProfile>\n";
860
861}
862
863
864void
866 if (e->getNumLanes() > 1) {
867 // compute 'stop line' of rightmost lane
868 const PositionVector shape0 = e->getLaneShape(0);
869 assert(shape0.size() >= 2);
870 const Position& from = shape0[-2];
871 const Position& to = shape0[-1];
872 PositionVector stopLine;
873 stopLine.push_back(to);
874 stopLine.push_back(to - PositionVector::sideOffset(from, to, -1000.0));
875 // endpoints of all other lanes should be on the stop line
876 for (int lane = 1; lane < e->getNumLanes(); ++lane) {
877 const double dist = stopLine.distance2D(e->getLaneShape(lane)[-1]);
878 if (dist > NUMERICAL_EPS) {
879 WRITE_WARNING("Uneven stop line at lane '" + e->getLaneID(lane) + "' (dist=" + toString(dist) + ") cannot be represented in OpenDRIVE.");
880 }
881 }
882 }
883}
884
885void
887 if (e->knowsParameter("roadObjects")) {
888 device.openTag("objects");
889 device.setPrecision(8); // geometry hdg requires higher precision
891 for (std::string id : StringTokenizer(e->getParameter("roadObjects", "")).getVector()) {
892 SUMOPolygon* p = shc.getPolygons().get(id);
893 if (p == nullptr) {
894 WRITE_WARNING("Road object polygon '" + id + "' not found for edge '" + e->getID() + "'");
895 } else if (p->getShape().size() != 4) {
896 WRITE_WARNING("Cannot convert road object polygon '" + id + "' with " + toString(p->getShape().size()) + " points for edge '" + e->getID() + "'");
897 } else {
898 const PositionVector& shape = p->getShape();
899 device.openTag("object");
900 Position center = shape.getPolygonCenter();
901 PositionVector sideline = shape.getSubpartByIndex(0, 2);
902 PositionVector ortholine = shape.getSubpartByIndex(1, 2);
903 const double absAngle = sideline.angleAt2D(0);
904 const double length = sideline.length2D();
905 const double width = ortholine.length2D();
906 const double edgeOffset = road.nearest_offset_to_point2D(center);
907 if (edgeOffset == GeomHelper::INVALID_OFFSET) {
908 WRITE_WARNING("Cannot map road object polygon '" + id + "' with center " + toString(center) + " onto edge '" + e->getID() + "'");
909 continue;
910 }
911 Position edgePos = road.positionAtOffset2D(edgeOffset);
912 const double edgeAngle = road.rotationAtOffset(edgeOffset);
913 const double relAngle = absAngle - edgeAngle;
914 double sideOffset = center.distanceTo2D(edgePos);
915 // determine sign of sideOffset
916 PositionVector tmp = road.getSubpart2D(MAX2(0.0, edgeOffset - 1), MIN2(road.length2D(), edgeOffset + 1));
917 tmp.move2side(sideOffset);
918 if (tmp.distance2D(center) < sideOffset) {
919 sideOffset *= -1;
920 }
921 //std::cout << " id=" << id
922 // << " shape=" << shape
923 // << " center=" << center
924 // << " edgeOffset=" << edgeOffset
925 // << "\n";
926 device.writeAttr("id", id);
927 device.writeAttr("type", p->getShapeType());
928 device.writeAttr("name", p->getParameter("name", ""));
929 device.writeAttr("s", edgeOffset);
930 device.writeAttr("t", sideOffset);
931 device.writeAttr("width", width);
932 device.writeAttr("length", length);
933 device.writeAttr("hdg", relAngle);
934 device.closeTag();
935 }
936 }
937 device.setPrecision(gPrecision);
938 device.closeTag();
939 } else {
940 device << " <objects/>\n";
941 }
942}
943
944
945void
946NWWriter_OpenDrive::writeSignals(OutputDevice& device, const NBEdge* e, double length,
947 SignalLanes& signalLanes) {
948 device.openTag("signals");
949 if (e->getToNode()->isTLControlled()) {
950 // try to faithfully represent the SUMO signal layout
951 // (if a realistic number of signals is needed, the user should set
952 // option --tls.group-signals)
954 std::map<std::string, bool> toWrite;
955 for (const NBConnection& c : tl->getControlledLinks()) {
956 if (c.getFrom() == e) {
957 const std::string id = tl->getID() + "_" + toString(c.getTLIndex());
958 if (toWrite.count(id) == 0) {
959 toWrite[id] = signalLanes.count(id) == 0;
960 }
961 signalLanes[id].first.insert(c.getFromLane());
962 signalLanes[id].second.insert(e->getToNode()->getDirection(e, c.getTo()));
963 }
964 }
965 for (auto item : toWrite) {
966 const std::string id = item.first;
967 const bool isNew = item.second;
968 const std::set<LinkDirection>& dirs = signalLanes[id].second;
969 const bool l = dirs.count(LinkDirection::LEFT) != 0 || dirs.count(LinkDirection::PARTLEFT) != 0;
970 const bool r = dirs.count(LinkDirection::RIGHT) != 0 || dirs.count(LinkDirection::PARTRIGHT) != 0;
971 const bool s = dirs.count(LinkDirection::STRAIGHT) != 0;
972 const std::string tag = isNew ? "signal" : "signalReference";
973 int firstLane = *signalLanes[id].first.begin();
974 double t = e->getLaneWidth(firstLane) * 0.5;
975 for (int i = firstLane + 1; i < e->getNumLanes(); i++) {
976 t += e->getLaneWidth(i);
977 }
978 device.openTag(tag);
979 device.writeAttr("id", id);
980 device.writeAttr("s", length);
981 device.writeAttr("t", -t);
982 device.writeAttr("orientation", "+");
983 if (isNew) {
984 int type = 1000001;
985 int subType = -1;
986 if (l && !s && !r) {
987 type = 1000011;
988 subType = 10;
989 } else if (!l && !s && r) {
990 type = 1000011;
991 subType = 20;
992 } else if (!l && s && !r) {
993 type = 1000011;
994 subType = 30;
995 } else if (l && s && !r) {
996 type = 1000011;
997 subType = 40;
998 } else if (!l && s && r) {
999 type = 1000011;
1000 subType = 50;
1001 }
1002 device.writeAttr("dynamic", "yes");
1003 device.writeAttr("zOffset", 5);
1004 device.writeAttr("country", "OpenDRIVE");
1005 device.writeAttr("type", type);
1006 device.writeAttr("subtype", subType);
1007 device.writeAttr("height", 0.78);
1008 device.writeAttr("width", 0.26);
1009 }
1010 device.openTag("validity");
1011 for (int lane : signalLanes[id].first) {
1012 device.writeAttr("fromLane", lane);
1013 device.writeAttr("toLane", lane);
1014 }
1015 device.closeTag();
1016 device.closeTag();
1017 }
1018 }
1019 device.closeTag();
1020}
1021
1022/****************************************************************************/
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define RAD2DEG(x)
Definition: GeomHelper.h:36
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:265
#define DEBUGCOND
#define INVALID_ID
const SVCPermissions SVCAll
all VClasses are allowed
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permission is a railway edge.
@ 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
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
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:25
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:30
T MIN2(T a, T b)
Definition: StdDefs.h:71
T MAX2(T a, T b)
Definition: StdDefs.h:77
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
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
Definition: GeoConvHelper.h:53
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.
Definition: GeomHelper.cpp:179
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:170
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition: NBEdgeCont.h:177
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:4137
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:1043
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:648
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:552
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1426
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:736
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:625
const std::string & getID() const
Definition: NBEdge.h:1526
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:3430
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:526
std::string getLaneID(int lane) const
get lane ID
Definition: NBEdge.cpp:3776
const std::string & getStreetName() const
Returns the street name of this edge.
Definition: NBEdge.h:675
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:545
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:962
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1347
Instance responsible for building networks.
Definition: NBNetBuilder.h:107
NBNodeCont & getNodeCont()
Returns a reference to the node container.
Definition: NBNetBuilder.h:144
ShapeContainer & getShapeCont()
Definition: NBNetBuilder.h:177
NBEdgeCont & getEdgeCont()
Definition: NBNetBuilder.h:139
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:58
int size() const
Returns the number of nodes stored in this container.
Definition: NBNodeCont.h:295
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:2229
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:326
int numNormalConnections() const
return the number of lane-to-lane connections at this junction (excluding crossings)
Definition: NBNode.cpp:3604
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3468
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition: NBNode.h:321
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:544
The base class for traffic light logic definitions.
const NBConnectionVector & getControlledLinks() const
returns the controlled links (depends on previous call to collectLinks)
static void writeSignals(OutputDevice &device, const NBEdge *e, double length, SignalLanes &signalLanes)
write signal record for traffic light
static void addPedestrianConnection(const NBEdge *inEdge, const NBEdge *outEdge, std::vector< NBEdge::Connection > &parallel)
static void checkLaneGeometries(const NBEdge *e)
check if the lane geometries are compatible with OpenDRIVE assumptions (colinear stop line)
std::map< std::string, std::pair< std::set< int >, std::set< LinkDirection > > > SignalLanes
signalID -> (lanes, dirs)
static void writeEmptyCenterLane(OutputDevice &device, const std::string &mark, double markWidth)
static void writeElevationProfile(const PositionVector &shape, OutputDevice &device, const OutputDevice_String &elevationDevice)
static PositionVector getRightLaneBorder(const NBEdge *edge, int laneIndex=-1)
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)
write normal edge to device
static std::string getLaneType(SVCPermissions permissions)
static bool writeGeomSmooth(const PositionVector &shape, double speed, OutputDevice &device, OutputDevice &elevationDevice, double straightThresh, double &length)
static void writeRoadObjects(OutputDevice &device, const NBEdge *e, const ShapeContainer &shc)
write road objects referenced as edge parameters
static PositionVector getLeftLaneBorder(const NBEdge *edge, int laneIndex=-1, double widthOffset=0)
get the left border of the given lane (the leftmost one by default)
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 double writeGeomLines(const PositionVector &shape, OutputDevice &device, OutputDevice &elevationDevice, double offset=0)
write geometry as sequence of lines (sumo style)
const std::string & getID() const
Returns the id.
Definition: Named.h:74
T get(const std::string &id) const
Retrieves an item.
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.
Definition: OptionsCont.cpp:59
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.
Definition: OutputDevice.h:61
void lf()
writes a line feed if applicable
Definition: OutputDevice.h:239
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:251
OutputDevice & writePreformattedTag(const std::string &val)
writes a preformatted tag to the device but ensures that any pending tags are closed
Definition: OutputDevice.h:308
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.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
bool knowsParameter(const std::string &key) const
Returns whether the parameter is known.
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:252
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
Definition: Position.h:262
double y() const
Returns the y-position.
Definition: Position.h:60
A list of positions.
double length2D() const
Returns the length.
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
void move2side(double amount, double maxExtension=100)
move position vector to side using certain ammount
PositionVector getSubpart2D(double beginOffset, double endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
double angleAt2D(int pos) const
get angle in certain position of position vector
PositionVector bezier(int numPoints)
return a bezier interpolation
int insertAtClosest(const Position &p, bool interpolateZ)
inserts p between the two closest positions
int removeClosest(const Position &p)
removes the point closest to p and return the removal index
PositionVector getSubpartByIndex(int beginIndex, int count) const
get subpart of a position vector using index and a cout
Position positionAtOffset2D(double pos, double lateralOffset=0) const
Returns the position at the given length.
const PositionVector & getShape() const
Returns whether the shape of the polygon.
Definition: SUMOPolygon.cpp:52
Storage for geometrical objects.
const Polygons & getPolygons() const
Returns all polygons.
const std::string & getShapeType() const
Returns the (abstract) type of the Shape.
Definition: Shape.h:77
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 std::string escapeXML(const std::string &orig, const bool maskDoubleHyphen=false)
Replaces the standard escapes by their XML entities.
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:227
int toLane
The lane the connections yields in.
Definition: NBEdge.h:233
NBEdge * toEdge
The edge the connections yields in.
Definition: NBEdge.h:230
double vmax
maximum velocity
Definition: NBEdge.h:290
std::string id
id of Connection
Definition: NBEdge.h:284
std::string type
the type of this lane
Definition: NBEdge.h:192