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>
30 #include <netbuild/NBNetBuilder.h>
34 #include <utils/common/StdDefs.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 // ---------------------------------------------------------------------------
53 void
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
67  StringBijection<int> edgeMap;
68  StringBijection<int> nodeMap;
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  // write normal edges (road)
111  for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
112  const NBEdge* e = (*i).second;
113  const int fromNodeID = e->getIncomingEdges().size() > 0 ? getID(e->getFromNode()->getID(), nodeMap, nodeID) : INVALID_ID;
114  const int toNodeID = e->getConnections().size() > 0 ? getID(e->getToNode()->getID(), nodeMap, nodeID) : INVALID_ID;
115  writeNormalEdge(device, e,
116  getID(e->getID(), edgeMap, edgeID),
117  fromNodeID, toNodeID,
118  origNames, straightThresh,
119  nb.getShapeCont());
120  }
121  device.lf();
122 
123  // write junction-internal edges (road). In OpenDRIVE these are called 'paths' or 'connecting roads'
124  OutputDevice_String junctionOSS(3);
125  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
126  NBNode* n = (*i).second;
127  int connectionID = 0; // unique within a junction
128  const int nID = getID(n->getID(), nodeMap, nodeID);
129  if (n->numNormalConnections() > 0) {
130  junctionOSS << " <junction name=\"" << n->getID() << "\" id=\"" << nID << "\">\n";
131  }
132  std::vector<NBEdge*> incoming = (*i).second->getIncomingEdges();
133  if (lefthand) {
134  std::reverse(incoming.begin(), incoming.end());
135  }
136  for (NBEdge* inEdge : incoming) {
137  std::string centerMark = "none";
138  const int inEdgeID = getID(inEdge->getID(), edgeMap, edgeID);
139  // group parallel edges
140  const NBEdge* outEdge = nullptr;
141  bool isOuterEdge = true; // determine where a solid outer border should be drawn
142  int lastFromLane = -1;
143  std::vector<NBEdge::Connection> parallel;
144  std::vector<NBEdge::Connection> connections = inEdge->getConnections();
145  if (lefthand) {
146  std::reverse(connections.begin(), connections.end());
147  }
148  for (const NBEdge::Connection& c : connections) {
149  assert(c.toEdge != 0);
150  if (outEdge != c.toEdge || c.fromLane == lastFromLane) {
151  if (outEdge != nullptr) {
152  if (isOuterEdge) {
153  addPedestrianConnection(inEdge, outEdge, parallel);
154  }
155  connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
156  getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
157  inEdgeID,
158  getID(outEdge->getID(), edgeMap, edgeID),
159  connectionID,
160  parallel, isOuterEdge, straightThresh, centerMark);
161  parallel.clear();
162  isOuterEdge = false;
163  }
164  outEdge = c.toEdge;
165  }
166  lastFromLane = c.fromLane;
167  parallel.push_back(c);
168  }
169  if (isOuterEdge) {
170  addPedestrianConnection(inEdge, outEdge, parallel);
171  }
172  if (!parallel.empty()) {
173  if (!lefthand && (n->geometryLike() || inEdge->isTurningDirectionAt(outEdge))) {
174  centerMark = "solid";
175  }
176  connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
177  getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
178  inEdgeID,
179  getID(outEdge->getID(), edgeMap, edgeID),
180  connectionID,
181  parallel, isOuterEdge, straightThresh, centerMark);
182  parallel.clear();
183  }
184  }
185  if (n->numNormalConnections() > 0) {
186  junctionOSS << " </junction>\n";
187  }
188  }
189  device.lf();
190  // write junctions (junction)
191  device << junctionOSS.getString();
192 
193  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
194  NBNode* n = (*i).second;
195  const std::vector<NBEdge*>& incoming = n->getIncomingEdges();
196  // check if any connections must be written
197  int numConnections = 0;
198  for (std::vector<NBEdge*>::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
199  numConnections += (int)((*j)->getConnections().size());
200  }
201  if (numConnections == 0) {
202  continue;
203  }
204  for (std::vector<NBEdge*>::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
205  const NBEdge* inEdge = *j;
206  const std::vector<NBEdge::Connection>& elv = inEdge->getConnections();
207  for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
208  const NBEdge::Connection& c = *k;
209  const NBEdge* outEdge = c.toEdge;
210  if (outEdge == nullptr) {
211  continue;
212  }
213  }
214  }
215  }
216 
217  device.closeTag();
218  device.close();
219 }
220 
221 
222 void
224  int edgeID, int fromNodeID, int toNodeID,
225  const bool origNames,
226  const double straightThresh,
227  const ShapeContainer& shc) {
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  device << " <signals/>\n";
323  if (origNames) {
324  device << " <userData code=\"sumoId\" value=\"" << e->getID() << "\"/>\n";
325  }
326  device.closeTag();
328 }
329 
330 void
331 NWWriter_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 
346 int
347 NWWriter_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  assert(parallel.size() != 0);
355  const NBEdge::Connection& cLeft = parallel.back();
356  const NBEdge* outEdge = cLeft.toEdge;
357  PositionVector begShape = getLeftLaneBorder(inEdge, cLeft.fromLane);
358  PositionVector endShape = getLeftLaneBorder(outEdge, cLeft.toLane);
359  //std::cout << "computing reference line for internal lane " << cLeft.getInternalLaneID() << " begLane=" << inEdge->getLaneShape(cLeft.fromLane) << " endLane=" << outEdge->getLaneShape(cLeft.toLane) << "\n";
360 
361  double length;
362  double laneOffset = 0;
363  PositionVector fallBackShape;
364  fallBackShape.push_back(begShape.back());
365  fallBackShape.push_back(endShape.front());
366  const bool turnaround = inEdge->isTurningDirectionAt(outEdge);
367  bool ok = true;
368  PositionVector init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
369  if (init.size() == 0) {
370  length = fallBackShape.length2D();
371  // problem with turnarounds is known, method currently returns 'ok' (#2539)
372  if (!ok) {
373  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.");
374  } else if (length <= NUMERICAL_EPS) {
375  // left-curving geometry-like edges must use the right
376  // side as reference line and shift
377  begShape = getRightLaneBorder(inEdge, cLeft.fromLane);
378  endShape = getRightLaneBorder(outEdge, cLeft.toLane);
379  init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
380  if (init.size() != 0) {
381  length = init.bezier(12).length2D();
382  laneOffset = outEdge->getLaneWidth(cLeft.toLane);
383  //std::cout << " internalLane=" << cLeft.getInternalLaneID() << " length=" << length << "\n";
384  }
385  }
386  } else {
387  length = init.bezier(12).length2D();
388  }
389 
390  junctionDevice << " <connection id=\"" << connectionID << "\" incomingRoad=\"" << inEdgeID << "\" connectingRoad=\"" << edgeID << "\" contactPoint=\"start\">\n";
391  device.openTag("road");
392  device.writeAttr("name", cLeft.id);
393  device.setPrecision(8); // length requires higher precision
394  device.writeAttr("length", MAX2(POSITION_EPS, length));
395  device.setPrecision(gPrecision);
396  device.writeAttr("id", edgeID);
397  device.writeAttr("junction", nodeID);
398  device.openTag("link");
399  device.openTag("predecessor");
400  device.writeAttr("elementType", "road");
401  device.writeAttr("elementId", inEdgeID);
402  device.writeAttr("contactPoint", "end");
403  device.closeTag();
404  device.openTag("successor");
405  device.writeAttr("elementType", "road");
406  device.writeAttr("elementId", outEdgeID);
407  device.writeAttr("contactPoint", "start");
408  device.closeTag();
409  device.closeTag();
410  device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
411  device.openTag("planView");
412  device.setPrecision(8); // geometry hdg requires higher precision
413  OutputDevice_String elevationOSS(3);
414  elevationOSS.setPrecision(8);
415 #ifdef DEBUG_SMOOTH_GEOM
416  if (DEBUGCOND) {
417  std::cout << "write planview for internal edge " << cLeft.id << " init=" << init << " fallback=" << fallBackShape
418  << " begShape=" << begShape << " endShape=" << endShape
419  << "\n";
420  }
421 #endif
422  if (init.size() == 0) {
423  writeGeomLines(fallBackShape, device, elevationOSS);
424  } else {
425  writeGeomPP3(device, elevationOSS, init, length);
426  }
427  device.setPrecision(gPrecision);
428  device.closeTag();
429  writeElevationProfile(fallBackShape, device, elevationOSS);
430  device << " <lateralProfile/>\n";
431  device << " <lanes>\n";
432  if (laneOffset != 0) {
433  device << " <laneOffset s=\"0\" a=\"" << laneOffset << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
434  }
435  device << " <laneSection s=\"0\">\n";
436  writeEmptyCenterLane(device, centerMark, 0);
437  device << " <right>\n";
438  int toIndexInternal = 0;
439  for (int j = (int)parallel.size(); --j >= 0;) {
440  toIndexInternal--;
441  const NBEdge::Connection& c = parallel[j];
442  const int fromIndex = c.fromLane - inEdge->getNumLanes();
443  const int toIndex = c.toLane - outEdge->getNumLanes();
444  device << " <lane id=\"-" << parallel.size() - j << "\" type=\"" << getLaneType(outEdge->getPermissions(c.toLane)) << "\" level=\"true\">\n";
445  device << " <link>\n";
446  device << " <predecessor id=\"" << fromIndex << "\"/>\n";
447  device << " <successor id=\"" << toIndex << "\"/>\n";
448  device << " </link>\n";
449  device << " <width sOffset=\"0\" a=\"" << outEdge->getLaneWidth(c.toLane) << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
450  std::string markType = "broken";
451  if (inEdge->isTurningDirectionAt(outEdge)) {
452  markType = "none";
453  } else if (c.fromLane == 0 && c.toLane == 0 && isOuterEdge) {
454  // solid road mark at the outer border
455  markType = "solid";
456  } else if (isOuterEdge && j > 0
457  && (outEdge->getPermissions(parallel[j - 1].toLane) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
458  // solid road mark to the left of sidewalk or bicycle lane
459  markType = "solid";
460  } else if (!inEdge->getToNode()->geometryLike()) {
461  // draw shorter road marks to indicate turning paths
462  LinkDirection dir = inEdge->getToNode()->getDirection(inEdge, outEdge, OptionsCont::getOptions().getBool("lefthand"));
464  // XXX <type><line/><type> is not rendered by odrViewer so cannot be validated
465  // device << " <type name=\"broken\" width=\"0.13\">\n";
466  // device << " <line length=\"0.5\" space=\"0.5\" tOffset=\"0\" sOffset=\"0\" rule=\"none\"/>\n";
467  // device << " </type>\n";
468  markType = "none";
469  }
470  }
471  device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
472  device << " <speed sOffset=\"0\" max=\"" << c.vmax << "\"/>\n";
473  device << " </lane>\n";
474 
475  junctionDevice << " <laneLink from=\"" << fromIndex << "\" to=\"" << toIndexInternal << "\"/>\n";
476  connectionID++;
477  }
478  device << " </right>\n";
479  device << " </laneSection>\n";
480  device << " </lanes>\n";
481  device << " <objects/>\n";
482  device << " <signals/>\n";
483  device.closeTag();
484  junctionDevice << " </connection>\n";
485 
486  return connectionID;
487 }
488 
489 
490 double
491 NWWriter_OpenDrive::writeGeomLines(const PositionVector& shape, OutputDevice& device, OutputDevice& elevationDevice, double offset) {
492  for (int j = 0; j < (int)shape.size() - 1; ++j) {
493  const Position& p = shape[j];
494  const Position& p2 = shape[j + 1];
495  const double hdg = shape.angleAt2D(j);
496  const double length = p.distanceTo2D(p2);
497  device.openTag("geometry");
498  device.writeAttr("s", offset);
499  device.writeAttr("x", p.x());
500  device.writeAttr("y", p.y());
501  device.writeAttr("hdg", hdg);
502  device.writeAttr("length", length);
503  device.openTag("line").closeTag();
504  device.closeTag();
505  elevationDevice << " <elevation s=\"" << offset << "\" a=\"" << p.z() << "\" b=\"" << (p2.z() - p.z()) / MAX2(POSITION_EPS, length) << "\" c=\"0\" d=\"0\"/>\n";
506  offset += length;
507  }
508  return offset;
509 }
510 
511 
512 void
513 NWWriter_OpenDrive::writeEmptyCenterLane(OutputDevice& device, const std::string& mark, double markWidth) {
514  device << " <center>\n";
515  device << " <lane id=\"0\" type=\"none\" level=\"true\">\n";
516  device << " <link/>\n";
517  device << " <roadMark sOffset=\"0\" type=\"" << mark << "\" weight=\"standard\" color=\"standard\" width=\"" << markWidth << "\"/>\n";
518  device << " </lane>\n";
519  device << " </center>\n";
520 }
521 
522 
523 int
524 NWWriter_OpenDrive::getID(const std::string& origID, StringBijection<int>& map, int& lastID) {
525  if (map.hasString(origID)) {
526  return map.get(origID);
527  }
528  map.insert(origID, lastID++);
529  return lastID - 1;
530 }
531 
532 
533 std::string
535  switch (permissions) {
536  case SVC_PEDESTRIAN:
537  return "sidewalk";
538  //case (SVC_BICYCLE | SVC_PEDESTRIAN):
539  // WRITE_WARNING("Ambiguous lane type (biking+driving) for road '" + roadID + "'");
540  // return "sidewalk";
541  case SVC_BICYCLE:
542  return "biking";
543  case 0:
544  // ambiguous
545  return "none";
546  case SVC_RAIL:
547  case SVC_RAIL_URBAN:
548  case SVC_RAIL_ELECTRIC:
549  case SVC_RAIL_FAST:
550  return "rail";
551  case SVC_TRAM:
552  return "tram";
553  default: {
554  // complex permissions
555  if (permissions == SVCAll) {
556  return "driving";
557  } else if (isRailway(permissions)) {
558  return "rail";
559  } else if ((permissions & SVC_PASSENGER) != 0) {
560  return "driving";
561  } else {
562  return "restricted";
563  }
564  }
565  }
566 }
567 
568 
570 NWWriter_OpenDrive::getLeftLaneBorder(const NBEdge* edge, int laneIndex, double widthOffset) {
571  const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
572  if (laneIndex == -1) {
573  // leftmost lane
574  laneIndex = lefthand ? 0 : (int)edge->getNumLanes() - 1;
575  }
577  // PositionVector result = edge->getLaneShape(laneIndex);
578  // (and the moveo2side)
579  // However, the lanes in SUMO have a small lateral gap (SUMO_const_laneOffset) to account for markings
580  // In OpenDRIVE this gap does not exists so we have to do all lateral
581  // computations based on the reference line
582  // This assumes that the 'stop line' for all lanes is colinear!
583  const int leftmost = lefthand ? 0 : (int)edge->getNumLanes() - 1;
584  widthOffset -= (edge->getLaneWidth(leftmost) / 2);
585  // collect lane widths from left border of edge to left border of lane to connect to
586  if (lefthand) {
587  for (int i = leftmost; i < laneIndex; i++) {
588  widthOffset += edge->getLaneWidth(i);
589  }
590  } else {
591  for (int i = leftmost; i > laneIndex; i--) {
592  widthOffset += edge->getLaneWidth(i);
593  }
594  }
595  PositionVector result = edge->getLaneShape(leftmost);
596  try {
597  result.move2side(widthOffset);
598  } catch (InvalidArgument&) { }
599  return result;
600 }
601 
603 NWWriter_OpenDrive::getRightLaneBorder(const NBEdge* edge, int laneIndex) {
604  return getLeftLaneBorder(edge, laneIndex, edge->getLaneWidth(laneIndex));
605 }
606 
607 
608 double
610  OutputDevice& device,
611  OutputDevice& elevationDevice,
612  PositionVector init,
613  double length,
614  double offset) {
615  assert(init.size() == 3 || init.size() == 4);
616 
617  // avoid division by 0
618  length = MAX2(POSITION_EPS, length);
619 
620  const Position p = init.front();
621  const double hdg = init.angleAt2D(0);
622 
623  // backup elevation values
624  const PositionVector initZ = init;
625  // translate to u,v coordinates
626  init.add(-p.x(), -p.y(), -p.z());
627  init.rotate2D(-hdg);
628 
629  // parametric coefficients
630  double aU, bU, cU, dU;
631  double aV, bV, cV, dV;
632  double aZ, bZ, cZ, dZ;
633 
634  // unfactor the Bernstein polynomials of degree 2 (or 3) and collect the coefficients
635  if (init.size() == 3) {
636  //f(x, a, b ,c) = a + (2*b - 2*a)*x + (a - 2*b + c)*x*x
637  aU = init[0].x();
638  bU = 2 * init[1].x() - 2 * init[0].x();
639  cU = init[0].x() - 2 * init[1].x() + init[2].x();
640  dU = 0;
641 
642  aV = init[0].y();
643  bV = 2 * init[1].y() - 2 * init[0].y();
644  cV = init[0].y() - 2 * init[1].y() + init[2].y();
645  dV = 0;
646 
647  // elevation is not parameteric on [0:1] but on [0:length]
648  aZ = initZ[0].z();
649  bZ = (2 * initZ[1].z() - 2 * initZ[0].z()) / length;
650  cZ = (initZ[0].z() - 2 * initZ[1].z() + initZ[2].z()) / (length * length);
651  dZ = 0;
652 
653  } else {
654  // 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))
655  aU = init[0].x();
656  bU = 3 * init[1].x() - 3 * init[0].x();
657  cU = 3 * init[0].x() - 6 * init[1].x() + 3 * init[2].x();
658  dU = -init[0].x() + 3 * init[1].x() - 3 * init[2].x() + init[3].x();
659 
660  aV = init[0].y();
661  bV = 3 * init[1].y() - 3 * init[0].y();
662  cV = 3 * init[0].y() - 6 * init[1].y() + 3 * init[2].y();
663  dV = -init[0].y() + 3 * init[1].y() - 3 * init[2].y() + init[3].y();
664 
665  // elevation is not parameteric on [0:1] but on [0:length]
666  aZ = initZ[0].z();
667  bZ = (3 * initZ[1].z() - 3 * initZ[0].z()) / length;
668  cZ = (3 * initZ[0].z() - 6 * initZ[1].z() + 3 * initZ[2].z()) / (length * length);
669  dZ = (-initZ[0].z() + 3 * initZ[1].z() - 3 * initZ[2].z() + initZ[3].z()) / (length * length * length);
670  }
671 
672  device.openTag("geometry");
673  device.writeAttr("s", offset);
674  device.writeAttr("x", p.x());
675  device.writeAttr("y", p.y());
676  device.writeAttr("hdg", hdg);
677  device.writeAttr("length", length);
678 
679  device.openTag("paramPoly3");
680  device.writeAttr("aU", aU);
681  device.writeAttr("bU", bU);
682  device.writeAttr("cU", cU);
683  device.writeAttr("dU", dU);
684  device.writeAttr("aV", aV);
685  device.writeAttr("bV", bV);
686  device.writeAttr("cV", cV);
687  device.writeAttr("dV", dV);
688  device.closeTag();
689  device.closeTag();
690 
691  // write elevation
692  elevationDevice.openTag("elevation");
693  elevationDevice.writeAttr("s", offset);
694  elevationDevice.writeAttr("a", aZ);
695  elevationDevice.writeAttr("b", bZ);
696  elevationDevice.writeAttr("c", cZ);
697  elevationDevice.writeAttr("d", dZ);
698  elevationDevice.closeTag();
699 
700  return offset + length;
701 }
702 
703 
704 bool
705 NWWriter_OpenDrive::writeGeomSmooth(const PositionVector& shape, double speed, OutputDevice& device, OutputDevice& elevationDevice, double straightThresh, double& length) {
706 #ifdef DEBUG_SMOOTH_GEOM
707  if (DEBUGCOND) {
708  std::cout << "writeGeomSmooth\n n=" << shape.size() << " shape=" << toString(shape) << "\n";
709  }
710 #endif
711  bool ok = true;
712  const double longThresh = speed; // 16.0; // make user-configurable (should match the sampling rate of the source data)
713  const double curveCutout = longThresh / 2; // 8.0; // make user-configurable (related to the maximum turning rate)
714  // 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')
715  assert(longThresh >= 2 * curveCutout);
716  assert(shape.size() > 2);
717  // add intermediate points wherever there is a strong angular change between long segments
718  // assume the geometry is simplified so as not to contain consecutive colinear points
719  PositionVector shape2 = shape;
720  double maxAngleDiff = 0;
721  double offset = 0;
722  for (int j = 1; j < (int)shape.size() - 1; ++j) {
723  //const double hdg = shape.angleAt2D(j);
724  const Position& p0 = shape[j - 1];
725  const Position& p1 = shape[j];
726  const Position& p2 = shape[j + 1];
727  const double dAngle = fabs(GeomHelper::angleDiff(p0.angleTo2D(p1), p1.angleTo2D(p2)));
728  const double length1 = p0.distanceTo2D(p1);
729  const double length2 = p1.distanceTo2D(p2);
730  maxAngleDiff = MAX2(maxAngleDiff, dAngle);
731 #ifdef DEBUG_SMOOTH_GEOM
732  if (DEBUGCOND) {
733  std::cout << " j=" << j << " dAngle=" << RAD2DEG(dAngle) << " length1=" << length1 << " length2=" << length2 << "\n";
734  }
735 #endif
736  if (dAngle > straightThresh
737  && (length1 > longThresh || j == 1)
738  && (length2 > longThresh || j == (int)shape.size() - 2)) {
739  shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 - MIN2(length1 - POSITION_EPS, curveCutout)), false);
740  shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 + MIN2(length2 - POSITION_EPS, curveCutout)), false);
741  shape2.removeClosest(p1);
742  }
743  offset += length1;
744  }
745  const int numPoints = (int)shape2.size();
746 #ifdef DEBUG_SMOOTH_GEOM
747  if (DEBUGCOND) {
748  std::cout << " n=" << numPoints << " shape2=" << toString(shape2) << "\n";
749  }
750 #endif
751 
752  if (maxAngleDiff < straightThresh) {
753  length = writeGeomLines(shape2, device, elevationDevice, 0);
754 #ifdef DEBUG_SMOOTH_GEOM
755  if (DEBUGCOND) {
756  std::cout << " special case: all lines. maxAngleDiff=" << maxAngleDiff << "\n";
757  }
758 #endif
759  return ok;
760  }
761 
762  // write the long segments as lines, short segments as curves
763  offset = 0;
764  for (int j = 0; j < numPoints - 1; ++j) {
765  const Position& p0 = shape2[j];
766  const Position& p1 = shape2[j + 1];
767  PositionVector line;
768  line.push_back(p0);
769  line.push_back(p1);
770  const double lineLength = line.length2D();
771  if (lineLength >= longThresh) {
772  offset = writeGeomLines(line, device, elevationDevice, offset);
773 #ifdef DEBUG_SMOOTH_GEOM
774  if (DEBUGCOND) {
775  std::cout << " writeLine=" << toString(line) << "\n";
776  }
777 #endif
778  } else {
779  // find control points
780  PositionVector begShape;
781  PositionVector endShape;
782  if (j == 0 || j == numPoints - 2) {
783  // keep the angle of the first/last segment but end at the front of the shape
784  begShape = line;
785  begShape.add(p0 - begShape.back());
786  } else if (j == 1 || p0.distanceTo2D(shape2[j - 1]) > longThresh) {
787  // use the previous segment if it is long or the first one
788  begShape.push_back(shape2[j - 1]);
789  begShape.push_back(p0);
790  } else {
791  // end at p0 with mean angle of the previous and current segment
792  begShape.push_back(shape2[j - 1]);
793  begShape.push_back(p1);
794  begShape.add(p0 - begShape.back());
795  }
796 
797  if (j == 0 || j == numPoints - 2) {
798  // keep the angle of the first/last segment but start at the end of the shape
799  endShape = line;
800  endShape.add(p1 - endShape.front());
801  } else if (j == numPoints - 3 || p1.distanceTo2D(shape2[j + 2]) > longThresh) {
802  // use the next segment if it is long or the final one
803  endShape.push_back(p1);
804  endShape.push_back(shape2[j + 2]);
805  } else {
806  // start at p1 with mean angle of the current and next segment
807  endShape.push_back(p0);
808  endShape.push_back(shape2[j + 2]);
809  endShape.add(p1 - endShape.front());
810  }
811  const double extrapolateLength = MIN2((double)25, lineLength / 4);
812  PositionVector init = NBNode::bezierControlPoints(begShape, endShape, false, extrapolateLength, extrapolateLength, ok, nullptr, straightThresh);
813  if (init.size() == 0) {
814  // could not compute control points, write line
815  offset = writeGeomLines(line, device, elevationDevice, offset);
816 #ifdef DEBUG_SMOOTH_GEOM
817  if (DEBUGCOND) {
818  std::cout << " writeLine lineLength=" << lineLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
819  }
820 #endif
821  } else {
822  // write bezier
823  const double curveLength = init.bezier(12).length2D();
824  offset = writeGeomPP3(device, elevationDevice, init, curveLength, offset);
825 #ifdef DEBUG_SMOOTH_GEOM
826  if (DEBUGCOND) {
827  std::cout << " writeCurve lineLength=" << lineLength << " curveLength=" << curveLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
828  }
829 #endif
830  }
831  }
832  }
833  length = offset;
834  return ok;
835 }
836 
837 
838 void
840  // check if the shape is flat
841  bool flat = true;
842  double z = shape.size() == 0 ? 0 : shape[0].z();
843  for (int i = 1; i < (int)shape.size(); ++i) {
844  if (fabs(shape[i].z() - z) > NUMERICAL_EPS) {
845  flat = false;
846  break;
847  }
848  }
849  device << " <elevationProfile>\n";
850  if (flat) {
851  device << " <elevation s=\"0\" a=\"" << z << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
852  } else {
853  device << elevationDevice.getString();
854  }
855  device << " </elevationProfile>\n";
856 
857 }
858 
859 
860 void
862  if (e->getNumLanes() > 1) {
863  // compute 'stop line' of rightmost lane
864  const PositionVector shape0 = e->getLaneShape(0);
865  assert(shape0.size() >= 2);
866  const Position& from = shape0[-2];
867  const Position& to = shape0[-1];
868  PositionVector stopLine;
869  stopLine.push_back(to);
870  stopLine.push_back(to - PositionVector::sideOffset(from, to, -1000.0));
871  // endpoints of all other lanes should be on the stop line
872  for (int lane = 1; lane < e->getNumLanes(); ++lane) {
873  const double dist = stopLine.distance2D(e->getLaneShape(lane)[-1]);
874  if (dist > NUMERICAL_EPS) {
875  WRITE_WARNING("Uneven stop line at lane '" + e->getLaneID(lane) + "' (dist=" + toString(dist) + ") cannot be represented in OpenDRIVE.");
876  }
877  }
878  }
879 }
880 
881 void
883  if (e->knowsParameter("roadObjects")) {
884  device.openTag("objects");
885  device.setPrecision(8); // geometry hdg requires higher precision
887  for (std::string id : StringTokenizer(e->getParameter("roadObjects", "")).getVector()) {
888  SUMOPolygon* p = shc.getPolygons().get(id);
889  if (p == nullptr) {
890  WRITE_WARNING("Road object polygon '" + id + "' not found for edge '" + e->getID() + "'");
891  } else if (p->getShape().size() != 4) {
892  WRITE_WARNING("Cannot convert road object polygon '" + id + "' with " + toString(p->getShape().size()) + " points for edge '" + e->getID() + "'");
893  } else {
894  const PositionVector& shape = p->getShape();
895  device.openTag("object");
896  Position center = shape.getPolygonCenter();
897  PositionVector sideline = shape.getSubpartByIndex(0, 2);
898  PositionVector ortholine = shape.getSubpartByIndex(1, 2);
899  const double absAngle = sideline.angleAt2D(0);
900  const double length = sideline.length2D();
901  const double width = ortholine.length2D();
902  const double edgeOffset = road.nearest_offset_to_point2D(center);
903  if (edgeOffset == GeomHelper::INVALID_OFFSET) {
904  WRITE_WARNING("Cannot map road object polygon '" + id + "' with center " + toString(center) + " onto edge '" + e->getID() + "'");
905  continue;
906  }
907  Position edgePos = road.positionAtOffset2D(edgeOffset);
908  const double edgeAngle = road.rotationAtOffset(edgeOffset);
909  const double relAngle = absAngle - edgeAngle;
910  double sideOffset = center.distanceTo2D(edgePos);
911  // determine sign of sideOffset
912  PositionVector tmp = road.getSubpart2D(MAX2(0.0, edgeOffset - 1), MIN2(road.length2D(), edgeOffset + 1));
913  tmp.move2side(sideOffset);
914  if (tmp.distance2D(center) < sideOffset) {
915  sideOffset *= -1;
916  }
917  //std::cout << " id=" << id
918  // << " shape=" << shape
919  // << " center=" << center
920  // << " edgeOffset=" << edgeOffset
921  // << "\n";
922  device.writeAttr("id", id);
923  device.writeAttr("type", p->getShapeType());
924  device.writeAttr("name", p->getParameter("name", ""));
925  device.writeAttr("s", edgeOffset);
926  device.writeAttr("t", sideOffset);
927  device.writeAttr("width", width);
928  device.writeAttr("length", length);
929  device.writeAttr("hdg", relAngle);
930  device.closeTag();
931  }
932  }
933  device.setPrecision(gPrecision);
934  device.closeTag();
935  } else {
936  device << " <objects/>\n";
937  }
938 }
939 
940 
941 /****************************************************************************/
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define RAD2DEG(x)
Definition: GeomHelper.h:36
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:280
#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.
@ PARTRIGHT
The link is a partial right direction.
int gPrecision
the precision for floating point outputs
Definition: StdDefs.cpp:25
T MIN2(T a, T b)
Definition: StdDefs.h:74
T MAX2(T a, T b)
Definition: StdDefs.h:80
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:129
double xmin() const
Returns minimum x-coordinate.
Definition: Boundary.cpp:117
double ymax() const
Returns maximum y-coordinate.
Definition: Boundary.cpp:135
double xmax() const
Returns maximum x-coordinate.
Definition: Boundary.cpp:123
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: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:183
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition: NBEdgeCont.h:191
The representation of a single edge during network building.
Definition: NBEdge.h:91
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:4022
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:630
const std::string & getStreetName() const
Returns the street name of this edge.
Definition: NBEdge.h:643
const std::string & getID() const
Definition: NBEdge.h:1465
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:701
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:541
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:614
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:3356
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:515
std::string getLaneID(int lane) const
get lane ID
Definition: NBEdge.cpp:3693
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:1006
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:930
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1368
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:534
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1317
Instance responsible for building networks.
Definition: NBNetBuilder.h:107
ShapeContainer & getShapeCont()
Definition: NBNetBuilder.h:188
NBEdgeCont & getEdgeCont()
Definition: NBNetBuilder.h:148
NBNodeCont & getNodeCont()
Returns a reference to the node container.
Definition: NBNetBuilder.h:153
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:58
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:288
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:2221
int numNormalConnections() const
return the number of lane-to-lane connections at this junction (excluding crossings)
Definition: NBNode.cpp:3465
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:256
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3343
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
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)
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)
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 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)
write internal edge to device, return next connectionID
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 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:58
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:305
void lf()
writes a line feed if applicable
Definition: OutputDevice.h:236
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:248
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)
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:76
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:197
int fromLane
The lane the connections starts at.
Definition: NBEdge.h:222
int toLane
The lane the connections yields in.
Definition: NBEdge.h:228
NBEdge * toEdge
The edge the connections yields in.
Definition: NBEdge.h:225
double vmax
maximum velocity
Definition: NBEdge.h:282
std::string id
id of Connection
Definition: NBEdge.h:276
std::string type
the type of this lane
Definition: NBEdge.h:188