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-2020 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  device << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
72  device.openTag("OpenDRIVE");
73  time_t now = time(nullptr);
74  std::string dstr(ctime(&now));
76  // write header
77  device.openTag("header");
78  device.writeAttr("revMajor", "1");
79  device.writeAttr("revMinor", "4");
80  device.writeAttr("name", "");
81  device.writeAttr("version", "1.00");
82  device.writeAttr("date", dstr.substr(0, dstr.length() - 1));
83  device.writeAttr("north", b.ymax());
84  device.writeAttr("south", b.ymin());
85  device.writeAttr("east", b.xmax());
86  device.writeAttr("west", b.xmin());
87  /* @note obsolete in 1.4
88  device.writeAttr("maxRoad", ec.size());
89  device.writeAttr("maxJunc", nc.size());
90  device.writeAttr("maxPrg", 0);
91  */
92  device.closeTag();
93  // write optional geo reference
95  if (gch.usingGeoProjection()) {
96  if (gch.getOffsetBase() == Position(0, 0)) {
97  device.openTag("geoReference");
98  device.writePreformattedTag(" <![CDATA[\n "
99  + gch.getProjString()
100  + "\n]]>\n");
101  device.closeTag();
102  } else {
103  WRITE_WARNING("Could not write OpenDRIVE geoReference. Only unshifted Coordinate systems are supported (offset=" + toString(gch.getOffsetBase()) + ")");
104  }
105  }
106 
107  // write normal edges (road)
108  for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
109  const NBEdge* e = (*i).second;
110  const int fromNodeID = e->getIncomingEdges().size() > 0 ? getID(e->getFromNode()->getID(), nodeMap, nodeID) : INVALID_ID;
111  const int toNodeID = e->getConnections().size() > 0 ? getID(e->getToNode()->getID(), nodeMap, nodeID) : INVALID_ID;
112  writeNormalEdge(device, e,
113  getID(e->getID(), edgeMap, edgeID),
114  fromNodeID, toNodeID,
115  origNames, straightThresh,
116  nb.getShapeCont());
117  }
118  device.lf();
119 
120  // write junction-internal edges (road). In OpenDRIVE these are called 'paths' or 'connecting roads'
121  OutputDevice_String junctionOSS(3);
122  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
123  NBNode* n = (*i).second;
124  int connectionID = 0; // unique within a junction
125  const int nID = getID(n->getID(), nodeMap, nodeID);
126  if (n->numNormalConnections() > 0) {
127  junctionOSS << " <junction name=\"" << n->getID() << "\" id=\"" << nID << "\">\n";
128  }
129  std::vector<NBEdge*> incoming = (*i).second->getIncomingEdges();
130  if (lefthand) {
131  std::reverse(incoming.begin(), incoming.end());
132  }
133  for (NBEdge* inEdge : incoming) {
134  std::string centerMark = "none";
135  const int inEdgeID = getID(inEdge->getID(), edgeMap, edgeID);
136  // group parallel edges
137  const NBEdge* outEdge = nullptr;
138  bool isOuterEdge = true; // determine where a solid outer border should be drawn
139  int lastFromLane = -1;
140  std::vector<NBEdge::Connection> parallel;
141  std::vector<NBEdge::Connection> connections = inEdge->getConnections();
142  if (lefthand) {
143  std::reverse(connections.begin(), connections.end());
144  }
145  for (const NBEdge::Connection& c : connections) {
146  assert(c.toEdge != 0);
147  if (outEdge != c.toEdge || c.fromLane == lastFromLane) {
148  if (outEdge != nullptr) {
149  if (isOuterEdge) {
150  addPedestrianConnection(inEdge, outEdge, parallel);
151  }
152  connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
153  getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
154  inEdgeID,
155  getID(outEdge->getID(), edgeMap, edgeID),
156  connectionID,
157  parallel, isOuterEdge, straightThresh, centerMark);
158  parallel.clear();
159  isOuterEdge = false;
160  }
161  outEdge = c.toEdge;
162  }
163  lastFromLane = c.fromLane;
164  parallel.push_back(c);
165  }
166  if (isOuterEdge) {
167  addPedestrianConnection(inEdge, outEdge, parallel);
168  }
169  if (!parallel.empty()) {
170  if (!lefthand && (n->geometryLike() || inEdge->isTurningDirectionAt(outEdge))) {
171  centerMark = "solid";
172  }
173  connectionID = writeInternalEdge(device, junctionOSS, inEdge, nID,
174  getID(parallel.back().getInternalLaneID(), edgeMap, edgeID),
175  inEdgeID,
176  getID(outEdge->getID(), edgeMap, edgeID),
177  connectionID,
178  parallel, isOuterEdge, straightThresh, centerMark);
179  parallel.clear();
180  }
181  }
182  if (n->numNormalConnections() > 0) {
183  junctionOSS << " </junction>\n";
184  }
185  }
186  device.lf();
187  // write junctions (junction)
188  device << junctionOSS.getString();
189 
190  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
191  NBNode* n = (*i).second;
192  const std::vector<NBEdge*>& incoming = n->getIncomingEdges();
193  // check if any connections must be written
194  int numConnections = 0;
195  for (std::vector<NBEdge*>::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
196  numConnections += (int)((*j)->getConnections().size());
197  }
198  if (numConnections == 0) {
199  continue;
200  }
201  for (std::vector<NBEdge*>::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
202  const NBEdge* inEdge = *j;
203  const std::vector<NBEdge::Connection>& elv = inEdge->getConnections();
204  for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
205  const NBEdge::Connection& c = *k;
206  const NBEdge* outEdge = c.toEdge;
207  if (outEdge == nullptr) {
208  continue;
209  }
210  }
211  }
212  }
213 
214  device.closeTag();
215  device.close();
216 }
217 
218 
219 void
221  int edgeID, int fromNodeID, int toNodeID,
222  const bool origNames,
223  const double straightThresh,
224  const ShapeContainer& shc) {
225  // buffer output because some fields are computed out of order
226  OutputDevice_String elevationOSS(3);
227  elevationOSS.setPrecision(8);
228  OutputDevice_String planViewOSS(2);
229  planViewOSS.setPrecision(8);
230  double length = 0;
231 
232  planViewOSS.openTag("planView");
233  // for the shape we need to use the leftmost border of the leftmost lane
234  const std::vector<NBEdge::Lane>& lanes = e->getLanes();
236 #ifdef DEBUG_SMOOTH_GEOM
237  if (DEBUGCOND) {
238  std::cout << "write planview for edge " << e->getID() << "\n";
239  }
240 #endif
241 
242  if (ls.size() == 2 || e->getPermissions() == SVC_PEDESTRIAN) {
243  // foot paths may contain sharp angles
244  length = writeGeomLines(ls, planViewOSS, elevationOSS);
245  } else {
246  bool ok = writeGeomSmooth(ls, e->getSpeed(), planViewOSS, elevationOSS, straightThresh, length);
247  if (!ok) {
248  WRITE_WARNING("Could not compute smooth shape for edge '" + e->getID() + "'.");
249  }
250  }
251  planViewOSS.closeTag();
252 
253  device.openTag("road");
254  device.writeAttr("name", StringUtils::escapeXML(e->getStreetName()));
255  device.setPrecision(8); // length requires higher precision
256  device.writeAttr("length", MAX2(POSITION_EPS, length));
257  device.setPrecision(gPrecision);
258  device.writeAttr("id", edgeID);
259  device.writeAttr("junction", -1);
260  if (fromNodeID != INVALID_ID || toNodeID != INVALID_ID) {
261  device.openTag("link");
262  if (fromNodeID != INVALID_ID) {
263  device.openTag("predecessor");
264  device.writeAttr("elementType", "junction");
265  device.writeAttr("elementId", fromNodeID);
266  device.closeTag();
267  }
268  if (toNodeID != INVALID_ID) {
269  device.openTag("successor");
270  device.writeAttr("elementType", "junction");
271  device.writeAttr("elementId", toNodeID);
272  device.closeTag();
273  }
274  device.closeTag();
275  }
276  device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
277  device << planViewOSS.getString();
278  writeElevationProfile(ls, device, elevationOSS);
279  device << " <lateralProfile/>\n";
280  device << " <lanes>\n";
281  device << " <laneSection s=\"0\">\n";
282  const std::string centerMark = e->getPermissions(e->getNumLanes() - 1) == 0 ? "none" : "solid";
283  writeEmptyCenterLane(device, centerMark, 0.13);
284  device << " <right>\n";
285  for (int j = e->getNumLanes(); --j >= 0;) {
286  device << " <lane id=\"-" << e->getNumLanes() - j << "\" type=\"" << getLaneType(e->getPermissions(j)) << "\" level=\"true\">\n";
287  device << " <link/>\n";
288  // this could be used for geometry-link junctions without u-turn,
289  // predecessor and sucessors would be lane indices,
290  // road predecessor / succesfors would be of type 'road' rather than
291  // 'junction'
292  //device << " <predecessor id=\"-1\"/>\n";
293  //device << " <successor id=\"-1\"/>\n";
294  //device << " </link>\n";
295  device << " <width sOffset=\"0\" a=\"" << e->getLaneWidth(j) << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
296  std::string markType = "broken";
297  if (j == 0) {
298  markType = "solid";
299  } else if (j > 0
300  && (e->getPermissions(j - 1) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
301  // solid road mark to the left of sidewalk or bicycle lane
302  markType = "solid";
303  } else if (e->getPermissions(j) == 0) {
304  // solid road mark to the right of a forbidden lane
305  markType = "solid";
306  }
307  device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
308  device << " <speed sOffset=\"0\" max=\"" << lanes[j].speed << "\"/>\n";
309  device << " </lane>\n";
310  }
311  device << " </right>\n";
312  device << " </laneSection>\n";
313  device << " </lanes>\n";
314  writeRoadObjects(device, e, shc);
315  device << " <signals/>\n";
316  if (origNames) {
317  device << " <userData code=\"sumoId\" value=\"" << e->getID() << "\"/>\n";
318  }
319  device.closeTag();
321 }
322 
323 void
324 NWWriter_OpenDrive::addPedestrianConnection(const NBEdge* inEdge, const NBEdge* outEdge, std::vector<NBEdge::Connection>& parallel) {
325  // by default there are no internal lanes for pedestrians. Determine if
326  // one is feasible and does not exist yet.
327  if (outEdge != nullptr
328  && inEdge->getPermissions(0) == SVC_PEDESTRIAN
329  && outEdge->getPermissions(0) == SVC_PEDESTRIAN
330  && (parallel.empty()
331  || parallel.front().fromLane != 0
332  || parallel.front().toLane != 0)) {
333  parallel.insert(parallel.begin(), NBEdge::Connection(0, const_cast<NBEdge*>(outEdge), 0, false));
334  parallel.front().vmax = (inEdge->getLanes()[0].speed + outEdge->getLanes()[0].speed) / (double) 2.0;
335  }
336 }
337 
338 
339 int
340 NWWriter_OpenDrive::writeInternalEdge(OutputDevice& device, OutputDevice& junctionDevice, const NBEdge* inEdge, int nodeID,
341  int edgeID, int inEdgeID, int outEdgeID,
342  int connectionID,
343  const std::vector<NBEdge::Connection>& parallel,
344  const bool isOuterEdge,
345  const double straightThresh,
346  const std::string& centerMark) {
347  assert(parallel.size() != 0);
348  const NBEdge::Connection& cLeft = parallel.back();
349  const NBEdge* outEdge = cLeft.toEdge;
350  PositionVector begShape = getLeftLaneBorder(inEdge, cLeft.fromLane);
351  PositionVector endShape = getLeftLaneBorder(outEdge, cLeft.toLane);
352  //std::cout << "computing reference line for internal lane " << cLeft.getInternalLaneID() << " begLane=" << inEdge->getLaneShape(cLeft.fromLane) << " endLane=" << outEdge->getLaneShape(cLeft.toLane) << "\n";
353 
354  double length;
355  double laneOffset = 0;
356  PositionVector fallBackShape;
357  fallBackShape.push_back(begShape.back());
358  fallBackShape.push_back(endShape.front());
359  const bool turnaround = inEdge->isTurningDirectionAt(outEdge);
360  bool ok = true;
361  PositionVector init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
362  if (init.size() == 0) {
363  length = fallBackShape.length2D();
364  // problem with turnarounds is known, method currently returns 'ok' (#2539)
365  if (!ok) {
366  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.");
367  } else if (length <= NUMERICAL_EPS) {
368  // left-curving geometry-like edges must use the right
369  // side as reference line and shift
370  begShape = getRightLaneBorder(inEdge, cLeft.fromLane);
371  endShape = getRightLaneBorder(outEdge, cLeft.toLane);
372  init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok, nullptr, straightThresh);
373  if (init.size() != 0) {
374  length = init.bezier(12).length2D();
375  laneOffset = outEdge->getLaneWidth(cLeft.toLane);
376  //std::cout << " internalLane=" << cLeft.getInternalLaneID() << " length=" << length << "\n";
377  }
378  }
379  } else {
380  length = init.bezier(12).length2D();
381  }
382 
383  junctionDevice << " <connection id=\"" << connectionID << "\" incomingRoad=\"" << inEdgeID << "\" connectingRoad=\"" << edgeID << "\" contactPoint=\"start\">\n";
384  device.openTag("road");
385  device.writeAttr("name", cLeft.id);
386  device.setPrecision(8); // length requires higher precision
387  device.writeAttr("length", MAX2(POSITION_EPS, length));
388  device.setPrecision(gPrecision);
389  device.writeAttr("id", edgeID);
390  device.writeAttr("junction", nodeID);
391  device.openTag("link");
392  device.openTag("predecessor");
393  device.writeAttr("elementType", "road");
394  device.writeAttr("elementId", inEdgeID);
395  device.writeAttr("contactPoint", "end");
396  device.closeTag();
397  device.openTag("successor");
398  device.writeAttr("elementType", "road");
399  device.writeAttr("elementId", outEdgeID);
400  device.writeAttr("contactPoint", "start");
401  device.closeTag();
402  device.closeTag();
403  device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
404  device.openTag("planView");
405  device.setPrecision(8); // geometry hdg requires higher precision
406  OutputDevice_String elevationOSS(3);
407  elevationOSS.setPrecision(8);
408 #ifdef DEBUG_SMOOTH_GEOM
409  if (DEBUGCOND) {
410  std::cout << "write planview for internal edge " << cLeft.id << " init=" << init << " fallback=" << fallBackShape
411  << " begShape=" << begShape << " endShape=" << endShape
412  << "\n";
413  }
414 #endif
415  if (init.size() == 0) {
416  writeGeomLines(fallBackShape, device, elevationOSS);
417  } else {
418  writeGeomPP3(device, elevationOSS, init, length);
419  }
420  device.setPrecision(gPrecision);
421  device.closeTag();
422  writeElevationProfile(fallBackShape, device, elevationOSS);
423  device << " <lateralProfile/>\n";
424  device << " <lanes>\n";
425  if (laneOffset != 0) {
426  device << " <laneOffset s=\"0\" a=\"" << laneOffset << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
427  }
428  device << " <laneSection s=\"0\">\n";
429  writeEmptyCenterLane(device, centerMark, 0);
430  device << " <right>\n";
431  for (int j = (int)parallel.size(); --j >= 0;) {
432  const NBEdge::Connection& c = parallel[j];
433  const int fromIndex = c.fromLane - inEdge->getNumLanes();
434  const int toIndex = c.toLane - outEdge->getNumLanes();
435  device << " <lane id=\"-" << parallel.size() - j << "\" type=\"" << getLaneType(outEdge->getPermissions(c.toLane)) << "\" level=\"true\">\n";
436  device << " <link>\n";
437  device << " <predecessor id=\"" << fromIndex << "\"/>\n";
438  device << " <successor id=\"" << toIndex << "\"/>\n";
439  device << " </link>\n";
440  device << " <width sOffset=\"0\" a=\"" << outEdge->getLaneWidth(c.toLane) << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
441  std::string markType = "broken";
442  if (inEdge->isTurningDirectionAt(outEdge)) {
443  markType = "none";
444  } else if (c.fromLane == 0 && c.toLane == 0 && isOuterEdge) {
445  // solid road mark at the outer border
446  markType = "solid";
447  } else if (isOuterEdge && j > 0
448  && (outEdge->getPermissions(parallel[j - 1].toLane) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
449  // solid road mark to the left of sidewalk or bicycle lane
450  markType = "solid";
451  } else if (!inEdge->getToNode()->geometryLike()) {
452  // draw shorter road marks to indicate turning paths
453  LinkDirection dir = inEdge->getToNode()->getDirection(inEdge, outEdge, OptionsCont::getOptions().getBool("lefthand"));
455  // XXX <type><line/><type> is not rendered by odrViewer so cannot be validated
456  // device << " <type name=\"broken\" width=\"0.13\">\n";
457  // device << " <line length=\"0.5\" space=\"0.5\" tOffset=\"0\" sOffset=\"0\" rule=\"none\"/>\n";
458  // device << " </type>\n";
459  markType = "none";
460  }
461  }
462  device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
463  device << " <speed sOffset=\"0\" max=\"" << c.vmax << "\"/>\n";
464  device << " </lane>\n";
465 
466  junctionDevice << " <laneLink from=\"" << fromIndex << "\" to=\"" << toIndex << "\"/>\n";
467  connectionID++;
468  }
469  device << " </right>\n";
470  device << " </laneSection>\n";
471  device << " </lanes>\n";
472  device << " <objects/>\n";
473  device << " <signals/>\n";
474  device.closeTag();
475  junctionDevice << " </connection>\n";
476 
477  return connectionID;
478 }
479 
480 
481 double
482 NWWriter_OpenDrive::writeGeomLines(const PositionVector& shape, OutputDevice& device, OutputDevice& elevationDevice, double offset) {
483  for (int j = 0; j < (int)shape.size() - 1; ++j) {
484  const Position& p = shape[j];
485  const Position& p2 = shape[j + 1];
486  const double hdg = shape.angleAt2D(j);
487  const double length = p.distanceTo2D(p2);
488  device.openTag("geometry");
489  device.writeAttr("s", offset);
490  device.writeAttr("x", p.x());
491  device.writeAttr("y", p.y());
492  device.writeAttr("hdg", hdg);
493  device.writeAttr("length", length);
494  device.openTag("line").closeTag();
495  device.closeTag();
496  elevationDevice << " <elevation s=\"" << offset << "\" a=\"" << p.z() << "\" b=\"" << (p2.z() - p.z()) / MAX2(POSITION_EPS, length) << "\" c=\"0\" d=\"0\"/>\n";
497  offset += length;
498  }
499  return offset;
500 }
501 
502 
503 void
504 NWWriter_OpenDrive::writeEmptyCenterLane(OutputDevice& device, const std::string& mark, double markWidth) {
505  device << " <center>\n";
506  device << " <lane id=\"0\" type=\"none\" level=\"true\">\n";
507  device << " <link/>\n";
508  device << " <roadMark sOffset=\"0\" type=\"" << mark << "\" weight=\"standard\" color=\"standard\" width=\"" << markWidth << "\"/>\n";
509  device << " </lane>\n";
510  device << " </center>\n";
511 }
512 
513 
514 int
515 NWWriter_OpenDrive::getID(const std::string& origID, StringBijection<int>& map, int& lastID) {
516  if (map.hasString(origID)) {
517  return map.get(origID);
518  }
519  map.insert(origID, lastID++);
520  return lastID - 1;
521 }
522 
523 
524 std::string
526  switch (permissions) {
527  case SVC_PEDESTRIAN:
528  return "sidewalk";
529  //case (SVC_BICYCLE | SVC_PEDESTRIAN):
530  // WRITE_WARNING("Ambiguous lane type (biking+driving) for road '" + roadID + "'");
531  // return "sidewalk";
532  case SVC_BICYCLE:
533  return "biking";
534  case 0:
535  // ambiguous
536  return "none";
537  case SVC_RAIL:
538  case SVC_RAIL_URBAN:
539  case SVC_RAIL_ELECTRIC:
540  case SVC_RAIL_FAST:
541  return "rail";
542  case SVC_TRAM:
543  return "tram";
544  default: {
545  // complex permissions
546  if (permissions == SVCAll) {
547  return "driving";
548  } else if (isRailway(permissions)) {
549  return "rail";
550  } else if ((permissions & SVC_PASSENGER) != 0) {
551  return "driving";
552  } else {
553  return "restricted";
554  }
555  }
556  }
557 }
558 
559 
561 NWWriter_OpenDrive::getLeftLaneBorder(const NBEdge* edge, int laneIndex, double widthOffset) {
562  const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
563  if (laneIndex == -1) {
564  // leftmost lane
565  laneIndex = lefthand ? 0 : (int)edge->getNumLanes() - 1;
566  }
568  // PositionVector result = edge->getLaneShape(laneIndex);
569  // (and the moveo2side)
570  // However, the lanes in SUMO have a small lateral gap (SUMO_const_laneOffset) to account for markings
571  // In OpenDRIVE this gap does not exists so we have to do all lateral
572  // computations based on the reference line
573  // This assumes that the 'stop line' for all lanes is colinear!
574  const int leftmost = lefthand ? 0 : (int)edge->getNumLanes() - 1;
575  widthOffset -= (edge->getLaneWidth(leftmost) / 2);
576  // collect lane widths from left border of edge to left border of lane to connect to
577  if (lefthand) {
578  for (int i = leftmost; i < laneIndex; i++) {
579  widthOffset += edge->getLaneWidth(i);
580  }
581  } else {
582  for (int i = leftmost; i > laneIndex; i--) {
583  widthOffset += edge->getLaneWidth(i);
584  }
585  }
586  PositionVector result = edge->getLaneShape(leftmost);
587  try {
588  result.move2side(widthOffset);
589  } catch (InvalidArgument&) { }
590  return result;
591 }
592 
594 NWWriter_OpenDrive::getRightLaneBorder(const NBEdge* edge, int laneIndex) {
595  return getLeftLaneBorder(edge, laneIndex, edge->getLaneWidth(laneIndex));
596 }
597 
598 
599 double
601  OutputDevice& device,
602  OutputDevice& elevationDevice,
603  PositionVector init,
604  double length,
605  double offset) {
606  assert(init.size() == 3 || init.size() == 4);
607 
608  // avoid division by 0
609  length = MAX2(POSITION_EPS, length);
610 
611  const Position p = init.front();
612  const double hdg = init.angleAt2D(0);
613 
614  // backup elevation values
615  const PositionVector initZ = init;
616  // translate to u,v coordinates
617  init.add(-p.x(), -p.y(), -p.z());
618  init.rotate2D(-hdg);
619 
620  // parametric coefficients
621  double aU, bU, cU, dU;
622  double aV, bV, cV, dV;
623  double aZ, bZ, cZ, dZ;
624 
625  // unfactor the Bernstein polynomials of degree 2 (or 3) and collect the coefficients
626  if (init.size() == 3) {
627  //f(x, a, b ,c) = a + (2*b - 2*a)*x + (a - 2*b + c)*x*x
628  aU = init[0].x();
629  bU = 2 * init[1].x() - 2 * init[0].x();
630  cU = init[0].x() - 2 * init[1].x() + init[2].x();
631  dU = 0;
632 
633  aV = init[0].y();
634  bV = 2 * init[1].y() - 2 * init[0].y();
635  cV = init[0].y() - 2 * init[1].y() + init[2].y();
636  dV = 0;
637 
638  // elevation is not parameteric on [0:1] but on [0:length]
639  aZ = initZ[0].z();
640  bZ = (2 * initZ[1].z() - 2 * initZ[0].z()) / length;
641  cZ = (initZ[0].z() - 2 * initZ[1].z() + initZ[2].z()) / (length * length);
642  dZ = 0;
643 
644  } else {
645  // 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))
646  aU = init[0].x();
647  bU = 3 * init[1].x() - 3 * init[0].x();
648  cU = 3 * init[0].x() - 6 * init[1].x() + 3 * init[2].x();
649  dU = -init[0].x() + 3 * init[1].x() - 3 * init[2].x() + init[3].x();
650 
651  aV = init[0].y();
652  bV = 3 * init[1].y() - 3 * init[0].y();
653  cV = 3 * init[0].y() - 6 * init[1].y() + 3 * init[2].y();
654  dV = -init[0].y() + 3 * init[1].y() - 3 * init[2].y() + init[3].y();
655 
656  // elevation is not parameteric on [0:1] but on [0:length]
657  aZ = initZ[0].z();
658  bZ = (3 * initZ[1].z() - 3 * initZ[0].z()) / length;
659  cZ = (3 * initZ[0].z() - 6 * initZ[1].z() + 3 * initZ[2].z()) / (length * length);
660  dZ = (-initZ[0].z() + 3 * initZ[1].z() - 3 * initZ[2].z() + initZ[3].z()) / (length * length * length);
661  }
662 
663  device.openTag("geometry");
664  device.writeAttr("s", offset);
665  device.writeAttr("x", p.x());
666  device.writeAttr("y", p.y());
667  device.writeAttr("hdg", hdg);
668  device.writeAttr("length", length);
669 
670  device.openTag("paramPoly3");
671  device.writeAttr("aU", aU);
672  device.writeAttr("bU", bU);
673  device.writeAttr("cU", cU);
674  device.writeAttr("dU", dU);
675  device.writeAttr("aV", aV);
676  device.writeAttr("bV", bV);
677  device.writeAttr("cV", cV);
678  device.writeAttr("dV", dV);
679  device.closeTag();
680  device.closeTag();
681 
682  // write elevation
683  elevationDevice.openTag("elevation");
684  elevationDevice.writeAttr("s", offset);
685  elevationDevice.writeAttr("a", aZ);
686  elevationDevice.writeAttr("b", bZ);
687  elevationDevice.writeAttr("c", cZ);
688  elevationDevice.writeAttr("d", dZ);
689  elevationDevice.closeTag();
690 
691  return offset + length;
692 }
693 
694 
695 bool
696 NWWriter_OpenDrive::writeGeomSmooth(const PositionVector& shape, double speed, OutputDevice& device, OutputDevice& elevationDevice, double straightThresh, double& length) {
697 #ifdef DEBUG_SMOOTH_GEOM
698  if (DEBUGCOND) {
699  std::cout << "writeGeomSmooth\n n=" << shape.size() << " shape=" << toString(shape) << "\n";
700  }
701 #endif
702  bool ok = true;
703  const double longThresh = speed; // 16.0; // make user-configurable (should match the sampling rate of the source data)
704  const double curveCutout = longThresh / 2; // 8.0; // make user-configurable (related to the maximum turning rate)
705  // 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')
706  assert(longThresh >= 2 * curveCutout);
707  assert(shape.size() > 2);
708  // add intermediate points wherever there is a strong angular change between long segments
709  // assume the geometry is simplified so as not to contain consecutive colinear points
710  PositionVector shape2 = shape;
711  double maxAngleDiff = 0;
712  double offset = 0;
713  for (int j = 1; j < (int)shape.size() - 1; ++j) {
714  //const double hdg = shape.angleAt2D(j);
715  const Position& p0 = shape[j - 1];
716  const Position& p1 = shape[j];
717  const Position& p2 = shape[j + 1];
718  const double dAngle = fabs(GeomHelper::angleDiff(p0.angleTo2D(p1), p1.angleTo2D(p2)));
719  const double length1 = p0.distanceTo2D(p1);
720  const double length2 = p1.distanceTo2D(p2);
721  maxAngleDiff = MAX2(maxAngleDiff, dAngle);
722 #ifdef DEBUG_SMOOTH_GEOM
723  if (DEBUGCOND) {
724  std::cout << " j=" << j << " dAngle=" << RAD2DEG(dAngle) << " length1=" << length1 << " length2=" << length2 << "\n";
725  }
726 #endif
727  if (dAngle > straightThresh
728  && (length1 > longThresh || j == 1)
729  && (length2 > longThresh || j == (int)shape.size() - 2)) {
730  shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 - MIN2(length1 - POSITION_EPS, curveCutout)), false);
731  shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 + MIN2(length2 - POSITION_EPS, curveCutout)), false);
732  shape2.removeClosest(p1);
733  }
734  offset += length1;
735  }
736  const int numPoints = (int)shape2.size();
737 #ifdef DEBUG_SMOOTH_GEOM
738  if (DEBUGCOND) {
739  std::cout << " n=" << numPoints << " shape2=" << toString(shape2) << "\n";
740  }
741 #endif
742 
743  if (maxAngleDiff < straightThresh) {
744  length = writeGeomLines(shape2, device, elevationDevice, 0);
745 #ifdef DEBUG_SMOOTH_GEOM
746  if (DEBUGCOND) {
747  std::cout << " special case: all lines. maxAngleDiff=" << maxAngleDiff << "\n";
748  }
749 #endif
750  return ok;
751  }
752 
753  // write the long segments as lines, short segments as curves
754  offset = 0;
755  for (int j = 0; j < numPoints - 1; ++j) {
756  const Position& p0 = shape2[j];
757  const Position& p1 = shape2[j + 1];
758  PositionVector line;
759  line.push_back(p0);
760  line.push_back(p1);
761  const double lineLength = line.length2D();
762  if (lineLength >= longThresh) {
763  offset = writeGeomLines(line, device, elevationDevice, offset);
764 #ifdef DEBUG_SMOOTH_GEOM
765  if (DEBUGCOND) {
766  std::cout << " writeLine=" << toString(line) << "\n";
767  }
768 #endif
769  } else {
770  // find control points
771  PositionVector begShape;
772  PositionVector endShape;
773  if (j == 0 || j == numPoints - 2) {
774  // keep the angle of the first/last segment but end at the front of the shape
775  begShape = line;
776  begShape.add(p0 - begShape.back());
777  } else if (j == 1 || p0.distanceTo2D(shape2[j - 1]) > longThresh) {
778  // use the previous segment if it is long or the first one
779  begShape.push_back(shape2[j - 1]);
780  begShape.push_back(p0);
781  } else {
782  // end at p0 with mean angle of the previous and current segment
783  begShape.push_back(shape2[j - 1]);
784  begShape.push_back(p1);
785  begShape.add(p0 - begShape.back());
786  }
787 
788  if (j == 0 || j == numPoints - 2) {
789  // keep the angle of the first/last segment but start at the end of the shape
790  endShape = line;
791  endShape.add(p1 - endShape.front());
792  } else if (j == numPoints - 3 || p1.distanceTo2D(shape2[j + 2]) > longThresh) {
793  // use the next segment if it is long or the final one
794  endShape.push_back(p1);
795  endShape.push_back(shape2[j + 2]);
796  } else {
797  // start at p1 with mean angle of the current and next segment
798  endShape.push_back(p0);
799  endShape.push_back(shape2[j + 2]);
800  endShape.add(p1 - endShape.front());
801  }
802  const double extrapolateLength = MIN2((double)25, lineLength / 4);
803  PositionVector init = NBNode::bezierControlPoints(begShape, endShape, false, extrapolateLength, extrapolateLength, ok, nullptr, straightThresh);
804  if (init.size() == 0) {
805  // could not compute control points, write line
806  offset = writeGeomLines(line, device, elevationDevice, offset);
807 #ifdef DEBUG_SMOOTH_GEOM
808  if (DEBUGCOND) {
809  std::cout << " writeLine lineLength=" << lineLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
810  }
811 #endif
812  } else {
813  // write bezier
814  const double curveLength = init.bezier(12).length2D();
815  offset = writeGeomPP3(device, elevationDevice, init, curveLength, offset);
816 #ifdef DEBUG_SMOOTH_GEOM
817  if (DEBUGCOND) {
818  std::cout << " writeCurve lineLength=" << lineLength << " curveLength=" << curveLength << " begShape" << j << "=" << toString(begShape) << " endShape" << j << "=" << toString(endShape) << " init" << j << "=" << toString(init) << "\n";
819  }
820 #endif
821  }
822  }
823  }
824  length = offset;
825  return ok;
826 }
827 
828 
829 void
831  // check if the shape is flat
832  bool flat = true;
833  double z = shape.size() == 0 ? 0 : shape[0].z();
834  for (int i = 1; i < (int)shape.size(); ++i) {
835  if (fabs(shape[i].z() - z) > NUMERICAL_EPS) {
836  flat = false;
837  break;
838  }
839  }
840  device << " <elevationProfile>\n";
841  if (flat) {
842  device << " <elevation s=\"0\" a=\"" << z << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
843  } else {
844  device << elevationDevice.getString();
845  }
846  device << " </elevationProfile>\n";
847 
848 }
849 
850 
851 void
853  if (e->getNumLanes() > 1) {
854  // compute 'stop line' of rightmost lane
855  const PositionVector shape0 = e->getLaneShape(0);
856  assert(shape0.size() >= 2);
857  const Position& from = shape0[-2];
858  const Position& to = shape0[-1];
859  PositionVector stopLine;
860  stopLine.push_back(to);
861  stopLine.push_back(to - PositionVector::sideOffset(from, to, -1000.0));
862  // endpoints of all other lanes should be on the stop line
863  for (int lane = 1; lane < e->getNumLanes(); ++lane) {
864  const double dist = stopLine.distance2D(e->getLaneShape(lane)[-1]);
865  if (dist > NUMERICAL_EPS) {
866  WRITE_WARNING("Uneven stop line at lane '" + e->getLaneID(lane) + "' (dist=" + toString(dist) + ") cannot be represented in OpenDRIVE.");
867  }
868  }
869  }
870 }
871 
872 void
874  if (e->knowsParameter("roadObjects")) {
875  device.openTag("objects");
876  device.setPrecision(8); // geometry hdg requires higher precision
878  for (std::string id : StringTokenizer(e->getParameter("roadObjects", "")).getVector()) {
879  SUMOPolygon* p = shc.getPolygons().get(id);
880  if (p == nullptr) {
881  WRITE_WARNING("Road object polygon '" + id + "' not found for edge '" + e->getID() + "'");
882  } else if (p->getShape().size() != 4) {
883  WRITE_WARNING("Cannot convert road object polygon '" + id + "' with " + toString(p->getShape().size()) + " points for edge '" + e->getID() + "'");
884  } else {
885  const PositionVector& shape = p->getShape();
886  device.openTag("object");
887  Position center = shape.getPolygonCenter();
888  PositionVector sideline = shape.getSubpartByIndex(0, 2);
889  PositionVector ortholine = shape.getSubpartByIndex(1, 2);
890  const double absAngle = sideline.angleAt2D(0);
891  const double length = sideline.length2D();
892  const double width = ortholine.length2D();
893  const double edgeOffset = road.nearest_offset_to_point2D(center);
894  if (edgeOffset == GeomHelper::INVALID_OFFSET) {
895  WRITE_WARNING("Cannot map road object polygon '" + id + "' with center " + toString(center) + " onto edge '" + e->getID() + "'");
896  continue;
897  }
898  Position edgePos = road.positionAtOffset2D(edgeOffset);
899  const double edgeAngle = road.rotationAtOffset(edgeOffset);
900  const double relAngle = absAngle - edgeAngle;
901  double sideOffset = center.distanceTo2D(edgePos);
902  // determine sign of sideOffset
903  PositionVector tmp = road.getSubpart2D(MAX2(0.0, edgeOffset - 1), MIN2(road.length2D(), edgeOffset + 1));
904  tmp.move2side(sideOffset);
905  if (tmp.distance2D(center) < sideOffset) {
906  sideOffset *= -1;
907  }
908  //std::cout << " id=" << id
909  // << " shape=" << shape
910  // << " center=" << center
911  // << " edgeOffset=" << edgeOffset
912  // << "\n";
913  device.writeAttr("id", id);
914  device.writeAttr("type", p->getShapeType());
915  device.writeAttr("name", p->getParameter("name", ""));
916  device.writeAttr("s", edgeOffset);
917  device.writeAttr("t", sideOffset);
918  device.writeAttr("width", width);
919  device.writeAttr("length", length);
920  device.writeAttr("hdg", relAngle);
921  device.closeTag();
922  }
923  }
924  device.setPrecision(gPrecision);
925  device.closeTag();
926  } else {
927  device << " <objects/>\n";
928  }
929 }
930 
931 
932 /****************************************************************************/
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define RAD2DEG(x)
Definition: GeomHelper.h:36
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:276
#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:73
T MAX2(T a, T b)
Definition: StdDefs.h:79
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:44
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:3655
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:605
const std::string & getStreetName() const
Returns the street name of this edge.
Definition: NBEdge.h:618
const std::string & getID() const
Definition: NBEdge.h:1423
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:677
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:516
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:589
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:3006
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:490
std::string getLaneID(int lane) const
get lane ID
Definition: NBEdge.cpp:3345
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:964
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:901
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:509
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1263
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:284
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:2078
int numNormalConnections() const
return the number of lane-to-lane connections at this junction (excluding crossings)
Definition: NBNode.cpp:3314
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:254
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3192
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:540
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:73
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:60
OutputDevice & writePreformattedTag(const std::string &val)
writes a preformatted tag to the device but ensures that any pending tags are closed
Definition: OutputDevice.h:277
void lf()
writes a line feed if applicable
Definition: OutputDevice.h:227
void close()
Closes the device and removes it from the dictionary.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:239
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
void setPrecision(int precision=gPrecision)
Sets the precison or resets it to default.
static OutputDevice & getDevice(const std::string &name)
Returns the described OutputDevice.
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:36
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:241
double x() const
Returns the x-position.
Definition: Position.h:54
double z() const
Returns the z-position.
Definition: Position.h:64
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:251
double y() const
Returns the y-position.
Definition: Position.h:59
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.h:82
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:73
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:188
int fromLane
The lane the connections starts at.
Definition: NBEdge.h:209
int toLane
The lane the connections yields in.
Definition: NBEdge.h:215
NBEdge * toEdge
The edge the connections yields in.
Definition: NBEdge.h:212
double vmax
maximum velocity
Definition: NBEdge.h:257
std::string id
id of Connection
Definition: NBEdge.h:251