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.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>
30 #include <netbuild/NBNetBuilder.h>
34 #include <utils/common/StdDefs.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 // ===========================================================================
53 bool NWWriter_OpenDrive::LHLL(false);
54 bool NWWriter_OpenDrive::LHRL(false);
55 
56 // ===========================================================================
57 // method definitions
58 // ===========================================================================
59 // ---------------------------------------------------------------------------
60 // static methods
61 // ---------------------------------------------------------------------------
62 void
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
78  StringBijection<int> edgeMap;
79  StringBijection<int> nodeMap;
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
103  const GeoConvHelper& gch = GeoConvHelper::getFinal();
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()) {
237  NBTrafficLightDefinition* tl = *n->getControllingTLS().begin();
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 
261 std::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 
282 void
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 
412 void
413 NWWriter_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 
428 int
429 NWWriter_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 
591 double
592 NWWriter_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);
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 
613 void
614 NWWriter_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 
624 int
625 NWWriter_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 
634 std::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:
649  case SVC_RAIL_ELECTRIC:
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 
671 NWWriter_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 
689 NWWriter_OpenDrive::getOuterLaneBorder(const NBEdge* edge, int laneIndex) {
690  return getInnerLaneBorder(edge, laneIndex, edge->getLaneWidth(laneIndex));
691 }
692 
693 
694 double
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 
791 bool
792 NWWriter_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 
926 void
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 
948 void
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 
969 void
970 NWWriter_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 
1005 std::vector<NWWriter_OpenDrive::TrafficSign>
1006 NWWriter_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 
1046 NWWriter_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 
1076 void
1077 NWWriter_OpenDrive::writeSignals(OutputDevice& device, const NBEdge* e, double length,
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)
1084  NBTrafficLightDefinition* tl = *e->getToNode()->getControllingTLS().begin();
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.writeAttr("s", length);
1112  device.writeAttr("t", -t);
1113  device.writeAttr("orientation", "+");
1114  if (isNew) {
1115  int type = 1000001;
1116  int subType = -1;
1117  if (l && !s && !r) {
1118  type = 1000011;
1119  subType = 10;
1120  } else if (!l && !s && r) {
1121  type = 1000011;
1122  subType = 20;
1123  } else if (!l && s && !r) {
1124  type = 1000011;
1125  subType = 30;
1126  } else if (l && s && !r) {
1127  type = 1000011;
1128  subType = 40;
1129  } else if (!l && s && r) {
1130  type = 1000011;
1131  subType = 50;
1132  }
1133  device.writeAttr("dynamic", "yes");
1134  device.writeAttr("zOffset", 5);
1135  device.writeAttr("country", "OpenDRIVE");
1136  device.writeAttr("type", type);
1137  device.writeAttr("subtype", subType);
1138  device.writeAttr("height", 0.78);
1139  device.writeAttr("width", 0.26);
1140  }
1141  for (int lane : signalLanes[id].first) {
1142  device.openTag("validity");
1143  device.writeAttr("fromLane", lane);
1144  device.writeAttr("toLane", lane);
1145  device.closeTag();
1146  }
1147  device.closeTag();
1148  }
1149  } else if (e->hasParameter(ROAD_OBJECTS)) {
1150  PositionVector roadShape = getInnerLaneBorder(e);
1151  for (std::string id : StringTokenizer(e->getParameter(ROAD_OBJECTS, "")).getVector()) {
1152  PointOfInterest* poi = shc.getPOIs().get(id);
1153  if (poi != nullptr) {
1154  if (poi->getShapeType() == "traffic_sign" && poi->hasParameter("traffic_sign")) {
1155  std::string traffic_sign_type = poi->getParameter("traffic_sign");
1156 
1157  std::vector<TrafficSign> trafficSigns = parseTrafficSign(traffic_sign_type, poi);
1158 
1159  auto distance = roadShape.nearest_offset_to_point2D(*poi, true);
1160  double t = getRoadSideOffset(e);
1161  double calculatedZOffset = 3.0;
1162  for (auto it = trafficSigns.rbegin(); it != trafficSigns.rend(); ++it) {
1163  TrafficSign trafficSign = *it;
1164  device.openTag("signal");
1165  device.writeAttr("id", id);
1166  device.writeAttr("s", distance);
1167  device.writeAttr("t", t);
1168  device.writeAttr("orientation", "-");
1169  device.writeAttr("dynamic", "no");
1170  device.writeAttr("zOffset", calculatedZOffset);
1171  device.writeAttr("country", trafficSign.country);
1172  device.writeAttr("type", trafficSign.type);
1173  device.writeAttr("subtype", trafficSign.subtype);
1174  device.writeAttr("value", trafficSign.value);
1175  device.writeAttr("height", 0.78);
1176  device.writeAttr("width", 0.78);
1177  device.closeTag();
1178  calculatedZOffset += 0.78;
1179  }
1180  }
1181  }
1182  }
1183  }
1184  device.closeTag();
1185 }
1186 
1187 
1188 double
1190  double t = 0.30;
1191  if (!lefthand || LHLL) {
1192  for (int i = 0; i < e->getNumLanes(); i++) {
1193  t += e->getPermissions(i) == SVC_PEDESTRIAN ? e->getLaneWidth(i) * 0.2 : e->getLaneWidth(i);
1194  }
1195  }
1196  t = LHRL ? t : -t;
1197  return t;
1198 }
1199 
1200 
1201 int
1202 NWWriter_OpenDrive::s2x(int sumoIndex, int numLanes) {
1203  // sumo lanes: 0, 1, 2 (0 being the outermost lane)
1204  // XODR: -3,-2,-1 (written in reverse order)
1205  // LHLL: 3, 2, 1 (written in reverse order)
1206  // lefthand (old):-1,-2,-3
1207  return (lefthand
1208  ? (LHLL
1209  ? numLanes - sumoIndex
1210  : - sumoIndex - 1)
1211  : sumoIndex - numLanes);
1212 }
1213 
1214 
1215 void
1217  if (shc.getPolygons().size() == 0 && shc.getPOIs().size() == 0) {
1218  return;
1219  }
1220  const double maxDist = OptionsCont::getOptions().getFloat("opendrive-output.shape-match-dist");
1221  if (maxDist < 0) {
1222  return;
1223  }
1224  // register custom assignements
1225  std::set<std::string> assigned;
1226  for (auto it = ec.begin(); it != ec.end(); ++it) {
1227  NBEdge* e = it->second;
1228  if (e->hasParameter(ROAD_OBJECTS)) {
1229  for (std::string id : StringTokenizer(e->getParameter(ROAD_OBJECTS, "")).getVector()) {
1230  assigned.insert(id);
1231  }
1232  }
1233  }
1234  // build rtree for edges
1235  NamedRTree r;
1236  for (auto it = ec.begin(); it != ec.end(); ++it) {
1237  NBEdge* edge = it->second;
1238  Boundary bound = edge->getGeometry().getBoxBoundary();
1239  bound.grow(maxDist);
1240  float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1241  float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1242  r.Insert(min, max, edge);
1243  }
1244 
1245  for (auto itPoly = shc.getPolygons().begin(); itPoly != shc.getPolygons().end(); itPoly++) {
1246  SUMOPolygon* p = itPoly->second;
1247  Boundary bound = p->getShape().getBoxBoundary();
1248  float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1249  float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1250  std::set<const Named*> edges;
1251  Named::StoringVisitor visitor(edges);
1252  r.Search(min, max, visitor);
1253  std::vector<std::pair<double, std::string> > nearby;
1254  for (const Named* namedEdge : edges) {
1255  NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
1256  const double distance = VectorHelper<double>::minValue(p->getShape().distances(e->getLaneShape(0), true));
1257  if (distance <= maxDist) {
1258  // sort by distance and ID to stabilize results
1259  nearby.push_back(std::make_pair(distance, e->getID()));
1260  //std::cout << " poly=" << p->getID() << " e=" << e->getID() << " dist=" << distance << "\n";
1261  }
1262  }
1263  if (nearby.size() > 0) {
1264  std::sort(nearby.begin(), nearby.end());
1265  NBEdge* closest = ec.retrieve(nearby.front().second);
1266  std::string objects = closest->getParameter(ROAD_OBJECTS, "");
1267  if (objects != "") {
1268  objects += " ";
1269  }
1270  objects += p->getID();
1271  closest->setParameter(ROAD_OBJECTS, objects);
1272  //std::cout << "poly=" << p->getID() << " closest=" << closest->getID() << "\n";
1273  }
1274  }
1275  for (auto itPoi = shc.getPOIs().begin(); itPoi != shc.getPOIs().end(); itPoi++) {
1276  PointOfInterest* p = itPoi->second;
1277  float min[2] = { static_cast<float>(p->x()), static_cast<float>(p->y()) };
1278  float max[2] = { static_cast<float>(p->x()), static_cast<float>(p->y()) };
1279  std::set<const Named*> edges;
1280  Named::StoringVisitor visitor(edges);
1281  r.Search(min, max, visitor);
1282  std::vector<std::pair<double, std::string> > nearby;
1283  for (const Named* namedEdge : edges) {
1284  NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
1285  const double distance = e->getLaneShape(0).distance2D(*p, true);
1286  if (distance != GeomHelper::INVALID_OFFSET && distance <= maxDist) {
1287  // sort by distance and ID to stabilize results
1288  nearby.push_back(std::make_pair(distance, e->getID()));
1289  //if (p->getID() == "1275468911") {
1290  // std::cout << " poly=" << p->getID() << " e=" << e->getID() << " dist=" << distance << "\n";
1291  //}
1292  }
1293  }
1294  if (nearby.size() > 0) {
1295  std::sort(nearby.begin(), nearby.end());
1296  NBEdge* closest = ec.retrieve(nearby.front().second);
1297  std::string objects = closest->getParameter(ROAD_OBJECTS, "");
1298  if (objects != "") {
1299  objects += " ";
1300  }
1301  objects += p->getID();
1302  closest->setParameter(ROAD_OBJECTS, objects);
1303  //std::cout << "poly=" << p->getID() << " closest=" << closest->getID() << "\n";
1304  }
1305  }
1306 }
1307 
1308 
1309 void
1311  Position center = *poi;
1312  const double edgeOffset = roadShape.nearest_offset_to_point2D(center, false);
1313  if (edgeOffset == GeomHelper::INVALID_OFFSET) {
1314  WRITE_WARNINGF("Cannot map road object POI '%' with center % onto edge '%'", poi->getID(), center, e->getID());
1315  return;
1316  }
1317 
1318  Position edgePos = roadShape.positionAtOffset2D(edgeOffset);
1319  double sideOffset = center.distanceTo2D(edgePos);
1320  // determine sign of sideOffset
1321  PositionVector tmp = roadShape.getSubpart2D(MAX2(0.0, edgeOffset - 1), MIN2(roadShape.length2D(), edgeOffset + 1));
1322  tmp.move2side(sideOffset);
1323  if (tmp.distance2D(center) < sideOffset) {
1324  sideOffset *= -1;
1325  }
1326  // place traffic signs on appropriate side of the road
1327  std::string type = poi->getShapeType();
1328  std::string name = StringUtils::escapeXML(poi->getParameter("name", ""), true);
1329  if (poi->getShapeType() == "traffic_sign") {
1330  sideOffset = getRoadSideOffset(e);
1331  type = "pole";
1332  name = "pole";
1333  }
1334 
1335  device.openTag("object");
1336  device.writeAttr("id", poi->getID());
1337  device.writeAttr("type", type);
1338  device.writeAttr("name", name);
1339  device.writeAttr("s", edgeOffset);
1340  device.writeAttr("t", sideOffset);
1341  device.writeAttr("hdg", 0);
1342  device.closeTag();
1343 }
1344 
1345 void
1347  PositionVector shape = p->getShape();
1348  Position center = shape.getPolygonCenter();
1349 
1350  const double edgeOffset = roadShape.nearest_offset_to_point2D(center, false);
1351  if (edgeOffset == GeomHelper::INVALID_OFFSET) {
1352  WRITE_WARNINGF("Cannot map road object polygon '%' with center % onto edge '%'", p->getID(), center, e->getID());
1353  return;
1354  }
1355  Position edgePos = roadShape.positionAtOffset2D(edgeOffset);
1356  const double edgeAngle = roadShape.rotationAtOffset(edgeOffset);
1357  double sideOffset = center.distanceTo2D(edgePos);
1358  // determine sign of sideOffset
1359  PositionVector tmp = roadShape.getSubpart2D(MAX2(0.0, edgeOffset - 1), MIN2(roadShape.length2D(), edgeOffset + 1));
1360  tmp.move2side(sideOffset);
1361  if (tmp.distance2D(center) < sideOffset) {
1362  sideOffset *= -1;
1363  }
1364  //std::cout << " id=" << id
1365  // << " shape=" << shape
1366  // << " center=" << center
1367  // << " edgeOffset=" << edgeOffset
1368  // << "\n";
1369  auto shapeType = p->getShapeType();
1370  device.openTag("object");
1371  device.writeAttr("id", p->getID());
1372  device.writeAttr("type", shapeType);
1373  if (p->hasParameter("name")) {
1374  device.writeAttr("name", StringUtils::escapeXML(p->getParameter("name", ""), true));
1375  }
1376  device.writeAttr("s", edgeOffset);
1377  device.writeAttr("t", shapeType == "crosswalk" && !lefthand ? 0 : sideOffset);
1378  double hdg = -edgeAngle;
1379  if (p->hasParameter("hdg")) {
1380  try {
1381  hdg = StringUtils::toDoubleSecure(p->getParameter("hdg", ""), 0);
1382  } catch (NumberFormatException&) {}
1383  }
1384  device.writeAttr("hdg", hdg);
1385  if (p->hasParameter("length")) {
1386  try {
1387  device.writeAttr("length", StringUtils::toDoubleSecure(p->getParameter("length", ""), 0));
1388  } catch (NumberFormatException&) {}
1389  }
1390  if (p->hasParameter("width")) {
1391  try {
1392  device.writeAttr("width", StringUtils::toDoubleSecure(p->getParameter("width", ""), 0));
1393  } catch (NumberFormatException&) {}
1394  }
1395  double height = 0;
1396  if (p->hasParameter("height")) {
1397  try {
1398  height = StringUtils::toDoubleSecure(p->getParameter("height", ""), 0);
1399  device.writeAttr("height", height);
1400  } catch (NumberFormatException&) {}
1401  }
1402  //device.openTag("outlines");
1403  device.openTag("outline");
1404  device.writeAttr("id", 0);
1405  device.writeAttr("fillType", "concrete");
1406  device.writeAttr("outer", "true");
1407  device.writeAttr("closed", p->getShape().isClosed() ? "true" : "false");
1408  device.writeAttr("laneType", "border");
1409 
1410  shape.sub(center);
1411  if (hdg != -edgeAngle) {
1412  shape.rotate2D(-edgeAngle - hdg);
1413  }
1414  int i = 0;
1415  for (Position pos : shape) {
1416  device.openTag("cornerLocal");
1417  device.writeAttr("u", pos.x());
1418  device.writeAttr("v", pos.y());
1419  device.writeAttr("z", MAX2(0.0, p->getShapeLayer()));
1420  device.writeAttr("height", height);
1421  device.writeAttr("id", i++);
1422  device.closeTag();
1423  }
1424 
1425 
1426  //device.closeTag();
1427  device.closeTag();
1428  device.closeTag();
1429 }
1430 
1431 /****************************************************************************/
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define RAD2DEG(x)
Definition: GeomHelper.h:36
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define TL(string)
Definition: MsgHandler.h:315
#define DEBUGCOND
#define ROAD_OBJECTS
#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.
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:319
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
const std::string & getProjString() const
Returns the original projection definition.
static const GeoConvHelper & getFinal()
the coordinate transformation for writing the location element and for tracking the original coordina...
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:178
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
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition: NBEdgeCont.h:178
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
Definition: NBEdgeCont.cpp:281
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:4308
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:638
const std::string & getStreetName() const
Returns the street name of this edge.
Definition: NBEdge.h:665
const std::string & getID() const
Definition: NBEdge.h:1522
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:726
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:542
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:615
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:3594
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:516
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:779
std::string getLaneID(int lane) const
get lane ID
Definition: NBEdge.cpp:3946
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:1035
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:967
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1422
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:535
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1358
Instance responsible for building networks.
Definition: NBNetBuilder.h:107
ShapeContainer & getShapeCont()
Definition: NBNetBuilder.h:177
NBEdgeCont & getEdgeCont()
Definition: NBNetBuilder.h:139
NBNodeCont & getNodeCont()
Returns a reference to the node container.
Definition: NBNetBuilder.h:144
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:57
std::map< std::string, NBNode * >::const_iterator begin() const
Returns the pointer to the begin of the stored nodes.
Definition: NBNodeCont.h:113
int size() const
Returns the number of nodes stored in this container.
Definition: NBNodeCont.h:307
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:2349
int numNormalConnections() const
return the number of lane-to-lane connections at this junction (excluding crossings)
Definition: NBNode.cpp:3906
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition: NBNode.cpp:2918
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
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3753
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.
Definition: OptionsCont.cpp:60
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
OutputDevice & writePreformattedTag(const std::string &val)
writes a preformatted tag to the device but ensures that any pending tags are closed
Definition: OutputDevice.h:319
void lf()
writes a line feed if applicable
Definition: OutputDevice.h:242
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.
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:254
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:271
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:281
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.
Definition: SUMOPolygon.cpp:51
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
static const double DEFAULT_ANGLE
Definition: Shape.h:46
double getShapeLayer() const
Returns the layer of the Shape.
Definition: Shape.h:91
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 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)
Definition: VectorHelper.h:97
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