Eclipse SUMO - Simulation of Urban MObility
NIImporter_OpenDrive.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
22 // Importer for networks stored in openDrive format
23 /****************************************************************************/
24 #include <config.h>
25 #include <string>
26 #include <cmath>
27 #include <iterator>
31 #include <utils/common/ToString.h>
38 #include <netbuild/NBEdge.h>
39 #include <netbuild/NBEdgeCont.h>
40 #include <netbuild/NBNode.h>
41 #include <netbuild/NBNodeCont.h>
42 #include <netbuild/NBNetBuilder.h>
43 #include <netbuild/NBOwnTLDef.h>
53 #include <utils/xml/XMLSubSys.h>
54 #include <utils/geom/Boundary.h>
55 #include "NILoader.h"
56 #include "NIImporter_OpenDrive.h"
57 
58 //#define DEBUG_VARIABLE_WIDTHS
59 //#define DEBUG_VARIABLE_SPEED
60 //#define DEBUG_CONNECTIONS
61 //#define DEBUG_SPIRAL
62 //#define DEBUG_INTERNALSHAPES
63 //#define DEBUG_SHAPE
64 
65 #define DEBUG_ID ""
66 #define DEBUG_COND(road) ((road)->id == DEBUG_ID)
67 #define DEBUG_COND2(edgeID) (StringUtils::startsWith((edgeID), DEBUG_ID))
68 #define DEBUG_COND3(roadID) (roadID == DEBUG_ID)
69 
70 // ===========================================================================
71 // definitions
72 // ===========================================================================
73 
74 // ===========================================================================
75 // static variables
76 // ===========================================================================
111 
113 };
114 
115 
170  // towards xodr v1.4 speed:unit
172 
174 };
175 
176 
183 
184 // ===========================================================================
185 // method definitions
186 // ===========================================================================
187 // ---------------------------------------------------------------------------
188 // static methods (interface in this case)
189 // ---------------------------------------------------------------------------
190 void
192  // check whether the option is set properly and all files exist
193  if (!oc.isUsableFileList("opendrive-files")) {
194  return;
195  }
196  // prepare types
197  myImportAllTypes = oc.getBool("opendrive.import-all-lanes");
198  myImportWidths = !oc.getBool("opendrive.ignore-widths");
199  myMinWidth = oc.getFloat("opendrive.min-width");
200  myImportInternalShapes = oc.getBool("opendrive.internal-shapes");
201  myIgnoreMisplacedSignals = oc.getBool("opendrive.ignore-misplaced-signals");
202  bool customLaneShapes = oc.getBool("opendrive.lane-shapes");
203  NBTypeCont& tc = nb.getTypeCont();
204  NBNodeCont& nc = nb.getNodeCont();
205  // build the handler
206  std::map<std::string, OpenDriveEdge*> edges;
207  NIImporter_OpenDrive handler(nb.getTypeCont(), edges);
208  handler.needsCharacterData();
209  // parse file(s)
210  for (const std::string& file : oc.getStringVector("opendrive-files")) {
211  handler.setFileName(file);
212  PROGRESS_BEGIN_MESSAGE("Parsing opendrive from '" + file + "'");
213  XMLSubSys::runParser(handler, file, false, false, true);
215  }
216  // apply signal reference information
217  for (auto& item : edges) {
218  for (OpenDriveSignal& signal : item.second->signals) {
219  if (signal.type == "") {
220  if (handler.getSignals().count(signal.id) == 0) {
221  WRITE_WARNINGF(TL("Could not find signal reference '%'."), signal.id);
222  } else {
223  const OpenDriveSignal& ref = handler.getSignals()[signal.id];
224  signal.type = ref.type;
225  signal.name = ref.name;
226  signal.dynamic = ref.dynamic;
227  signal.controller = ref.controller;
228  }
229  }
230  }
231  }
232 
233  // split inner/outer edges
234  std::map<std::string, OpenDriveEdge*> innerEdges, outerEdges;
235  for (std::map<std::string, OpenDriveEdge*>::iterator i = edges.begin(); i != edges.end(); ++i) {
236  if ((*i).second->isInner) {
237  innerEdges[(*i).first] = (*i).second;
238  } else {
239  outerEdges[(*i).first] = (*i).second;
240  }
241  }
242 
243  // convert geometries into a discretised representation
244  computeShapes(edges);
245  // check whether lane sections are valid and whether further must be introduced
246  revisitLaneSections(tc, edges);
247 
248  // -------------------------
249  // node building
250  // -------------------------
251  // build nodes#1
252  // look at all links which belong to a node, collect their bounding boxes
253  // and place the node in the middle of this bounding box
254  std::map<std::string, Boundary> posMap;
255  std::map<std::string, std::string> edge2junction;
256  std::vector<NodeSet> joinedNodeIDs;
257  // compute node positions
258  for (std::map<std::string, OpenDriveEdge*>::iterator i = innerEdges.begin(); i != innerEdges.end(); ++i) {
259  OpenDriveEdge* e = (*i).second;
260  assert(e->junction != "-1" && e->junction != "");
261  edge2junction[e->id] = e->junction;
262  if (posMap.find(e->junction) == posMap.end()) {
263  posMap[e->junction] = Boundary();
264  }
265  posMap[e->junction].add(e->geom.getBoxBoundary());
266  }
267  // build nodes
268  for (std::map<std::string, Boundary>::iterator i = posMap.begin(); i != posMap.end(); ++i) {
269  //std::cout << " import node=" << (*i).first << " z=" << (*i).second.getCenter() << " boundary=" << (*i).second << "\n";
270  if (!nb.getNodeCont().insert((*i).first, (*i).second.getCenter())) {
271  throw ProcessError(TLF("Could not add node '%'.", (*i).first));
272  }
273  }
274  // assign built nodes
275  for (std::map<std::string, OpenDriveEdge*>::iterator i = outerEdges.begin(); i != outerEdges.end(); ++i) {
276  OpenDriveEdge* e = (*i).second;
277  for (std::vector<OpenDriveLink>::iterator j = e->links.begin(); j != e->links.end(); ++j) {
278  OpenDriveLink& l = *j;
279  const std::string& nid = l.elementID;
280  if (l.elementType != OPENDRIVE_ET_ROAD) {
281  if (nb.getNodeCont().retrieve(nid) == nullptr) {
282  // not yet seen, build (possibly a junction without connections)
283  Position pos = l.linkType == OPENDRIVE_LT_SUCCESSOR ? e->geom[-1] : e->geom[0];
284  if (!nb.getNodeCont().insert(nid, pos)) {
285  throw ProcessError(TLF("Could not build node '%'.", nid));
286  }
287  }
288  // set node information
289  setNodeSecure(nb.getNodeCont(), *e, l.elementID, l.linkType, joinedNodeIDs);
290  continue;
291  }
292  if (edge2junction.find(l.elementID) != edge2junction.end()) {
293  // set node information of an internal road
294  setNodeSecure(nb.getNodeCont(), *e, edge2junction[l.elementID], l.linkType, joinedNodeIDs);
295  continue;
296  }
297  }
298  }
299  // we should now have all nodes set for links which are not outer edge-to-outer edge links
300 
301 
302  // build nodes#2
303  // build nodes for all outer edge-to-outer edge connections
304  for (std::map<std::string, OpenDriveEdge*>::iterator i = outerEdges.begin(); i != outerEdges.end(); ++i) {
305  OpenDriveEdge* e = (*i).second;
306  for (std::vector<OpenDriveLink>::iterator j = e->links.begin(); j != e->links.end(); ++j) {
307  OpenDriveLink& l = *j;
308  if (l.elementType != OPENDRIVE_ET_ROAD || edge2junction.find(l.elementID) != edge2junction.end()) {
309  // is a connection to an internal edge, or a node, skip
310  continue;
311  }
312  // we have a direct connection between to external edges
313  std::string id1 = e->id;
314  std::string id2 = l.elementID;
315  if (id1 < id2) {
316  std::swap(id1, id2);
317  }
318  std::string nid = id1 + "." + id2;
319  if (nb.getNodeCont().retrieve(nid) == nullptr) {
320  // not yet seen, build
321  Position pos = l.linkType == OPENDRIVE_LT_SUCCESSOR ? e->geom[-1] : e->geom[0];
322  if (!nb.getNodeCont().insert(nid, pos)) {
323  throw ProcessError(TLF("Could not build node '%'.", nid));
324  }
325  }
326  /* debug-stuff
327  else {
328  Position pos = l.linkType==OPENDRIVE_LT_SUCCESSOR ? e.geom[e.geom.size()-1] : e.geom[0];
329  cout << nid << " " << pos << " " << nb.getNodeCont().retrieve(nid)->getPosition() << endl;
330  }
331  */
332  setNodeSecure(nb.getNodeCont(), *e, nid, l.linkType, joinedNodeIDs);
333  }
334  }
335  // we should now have start/end nodes for all outer edge-to-outer edge connections
336 
337 
338  // build nodes#3
339  // assign further nodes generated from inner-edges
340  // these nodes have not been assigned earlier, because the connections are referenced in inner-edges
341  for (std::map<std::string, OpenDriveEdge*>::iterator i = outerEdges.begin(); i != outerEdges.end(); ++i) {
342  OpenDriveEdge* e = (*i).second;
343  if (e->to != nullptr && e->from != nullptr) {
344  continue;
345  }
346  for (std::map<std::string, OpenDriveEdge*>::iterator j = innerEdges.begin(); j != innerEdges.end(); ++j) {
347  OpenDriveEdge* ie = (*j).second;
348  for (std::vector<OpenDriveLink>::iterator k = ie->links.begin(); k != ie->links.end(); ++k) {
349  OpenDriveLink& il = *k;
350  if (il.elementType != OPENDRIVE_ET_ROAD || il.elementID != e->id) {
351  // not conneted to the currently investigated outer edge
352  continue;
353  }
354  std::string nid = edge2junction[ie->id];
355  if (il.contactPoint == OPENDRIVE_CP_START) {
356  setNodeSecure(nb.getNodeCont(), *e, nid, OPENDRIVE_LT_PREDECESSOR, joinedNodeIDs);
357  } else {
358  setNodeSecure(nb.getNodeCont(), *e, nid, OPENDRIVE_LT_SUCCESSOR, joinedNodeIDs);
359  }
360  }
361  }
362 
363  }
364 
365  // build start/end nodes which were not defined previously
366  for (std::map<std::string, OpenDriveEdge*>::iterator i = outerEdges.begin(); i != outerEdges.end(); ++i) {
367  OpenDriveEdge* e = (*i).second;
368  if ((e->from == nullptr || e->to == nullptr) && e->geom.size() == 0) {
369  continue;
370  }
371  if (e->from == nullptr) {
372  const std::string nid = e->id + ".begin";
373  e->from = getOrBuildNode(nid, e->geom.front(), nb.getNodeCont());
374  }
375  if (e->to == nullptr) {
376  const std::string nid = e->id + ".end";
377  e->to = getOrBuildNode(nid, e->geom.back(), nb.getNodeCont());
378  }
379  }
380 
381  std::map<NBNode*, NBNode*> joinedNodes;
382  for (NodeSet& joined : joinedNodeIDs) {
383  Position joinedPos(0, 0);
384  for (NBNode* j : joined) {
385  joinedPos.add(j->getPosition());
386  }
387  joinedPos.mul(1. / (double)joined.size());
388  const std::string joinedID = nc.createClusterId(joined);
389  if (!nc.insert(joinedID, joinedPos)) {
390  throw ProcessError(TLF("Could not add node '%'.", joinedID));
391  }
392  NBNode* n = nc.retrieve(joinedID);
393  for (NBNode* j : joined) {
394  joinedNodes[j] = n;
395  }
396  }
397  for (std::map<std::string, OpenDriveEdge*>::iterator i = outerEdges.begin(); i != outerEdges.end(); ++i) {
398  OpenDriveEdge* e = (*i).second;
399  if (joinedNodes.count(e->from) != 0) {
400  nc.extract(e->from, true);
401  e->from = joinedNodes[e->from];
402  }
403  if (joinedNodes.count(e->to) != 0) {
404  nc.extract(e->to, true);
405  e->to = joinedNodes[e->to];
406  }
407  }
408 
409 
410  // -------------------------
411  // edge building
412  // -------------------------
413  const double defaultSpeed = tc.getEdgeTypeSpeed("");
414  const bool saveOrigIDs = OptionsCont::getOptions().getBool("output.original-names");
415  const bool positionIDs = OptionsCont::getOptions().getBool("opendrive.position-ids");
416  // lane-id-map sumoEdge,sumoLaneIndex->odrLaneIndex
417  std::map<std::pair<NBEdge*, int>, int> laneIndexMap;
418  // build edges
419  for (std::map<std::string, OpenDriveEdge*>::iterator i = outerEdges.begin(); i != outerEdges.end(); ++i) {
420  OpenDriveEdge* e = (*i).second;
421  if (e->geom.size() < 2) {
422  WRITE_WARNINGF(TL("Ignoring road '%' without geometry."), e->id);
423  continue;
424  }
425  bool lanesBuilt = false;
426 
427  // go along the lane sections, build a node in between of each pair
428 
431 
433  NBNode* sFrom = e->from;
434  NBNode* sTo = e->to;
435  int priorityR = e->getPriority(OPENDRIVE_TAG_RIGHT);
436  int priorityL = e->getPriority(OPENDRIVE_TAG_LEFT);
437  double sB = 0;
438  double sE = e->length;
439  // 0-length geometries are possible if only the inner points are represented
440  PositionVector geomWithOffset = e->geom;
441  if (e->laneOffsets.size() > 0) {
442  try {
443  geomWithOffset.move2sideCustom(e->laneOffsets);
444  //std::cout << " e=" << e->id << " offsets=" << e->laneOffsets << " geom=" << e->geom << " geom2=" << geomWithOffset << "\n";
445  } catch (InvalidArgument&) {
446  WRITE_WARNINGF(TL("Could not apply laneOffsets for edge '%'"), e->id);
447  }
448  }
449 #ifdef DEBUG_SHAPE
450  if (DEBUG_COND3(e->id)) {
451  std::cout << " geomWithOffset=" << geomWithOffset << "\n";
452  }
453 #endif
454  const double length2D = geomWithOffset.length2D();
455  double cF = length2D == 0 ? 1 : e->length / length2D;
456  NBEdge* prevRight = nullptr;
457  NBEdge* prevLeft = nullptr;
458 
459  // starting at the same node as ending, and no lane sections?
460  if (sFrom == sTo && e->laneSections.size() == 1) {
461  // --> loop, split!
463  ls.s = e->length / 2.;
464  e->laneSections.push_back(ls);
465  WRITE_WARNING("Edge '" + e->id + "' has to be split as it connects same junctions.")
466  }
467  sanitizeWidths(e);
468  if (myMinWidth > 0) {
469  const double minDist = oc.getFloat("opendrive.curve-resolution");
470  splitMinWidths(e, tc, minDist);
471  }
472 
473  // build along lane sections
474  int sectionIndex = 0;
475  for (std::vector<OpenDriveLaneSection>::iterator j = e->laneSections.begin(); j != e->laneSections.end(); ++j) {
476  // add internal node if needed
477  if (j == e->laneSections.end() - 1) {
478  sTo = e->to;
479  sE = e->length / cF;
480  } else {
481  double nextS = (j + 1)->s;
482  const std::string nodeID = e->id + (positionIDs ? "." + toString(nextS) : "#" + toString(sectionIndex + 1));
483  sTo = new NBNode(nodeID, geomWithOffset.positionAtOffset(nextS));
484  if (!nb.getNodeCont().insert(sTo)) {
485  throw ProcessError(TLF("Could not add node '%'.", sTo->getID()));
486  }
487  sE = nextS / cF;
488  }
489  PositionVector geom = geomWithOffset.getSubpart2D(sB, sE);
490  std::string id = e->id;
491  if (positionIDs) {
492  if (sFrom != e->from || sTo != e->to) {
493  id = id + "." + toString((*j).s);
494  } else if (e->laneSections.size() == 1) {
495  id = id + ".0.00";
496  }
497  } else if (e->laneSections.size() > 1) {
498  id = id + "#" + toString(sectionIndex++);
499  }
500 #ifdef DEBUG_VARIABLE_WIDTHS
501  if (DEBUG_COND(e)) {
502  std::cout << " id=" << id << " sB=" << sB << " sE=" << sE << " geom=" << geom << "\n";
503  }
504 #endif
505 
506  // build lanes to right
507  NBEdge* currRight = nullptr;
508  if ((*j).rightLaneNumber > 0) {
509  std::vector<double> offsets(geom.size(), 0);
510  bool useOffsets = false;
511  PositionVector rightGeom = geom;
512 #ifdef DEBUG_SHAPE
513  if (DEBUG_COND3(e->id)) {
514  gDebugFlag1 = true;
515  }
516 #endif
517  rightGeom.move2side((*j).discardedInnerWidthRight);
518 #ifdef DEBUG_SHAPE
519  if (DEBUG_COND3(e->id)) {
520  std::cout << " -" << id << "_geom=" << geom << " -" << id << "_rightGeom=" << rightGeom << "\n";
521  gDebugFlag1 = false;
522  }
523 #endif
524  PositionVector laneGeom = rightGeom;
525  currRight = new NBEdge("-" + id, sFrom, sTo, (*j).rightType, defaultSpeed, NBEdge::UNSPECIFIED_FRICTION, (*j).rightLaneNumber, priorityR,
527  lanesBuilt = true;
528  std::vector<OpenDriveLane>& lanes = (*j).lanesByDir[OPENDRIVE_TAG_RIGHT];
529  std::sort(lanes.begin(), lanes.end(), LaneSorter());
530  for (std::vector<OpenDriveLane>::const_iterator k = lanes.begin(); k != lanes.end(); ++k) {
531  std::map<int, int>::const_iterator lp = (*j).laneMap.find((*k).id);
532  if (lp != (*j).laneMap.end()) {
533  int sumoLaneIndex = lp->second;
534  setLaneAttributes(e, currRight->getLaneStruct(sumoLaneIndex), *k, saveOrigIDs, tc);
535  laneIndexMap[std::make_pair(currRight, sumoLaneIndex)] = (*k).id;
536  if (useOffsets) {
537  PositionVector laneShape = laneGeom;
538  laneShape.move2sideCustom(offsets);
539  currRight->getLaneStruct(sumoLaneIndex).customShape = laneShape;
540  }
541  } else if (customLaneShapes) {
542  useOffsets = true;
543  }
544  if (customLaneShapes) {
545  addOffsets(false, laneGeom, (*k).widthData, e->id + "_" + toString((*k).id), offsets);
546  }
547  }
548  if (!nb.getEdgeCont().insert(currRight, myImportAllTypes)) {
549  throw ProcessError(TLF("Could not add edge '%'.", currRight->getID()));
550  }
551  if (nb.getEdgeCont().wasIgnored(id)) {
552  prevRight = nullptr;
553  } else {
554  // connect lane sections
555  if (prevRight != nullptr) {
556  std::map<int, int> connections = (*j).getInnerConnections(OPENDRIVE_TAG_RIGHT, *(j - 1));
557  for (std::map<int, int>::const_iterator k = connections.begin(); k != connections.end(); ++k) {
558 #ifdef DEBUG_CONNECTIONS
559  if (DEBUG_COND(e)) {
560  std::cout << "addCon1 from=" << prevRight->getID() << "_" << (*k).first << " to=" << currRight->getID() << "_" << (*k).second << "\n";
561  }
562 #endif
563  prevRight->addLane2LaneConnection((*k).first, currRight, (*k).second, NBEdge::Lane2LaneInfoType::VALIDATED);
564  }
565  }
566  prevRight = currRight;
567  }
568  }
569 
570  // build lanes to left
571  NBEdge* currLeft = nullptr;
572  if ((*j).leftLaneNumber > 0) {
573  std::vector<double> offsets(geom.size(), 0);
574  bool useOffsets = false;
575  PositionVector leftGeom = geom;
576  leftGeom.move2side(-(*j).discardedInnerWidthLeft);
577  PositionVector laneGeom = leftGeom;
578 #ifdef DEBUG_SHAPE
579  if (DEBUG_COND3(e->id)) {
580  std::cout << " " << id << "_geom=" << geom << " " << id << "_leftGeom=" << leftGeom << "\n";
581  }
582 #endif
583  currLeft = new NBEdge(id, sTo, sFrom, (*j).leftType, defaultSpeed, NBEdge::UNSPECIFIED_FRICTION, (*j).leftLaneNumber, priorityL,
585  lanesBuilt = true;
586  std::vector<OpenDriveLane>& lanes = (*j).lanesByDir[OPENDRIVE_TAG_LEFT];
587  std::sort(lanes.begin(), lanes.end(), LaneSorter());
588  for (std::vector<OpenDriveLane>::const_iterator k = lanes.begin(); k != lanes.end(); ++k) {
589  std::map<int, int>::const_iterator lp = (*j).laneMap.find((*k).id);
590  if (lp != (*j).laneMap.end()) {
591  int sumoLaneIndex = lp->second;
592  setLaneAttributes(e, currLeft->getLaneStruct(sumoLaneIndex), *k, saveOrigIDs, tc);
593  laneIndexMap[std::make_pair(currLeft, sumoLaneIndex)] = (*k).id;
594  if (useOffsets) {
595  PositionVector laneShape = laneGeom;
596  laneShape.move2sideCustom(offsets);
597  currLeft->getLaneStruct(sumoLaneIndex).customShape = laneShape.reverse();
598  }
599  } else if (customLaneShapes) {
600  useOffsets = true;
601  }
602  if (customLaneShapes) {
603  addOffsets(true, laneGeom, (*k).widthData, e->id + "_" + toString((*k).id), offsets);
604  }
605  }
606  if (!nb.getEdgeCont().insert(currLeft, myImportAllTypes)) {
607  throw ProcessError(TLF("Could not add edge '%'.", currLeft->getID()));
608  }
609  if (nb.getEdgeCont().wasIgnored(id)) {
610  prevLeft = nullptr;
611  } else {
612  // connect lane sections
613  if (prevLeft != nullptr) {
614  std::map<int, int> connections = (*j).getInnerConnections(OPENDRIVE_TAG_LEFT, *(j - 1));
615  for (std::map<int, int>::const_iterator k = connections.begin(); k != connections.end(); ++k) {
616 #ifdef DEBUG_CONNECTIONS
617  if (DEBUG_COND(e)) {
618  std::cout << "addCon2 from=" << currLeft->getID() << "_" << (*k).first << " to=" << prevLeft->getID() << "_" << (*k).second << "\n";
619  }
620 #endif
621  currLeft->addLane2LaneConnection((*k).first, prevLeft, (*k).second, NBEdge::Lane2LaneInfoType::VALIDATED);
622  }
623  }
624  prevLeft = currLeft;
625  }
626  }
627  (*j).sumoID = id;
628 
629 
630  sB = sE;
631  sFrom = sTo;
632  }
633  if (oc.isSet("polygon-output")) {
634  writeRoadObjects(e);
635  }
636  if (!lanesBuilt) {
637  WRITE_WARNINGF(TL("Edge '%' has no lanes."), e->id);
638  }
639  }
640  if (oc.isSet("polygon-output")) {
641  for (auto item : innerEdges) {
642  writeRoadObjects(item.second);
643  }
644  }
645 
646  // -------------------------
647  // connections building
648  // -------------------------
649  // generate explicit lane-to-lane connections
650  for (std::map<std::string, OpenDriveEdge*>::iterator i = edges.begin(); i != edges.end(); ++i) {
651  setEdgeLinks2(*(*i).second, edges);
652  }
653  // compute connections across intersections, if any
654  std::vector<Connection> connections2;
655  for (std::map<std::string, OpenDriveEdge*>::iterator j = edges.begin(); j != edges.end(); ++j) {
656  const std::set<Connection>& conns = (*j).second->connections;
657 
658  for (std::set<Connection>::const_iterator i = conns.begin(); i != conns.end(); ++i) {
659  if (innerEdges.find((*i).fromEdge) != innerEdges.end()) {
660  // connections starting at inner edges are processed by starting from outer edges
661  continue;
662  }
663  if (innerEdges.find((*i).toEdge) != innerEdges.end()) {
664  std::set<Connection> seen;
665  buildConnectionsToOuter(*i, innerEdges, edges, tc, connections2, seen);
666  } else {
667  connections2.push_back(*i);
668  }
669  }
670  }
671  // set connections
672  for (std::vector<Connection>::const_iterator i = connections2.begin(); i != connections2.end(); ++i) {
673 #ifdef DEBUG_CONNECTIONS
674  std::cout << "connections2 " << (*i).getDescription() << "\n";
675 #endif
676  std::string fromEdge = (*i).fromEdge;
677  if (edges.find(fromEdge) == edges.end()) {
678  WRITE_WARNINGF(TL("While setting connections: from-edge '%' is not known."), fromEdge);
679  continue;
680  }
681  OpenDriveEdge* odFrom = edges[fromEdge];
682  int fromLane = (*i).fromLane;
683  bool fromLast = ((*i).fromCP == OPENDRIVE_CP_END) && ((*i).fromLane < 0);
684  fromEdge = fromLast ? odFrom->laneSections.back().sumoID : odFrom->laneSections[0].sumoID;
685 
686  std::string toEdge = (*i).toEdge;
687  if (edges.find(toEdge) == edges.end()) {
688  WRITE_WARNINGF(TL("While setting connections: to-edge '%' is not known."), toEdge);
689  continue;
690  }
691 
692  OpenDriveEdge* odTo = edges[toEdge];
693  int toLane = (*i).toLane;
694  bool toLast = ((*i).toCP == OPENDRIVE_CP_END) || ((*i).toLane > 0);
695  toEdge = toLast ? odTo->laneSections.back().sumoID : odTo->laneSections[0].sumoID;
696 
697  if (fromLane == UNSET_CONNECTION) {
698  continue;
699  }
700  if (fromLane < 0) {
701  fromEdge = revertID(fromEdge);
702  }
703  if (toLane == UNSET_CONNECTION) {
704  continue;
705  }
706  if (toLane < 0) {
707  toEdge = revertID(toEdge);
708  }
709  fromLane = fromLast ? odFrom->laneSections.back().laneMap[fromLane] : odFrom->laneSections[0].laneMap[fromLane];
710  toLane = toLast ? odTo->laneSections.back().laneMap[toLane] : odTo->laneSections[0].laneMap[toLane];
711  NBEdge* from = nb.getEdgeCont().retrieve(fromEdge);
712  NBEdge* to = nb.getEdgeCont().retrieve(toEdge);
713  if (from == nullptr) {
714  WRITE_WARNINGF(TL("Could not find fromEdge representation of '%' in connection '%'."), fromEdge, (*i).origID);
715  }
716  if (to == nullptr) {
717  WRITE_WARNINGF(TL("Could not find fromEdge representation of '%' in connection '%'."), toEdge, (*i).origID);
718  }
719  if (from == nullptr || to == nullptr) {
720  continue;
721  }
722 
723 #ifdef DEBUG_CONNECTIONS
724  if (DEBUG_COND2(from->getID())) {
725  std::cout << "addCon3 from=" << from->getID() << "_" << fromLane << " to=" << to->getID() << "_" << toLane << "\n";
726  }
727 #endif
728  from->addLane2LaneConnection(fromLane, to, toLane, NBEdge::Lane2LaneInfoType::USER, false, false,
735  (*i).shape);
736 
737  if ((*i).origID != "" && saveOrigIDs) {
738  // @todo: this is the most silly way to determine the connection
739  std::vector<NBEdge::Connection>& cons = from->getConnections();
740  for (std::vector<NBEdge::Connection>::iterator k = cons.begin(); k != cons.end(); ++k) {
741  if ((*k).fromLane == fromLane && (*k).toEdge == to && (*k).toLane == toLane) {
742  (*k).setParameter(SUMO_PARAM_ORIGID, (*i).origID + "_" + toString((*i).origLane));
743  break;
744  }
745  }
746  }
747  }
748 
749 
750  // -------------------------
751  // traffic lights
752  // -------------------------
753  std::map<std::string, std::string> signal2junction;
754  std::map<std::string, OpenDriveController>& controllers = handler.getControllers();
755 
756  for (const auto& it : edges) {
757  const OpenDriveEdge* e = it.second;
758  for (const OpenDriveSignal& signal : e->signals) { // signal for which junction?
759  if (signal.controller.size() == 0) {
760  continue;
761  }
762  std::string junctionID;
763  for (auto connection : e->connections) {
764  if ((connection.fromLane < 0 && signal.orientation < 0) || (connection.fromLane > 0 && signal.orientation > 0)) {
765  continue;
766  }
767  if ((signal.minLane == 0 && signal.maxLane == 0) || (signal.maxLane >= connection.fromLane && signal.minLane <= connection.fromLane)) {
768  const OpenDriveEdge* connectedEdge = edges[connection.toEdge];
769  if (controllers[signal.controller].junction.size() > 0 && controllers[signal.controller].junction != connectedEdge->junction) {
770  WRITE_WARNINGF(TL("Controlling multiple junctions by the same controller '%' is currently not implemented."), signal.controller);
771  }
772  controllers[signal.controller].junction = connectedEdge->junction;
773  }
774  }
775  }
776  }
777 
778  const bool importSignalGroups = oc.getBool("opendrive.signal-groups");
779  for (std::map<std::string, OpenDriveEdge*>::iterator i = edges.begin(); i != edges.end(); ++i) {
780  OpenDriveEdge* e = (*i).second;
781  for (const OpenDriveSignal& signal : e->signals) {
782  int intType = -1;
783  try {
784  intType = StringUtils::toInt(signal.type);
785  } catch (NumberFormatException&) {}
786  if (intType < 1000001 || (intType > 1000013 && intType != 1000020) || intType == 1000008) {
787  // not a traffic_light (Section 6.11)
788  continue;
789  }
790  if (e->laneSections.size() == 0) {
791  WRITE_WARNINGF(TL("Edge '%' has signals but no lane sections."), e->id);
792  continue;
793  }
794  std::vector<OpenDriveLaneSection>::iterator k = e->laneSections.begin();
795  bool found = false;
796  for (; k != e->laneSections.end() - 1 && !found;) {
797  if (signal.s > (*k).s && signal.s <= (*(k + 1)).s) {
798  found = true;
799  } else {
800  ++k;
801  }
802  }
803 
804  std::string id = (*k).sumoID;
805  if (id == "") {
806  // traffic light on connecting road
807  if (e->junction != "") {
808  //WRITE_WARNINGF(TL("Found a traffic light signal on an internal edge; will not build it (original edge id='%')."), e->id);
809  std::string fromID, toID;
810  for (std::vector<OpenDriveLink>::const_iterator l = e->links.begin(); l != e->links.end(); ++l) {
811  if ((*l).linkType == OPENDRIVE_LT_PREDECESSOR && (*l).elementType == OPENDRIVE_ET_ROAD) {
812  if (fromID != "") {
813  WRITE_WARNING(TL("Ambiguous start of connection."));
814  }
815  const OpenDriveEdge* const ode = edges[(*l).elementID];
816  if ((*l).contactPoint == OPENDRIVE_CP_START) {
817  fromID = ode->laneSections[0].sumoID;
818  if (signal.orientation < 0) {
819  fromID = "-" + fromID;
820  }
821  } else {
822  fromID = ode->laneSections.back().sumoID;
823  if (signal.orientation > 0) {
824  fromID = "-" + fromID;
825  }
826  }
827  }
828  if ((*l).linkType == OPENDRIVE_LT_SUCCESSOR && (*l).elementType == OPENDRIVE_ET_ROAD) {
829  if (toID != "") {
830  WRITE_WARNING(TL("Ambiguous end of connection."));
831  }
832  const OpenDriveEdge* const ode = edges[(*l).elementID];
833  toID = (*l).contactPoint == OPENDRIVE_CP_START ? ode->laneSections[0].sumoID : ode->laneSections.back().sumoID;
834  }
835  }
836  // figure out the correct combination of directions
837  NBEdge* from;
838  NBEdge* to;
839  auto fromTo = retrieveSignalEdges(nb, fromID, toID, e->junction);
840  from = fromTo.first;
841  to = fromTo.second;
842  if (from == nullptr) {
843  WRITE_WARNINGF(TL("Could not find edge '%' for signal '%'."), fromID, signal.id);
844  continue;
845  }
846 
847  // consider signal validity to determine direction
848  if (signal.maxLane != 0) {
849  bool fromForward = from->getID()[0] == '-';
850  bool lanesForward = signal.maxLane < 0;
851  if (fromForward != lanesForward) {
852  std::swap(fromID, toID);
853 
854  const auto& signalFromTo = retrieveSignalEdges(nb, fromID, toID, e->junction);
855  from = signalFromTo.first;
856  to = signalFromTo.second;
857  if (from == nullptr) {
858  WRITE_WARNINGF(TL("Could not find edge '%' for signal '%'."), fromID, signal.id);
859  continue;
860  }
861  }
862  }
863  for (NBEdge::Connection& c : from->getConnections()) {
864  if (c.toEdge == to) {
865  int odLane = laneIndexMap[std::make_pair(from, c.fromLane)];
866  //std::cout << " fromLane=" << c.fromLane << " odLane=" << odLane << "\n";
867  if (signal.minLane == 0 || (signal.minLane <= odLane && signal.maxLane >= odLane)) {
868  if (c.hasParameter("signalID")) {
869  c.setParameter("signalID", c.getParameter("signalID") + " " + signal.id);
870  } else {
871  c.setParameter("signalID", signal.id);
872  }
873  }
874  // set tlIndex to allow signal groups (defined in OpenDRIVE controller elements)
875  if (importSignalGroups) {
876  const OpenDriveController& controller = handler.getController(signal.id);
877  if (controller.id != "") {
878  if (c.getParameter("controllerID") != "") {
879  WRITE_WARNINGF(TL("The signaling of the connection from '%' to '%' (controller '%') is ambiguous because it is overwritten signal '%' and with controller '%'."), from->getID(), c.toEdge->getID(), c.getParameter("controllerID"), signal.id, controller.id);
880  }
881  //junctionsWithControllers.insert(from->getToNode()->getID());
882  int tlIndex = handler.getTLIndexForController(controller.id);
883  c.tlLinkIndex = tlIndex;
884  c.setParameter("controllerID", controller.id);
885  }
886  }
887  }
888  }
889  getTLSSecure(from, nb);
890 
891  //std::cout << "odrEdge=" << e->id << " fromID=" << fromID << " toID=" << toID << " from=" << from->getID() << " to=" << to->getID()
892  // << " signal=" << signal.id << " minLane=" << signal.minLane << " maxLane=" << signal.maxLane << "\n";
893  } else {
894  WRITE_WARNINGF(TL("Found a traffic light signal on an unknown edge (original edge id='%')."), e->id);
895  continue;
896  }
897  } else {
898  // traffic light on normal road
899  if (signal.orientation == 1) {
900  // forward direction has negative lane indices and gets a negative prefix in sumo
901  id = "-" + id;
902  }
903  NBEdge* edge = nb.getEdgeCont().retrieve(id);
904  if (edge == nullptr) {
905  WRITE_WARNINGF(TL("Could not find edge '%' for signal '%'."), id, signal.id);
906  continue;
907  }
908 
910  for (NBEdge::Connection& c : edge->getConnections()) {
911  int odLane = laneIndexMap[std::make_pair(edge, c.fromLane)];
912  if (signal.minLane == 0 || (signal.minLane <= odLane && signal.maxLane >= odLane)) {
913  if (c.hasParameter("signalID")) {
914  c.setParameter("signalID", c.getParameter("signalID") + " " + signal.id);
915  } else {
916  c.setParameter("signalID", signal.id);
917  }
918  }
919 
920  // set tlIndex to allow signal groups (defined in OpenDRIVE controller elements)
921  if (importSignalGroups) {
922  const OpenDriveController& controller = handler.getController(signal.id);
923  if (controller.id != "") {
924  if (c.getParameter("controllerID") != "") {
925  WRITE_WARNINGF(TL("The signaling of the connection from '%' to '%' (controller '%') is ambiguous because it is overwritten with signal '%' and controller '%'."), edge->getID(), c.toEdge->getID(), c.getParameter("controllerID"), signal.id, controller.id);
926  }
927  //junctionsWithControllers.insert(edge->getToNode()->getID());
928  int tlIndex = handler.getTLIndexForController(controller.id);
929  c.tlLinkIndex = tlIndex;
930  c.setParameter("controllerID", controller.id);
931  }
932  }
933  }
934  getTLSSecure(edge, nb);
935  //std::cout << "odrEdge=" << e->id << " sumoID=" << (*k).sumoID << " sumoEdge=" << edge->getID()
936  // << " signal=" << signal.id << " minLane=" << signal.minLane << " maxLane=" << signal.maxLane << "\n";
937  }
938  // @note: tls 'signalID' parameters are set via NBTrafficLightLogicCont::setOpenDriveSignalParameters
939  // @note: OpenDRIVE controllers are applied to the signal programs in NBTrafficLightLogicCont::applyOpenDriveControllers
940  }
941  }
942 
943  // -------------------------
944  // clean up
945  // -------------------------
946  for (std::map<std::string, OpenDriveEdge*>::iterator i = edges.begin(); i != edges.end(); ++i) {
947  delete (*i).second;
948  }
949 }
950 
951 
952 void
955  const bool writeGeo = GeoConvHelper::getLoaded().usingGeoProjection() && (
956  oc.isDefault("proj.plain-geo") || oc.getBool("proj.plain-geo"));
957  OutputDevice& dev = OutputDevice::getDevice(oc.getString("polygon-output"));
958  dev.writeXMLHeader("additional", "additional_file.xsd");
959  //SUMOPolygon poly("road_" + e->id, "road", RGBColor::BLUE, e->geom, true, false);
960  //poly.writeXML(dev, false);
961  for (auto& o : e->objects) {
962  Position ref = e->geom.positionAtOffset2D(o.s, -o.t);
963  if (o.radius >= 0) {
964  // cicrular shape
965  // GeoConvHelper::getFinal is not ready yet
967  PointOfInterest POI(o.id, o.type, RGBColor::YELLOW, ref, true, "", -1, false, 0, SUMOXMLDefinitions::POIIcons.getString(POIIcon::NONE));
968  POI.setParameter("name", o.name);
969  POI.writeXML(dev, writeGeo);
970  } else {
971  // rectangular shape
972  PositionVector centerLine;
973  centerLine.push_back(Position(-o.length / 2, 0));
974  centerLine.push_back(Position(o.length / 2, 0));
975  double roadHdg = e->geom.rotationAtOffset(o.s);
976  centerLine.rotate2D(roadHdg + o.hdg);
977  //PointOfInterest poiRef("ref_" + o.id, "", RGBColor::CYAN, ref, false, "", 0, 0, Shape::DEFAULT_LAYER + 2);
978  //poiRef.writeXML(dev, false);
979  centerLine.add(ref);
980  //SUMOPolygon polyCenter("center_" + o.id, "", RGBColor::MAGENTA, centerLine, true, false, Shape::DEFAULT_LAYER + 1);
981  //polyCenter.writeXML(dev, false);
982  centerLine.move2side(o.width / 2);
983  PositionVector shape = centerLine;
984  centerLine.move2side(-o.width);
985  shape.append(centerLine.reverse(), POSITION_EPS);
986  if (writeGeo) {
987  // GeoConvHelper::getFinal is not ready yet
988  for (Position& p : shape) {
990  }
991  }
992  SUMOPolygon poly(o.id, o.type, RGBColor::YELLOW, shape, true, true, 1, 7);
993  poly.setParameter("name", o.name);
994  poly.writeXML(dev, writeGeo);
995  }
996  }
997 }
998 
999 
1000 std::pair<NBEdge*, NBEdge*>
1001 NIImporter_OpenDrive::retrieveSignalEdges(NBNetBuilder& nb, const std::string& fromID, const std::string& toID, const std::string& junction) {
1002  NBEdge* from;
1003  NBEdge* to;
1004  from = nb.getEdgeCont().retrieve(fromID);
1005  if (from == nullptr || from->getToNode()->getID() != junction) {
1006  from = nb.getEdgeCont().retrieve(fromID[0] == '-' ? fromID.substr(1) : "-" + fromID);
1007  }
1008  to = nb.getEdgeCont().retrieve(toID);
1009  if (to == nullptr || to->getFromNode()->getID() != junction) {
1010  to = nb.getEdgeCont().retrieve(toID[0] == '-' ? toID.substr(1) : "-" + toID);
1011  }
1012  return std::make_pair(from, to);
1013 }
1014 
1015 
1017 NIImporter_OpenDrive::getTLSSecure(NBEdge* inEdge, /*const NBEdge::Connection& conn,*/ NBNetBuilder& nb) {
1018  NBNode* toNode = inEdge->getToNode();
1019  if (!toNode->isTLControlled()) {
1021  NBOwnTLDef* tlDef = new NBOwnTLDef(toNode->getID(), toNode, 0, type);
1022  if (!nb.getTLLogicCont().insert(tlDef)) {
1023  // actually, nothing should fail here
1024  delete tlDef;
1025  throw ProcessError();
1026  }
1027  toNode->addTrafficLight(tlDef);
1028  //tlDef->setSinglePhase();
1029  }
1030  return *toNode->getControllingTLS().begin();
1031 }
1032 
1033 void
1034 NIImporter_OpenDrive::setLaneAttributes(const OpenDriveEdge* e, NBEdge::Lane& sumoLane, const OpenDriveLane& odLane, bool saveOrigIDs, const NBTypeCont& tc) {
1035  if (saveOrigIDs) {
1036  sumoLane.setParameter(SUMO_PARAM_ORIGID, e->id + "_" + toString(odLane.id));
1037  }
1038  sumoLane.speed = odLane.speed != 0 ? odLane.speed : tc.getEdgeTypeSpeed(odLane.type);
1039  sumoLane.permissions = odLane.permission > 0 ? odLane.permission : tc.getEdgeTypePermissions(odLane.type); // TODO: how to get the OD lane specific restrictions?
1040  sumoLane.width = myImportWidths && odLane.width != NBEdge::UNSPECIFIED_WIDTH ? odLane.width : tc.getEdgeTypeWidth(odLane.type);
1041  sumoLane.type = odLane.type;
1042 
1043  const double widthResolution = tc.getEdgeTypeWidthResolution(odLane.type);
1044  const double maxWidth = tc.getEdgeTypeMaxWidth(odLane.type);
1045 
1046  const bool forbiddenNarrow = (sumoLane.width < myMinWidth
1047  && (sumoLane.permissions & SVC_PASSENGER) != 0
1048  && sumoLane.width < tc.getEdgeTypeWidth(odLane.type));
1049 
1050  if (sumoLane.width >= 0 && widthResolution > 0) {
1051  sumoLane.width = floor(sumoLane.width / widthResolution + 0.5) * widthResolution;
1052  if (forbiddenNarrow && sumoLane.width >= myMinWidth) {
1053  sumoLane.width -= widthResolution;
1054  if (sumoLane.width <= 0) {
1055  sumoLane.width = MAX2(POSITION_EPS, myMinWidth - POSITION_EPS);
1056  }
1057  } else if (sumoLane.width == 0) {
1058  // round up when close to 0
1059  sumoLane.width = widthResolution;
1060  }
1061  }
1062  if (maxWidth > 0) {
1063  sumoLane.width = MIN2(sumoLane.width, maxWidth);
1064  }
1065  if (forbiddenNarrow) {
1066  // avoid narrow passenger car lanes (especially at sections with varying width)
1068  }
1069 }
1070 
1071 void
1073  const std::map<std::string, OpenDriveEdge*>& innerEdges,
1074  const std::map<std::string, OpenDriveEdge*>& edges,
1075  const NBTypeCont& tc,
1076  std::vector<Connection>& into, std::set<Connection>& seen) {
1077 
1078  OpenDriveEdge* dest = innerEdges.find(c.toEdge)->second;
1079 #ifdef DEBUG_CONNECTIONS
1080  if (DEBUG_COND3(c.fromEdge)) {
1081  std::cout << " buildConnectionsToOuter " << c.getDescription() << "\n";
1082  std::cout << " dest=" << (dest == nullptr ? "NULL" : dest->id) << " seenlist=";
1083  for (std::set<Connection>::const_iterator i = seen.begin(); i != seen.end(); ++i) {
1084  std::cout << " " << (*i).fromEdge << "," << (*i).toEdge << " ";
1085  }
1086  std::cout << "\n";
1087  }
1088 #endif
1089  if (dest == nullptr) {
1091  return;
1092  }
1093  seen.insert(c);
1094  for (const Connection& destCon : dest->connections) {
1095  auto innerEdgesIt = innerEdges.find(destCon.toEdge);
1096 #ifdef DEBUG_CONNECTIONS
1097  if (DEBUG_COND3(c.fromEdge)) {
1098  std::cout << " toInner=" << (innerEdgesIt != innerEdges.end()) << " destCon " << destCon.getDescription() << "\n";
1099  }
1100 #endif
1101  if (innerEdgesIt != innerEdges.end()) {
1102  std::vector<Connection> t;
1103  if (seen.count(destCon) == 0) {
1104  buildConnectionsToOuter(destCon, innerEdges, edges, tc, t, seen);
1105  for (std::vector<Connection>::const_iterator j = t.begin(); j != t.end(); ++j) {
1106  // @todo this section is unverified
1107  Connection cn = (*j);
1108  cn.fromEdge = c.fromEdge;
1109  cn.fromLane = c.fromLane;
1110  cn.fromCP = c.fromCP;
1111  cn.all = c.all; // @todo "all" is a hack trying to avoid the "from is zero" problem;
1112  if (myImportInternalShapes) {
1113  cn.shape = innerEdgesIt->second->geom + c.shape;
1114  }
1115  into.push_back(cn);
1116  }
1117  } else {
1118  WRITE_WARNING("Circular connections in junction including roads '" + c.fromEdge + "' and '" + c.toEdge + "', loop size " + toString(seen.size()));
1119  }
1120  } else {
1121  int in = c.toLane;
1122  int out = destCon.fromLane;
1123  if (c.toCP == OPENDRIVE_CP_END) {
1124  // inner edge runs in reverse direction
1125  std::swap(in, out);
1126  }
1127 #ifdef DEBUG_CONNECTIONS
1128  if (DEBUG_COND3(c.fromEdge)) {
1129  std::cout << " laneSectionsConnected dest=" << dest->id << " in=" << in << " out=" << out
1130  << " connected=" << laneSectionsConnected(dest, in, out) << "\n";
1131  }
1132 #endif
1133 
1134  if (laneSectionsConnected(dest, in, out)) {
1135  Connection cn = destCon;
1136  cn.fromEdge = c.fromEdge;
1137  cn.fromLane = c.fromLane;
1138  cn.fromCP = c.fromCP;
1139  cn.all = c.all;
1140  cn.origID = c.toEdge;
1141  cn.origLane = c.toLane;
1142  if (myImportInternalShapes) {
1143  OpenDriveXMLTag lanesDir;
1144  cn.shape = dest->geom;
1145  // determine which lane of dest belongs to this connection
1146  int referenceLane = 0;
1147  int offsetFactor = 1;
1148  if (c.toCP == OPENDRIVE_CP_END) {
1149  offsetFactor = -1;
1150  lanesDir = OPENDRIVE_TAG_LEFT;
1151  for (const auto& destLane : dest->laneSections.front().lanesByDir[lanesDir]) {
1152  if (destLane.successor == c.fromLane) {
1153  referenceLane = destLane.id;
1154  break;
1155  }
1156  }
1157  } else {
1158  lanesDir = OPENDRIVE_TAG_RIGHT;
1159  for (const auto& destLane : dest->laneSections.front().lanesByDir[lanesDir]) {
1160  if (destLane.predecessor == c.fromLane) {
1161  referenceLane = destLane.id;
1162  break;
1163  }
1164  }
1165  }
1166  // compute offsets
1167  //if (cn.fromEdge == "1014000" && dest->id == "3001022") {
1168  // std::cout << "computeOffsets\n";
1169  //}
1170  std::vector<double> offsets(dest->geom.size(), 0);
1171  if (dest->laneOffsets.size() > 0) {
1172  offsets = dest->laneOffsets;
1173  }
1174 #ifdef DEBUG_INTERNALSHAPES
1175  std::string destPred;
1176 #endif
1177  double s = 0;
1178  int iShape = 0;
1179  for (int laneSectionIndex = 0; laneSectionIndex < (int)dest->laneSections.size(); laneSectionIndex++) {
1180  OpenDriveLaneSection& laneSection = dest->laneSections[laneSectionIndex];
1181  const double nextS = laneSectionIndex + 1 < (int)dest->laneSections.size() ? dest->laneSections[laneSectionIndex + 1].s : std::numeric_limits<double>::max();
1182  double sStart = s; // distance offset a the start of the current lane section
1183  double finalS = s; // final distance value after processing this segment
1184  int finalI = iShape;
1185  for (const OpenDriveLane& destLane : laneSection.lanesByDir[lanesDir]) {
1186  // each lane of the current segment repeats the same section of shape points and distance offsets
1187  double sectionS = 0;
1188  int i = iShape; // shape index at the start of the current lane section
1189  s = sStart;
1190 #ifdef DEBUG_INTERNALSHAPES
1191  destPred += " lane=" + toString(destLane.id)
1192  + " pred=" + toString(destLane.predecessor)
1193  + " succ=" + toString(destLane.successor)
1194  + " wStart=" + (destLane.widthData.empty() ? "?" : toString(destLane.widthData.front().computeAt(0)))
1195  + " wEnd=" + (destLane.widthData.empty() ? "?" : toString(destLane.widthData.front().computeAt(cn.shape.length2D())))
1196  + " width=" + toString(destLane.width) + "\n";
1197 #endif
1198  if (abs(destLane.id) <= abs(referenceLane)) {
1199  const double multiplier = offsetFactor * (destLane.id == referenceLane ? 0.5 : 1);
1200 #ifdef DEBUG_INTERNALSHAPES
1201  destPred += " multiplier=" + toString(multiplier) + "\n";
1202 #endif
1203  int widthDataIndex = 0;
1204  while (s < nextS && i < (int)cn.shape.size()) {
1205  if (i > 0) {
1206  const double dist = cn.shape[i - 1].distanceTo2D(cn.shape[i]);
1207  s += dist;
1208  sectionS += dist;
1209 
1210  }
1211  while (widthDataIndex + 1 < (int)destLane.widthData.size()
1212  && sectionS >= destLane.widthData[widthDataIndex + 1].s) {
1213  widthDataIndex++;
1214  }
1215  double width = tc.getEdgeTypeWidth(destLane.type);
1216  if (destLane.widthData.size() > 0) {
1217  width = destLane.widthData[widthDataIndex].computeAt(sectionS);
1218  } else {
1219 #ifdef DEBUG_INTERNALSHAPES
1220  std::cout << " missing width data at inner edge " << dest->id << " to=" << cn.toEdge << "_" << cn.toLane << " cp=" << cn.toCP << "\n";
1221 #endif
1222  // use first width of the target lane
1223  OpenDriveEdge* const outerToEdge = edges.find(cn.toEdge)->second;
1224  OpenDriveLaneSection& toLaneSection = cn.toCP == OPENDRIVE_CP_END ? outerToEdge->laneSections.front() : outerToEdge->laneSections.back();
1225  const OpenDriveXMLTag laneDir = cn.toLane < 0 ? OPENDRIVE_TAG_RIGHT : OPENDRIVE_TAG_LEFT;
1226  for (const OpenDriveLane& outerToLane : toLaneSection.lanesByDir[laneDir]) {
1227  if (outerToLane.id == cn.toLane && outerToLane.width > 0) {
1228 #ifdef DEBUG_INTERNALSHAPES
1229  std::cout << " using toLane width " << width << "\n";
1230 #endif
1231  break;
1232  }
1233  }
1234  }
1235  offsets[i] += width * multiplier;
1236  //if (cn.fromEdge == "1014000" && dest->id == "3001022") {
1237  // std::cout << " i=" << i << " s=" << s << " lane=" << destLane.id << " rlane=" << referenceLane /*<< " nextS=" << nextS << */ << " lsIndex=" << laneSectionIndex << " wI=" << widthDataIndex << " wSize=" << destLane.widthData.size() << " m=" << multiplier << " o=" << offsets[i] << "\n";
1238  //}
1239  i++;
1240  }
1241  finalS = s;
1242  finalI = i;
1243  } else if (finalS == s) {
1244  // update finalS without changing offsets
1245  while (s < nextS && i < (int)cn.shape.size()) {
1246  if (i > 0) {
1247  const double dist = cn.shape[i - 1].distanceTo2D(cn.shape[i]);
1248  s += dist;
1249  finalS += dist;
1250 
1251  }
1252  i++;
1253  }
1254  finalI = i;
1255 
1256  }
1257  }
1258  // advance values for the next lane section
1259  iShape = finalI;
1260  s = finalS;
1261  }
1262  try {
1263  cn.shape.move2sideCustom(offsets);
1264  } catch (InvalidArgument&) {
1265  WRITE_WARNING("Could not import internal lane shape from edge '" + c.fromEdge + "' to edge '" + c.toEdge);
1266  cn.shape.clear();
1267  }
1268 #ifdef DEBUG_INTERNALSHAPES
1269  std::cout << "internalShape "
1270  << c.getDescription()
1271  << " dest=" << dest->id
1272  << " refLane=" << referenceLane
1273  << " destPred\n" << destPred
1274  << " offsets=" << offsets
1275  << "\n shape=" << dest->geom
1276  << "\n shape2=" << cn.shape
1277  << "\n";
1278 #endif
1279  if (c.toCP == OPENDRIVE_CP_END) {
1280  cn.shape = cn.shape.reverse();
1281  }
1282  }
1283 #ifdef DEBUG_CONNECTIONS
1284  if (DEBUG_COND3(c.fromEdge)) {
1285  std::cout << " added connection\n";
1286  }
1287 #endif
1288  into.push_back(cn);
1289  }
1290  }
1291  }
1292 }
1293 
1294 
1295 bool
1297  if (edge->laneSections.size() == 1) {
1298  return in == out;
1299  } else {
1300  // there could be spacing lanes (type 'none') that lead to a shift in lane index
1301  for (auto it = edge->laneSections.begin(); it + 1 < edge->laneSections.end(); it++) {
1302  OpenDriveLaneSection& laneSection = *it;
1303  if (laneSection.lanesByDir.find(OPENDRIVE_TAG_RIGHT) != laneSection.lanesByDir.end()) {
1304  for (OpenDriveLane& lane : laneSection.lanesByDir.find(OPENDRIVE_TAG_RIGHT)->second) {
1305  if (lane.id == in) {
1306  in = lane.successor;
1307  break;
1308  }
1309  }
1310  }
1311  if (laneSection.lanesByDir.find(OPENDRIVE_TAG_LEFT) != laneSection.lanesByDir.end()) {
1312  for (OpenDriveLane& lane : laneSection.lanesByDir.find(OPENDRIVE_TAG_LEFT)->second) {
1313  if (lane.id == in) {
1314  in = lane.successor;
1315  break;
1316  }
1317  }
1318  }
1319  }
1320  return in == out;
1321  }
1322 }
1323 
1324 
1325 void
1326 NIImporter_OpenDrive::setEdgeLinks2(OpenDriveEdge& e, const std::map<std::string, OpenDriveEdge*>& edges) {
1327  for (std::vector<OpenDriveLink>::iterator i = e.links.begin(); i != e.links.end(); ++i) {
1328  OpenDriveLink& l = *i;
1329  if (l.elementType != OPENDRIVE_ET_ROAD) {
1330  // we assume that links to nodes are later given as connections to edges
1331  continue;
1332  }
1333  // get the right direction of the connected edge
1334  std::string connectedEdge = l.elementID;
1335  std::string edgeID = e.id;
1336 
1337  OpenDriveLaneSection& laneSection = l.linkType == OPENDRIVE_LT_SUCCESSOR ? e.laneSections.back() : e.laneSections[0];
1338  const std::map<int, int>& laneMap = laneSection.laneMap;
1339 #ifdef DEBUG_CONNECTIONS
1340  if (DEBUG_COND(&e)) {
1341  std::cout << "edge=" << e.id << " eType=" << l.elementType << " lType=" << l.linkType << " connectedEdge=" << connectedEdge << " laneSection=" << laneSection.s << " map:\n";
1342  std::cout << joinToString(laneMap, "\n", ":") << "\n";
1343  }
1344 #endif
1345  if (laneSection.lanesByDir.find(OPENDRIVE_TAG_RIGHT) != laneSection.lanesByDir.end()) {
1346  const std::vector<OpenDriveLane>& lanes = laneSection.lanesByDir.find(OPENDRIVE_TAG_RIGHT)->second;
1347  for (std::vector<OpenDriveLane>::const_iterator j = lanes.begin(); j != lanes.end(); ++j) {
1348  if (!myImportAllTypes && laneMap.find((*j).id) == laneMap.end()) {
1349  continue;
1350  }
1351  Connection c; // @todo: give Connection a new name and a constructor
1352  c.fromEdge = e.id;
1353  c.fromLane = (*j).id;
1355  c.toLane = l.linkType == OPENDRIVE_LT_SUCCESSOR ? (*j).successor : (*j).predecessor;
1356  c.toEdge = connectedEdge;
1357  c.toCP = l.contactPoint;
1358  c.all = false;
1359  if (l.linkType != OPENDRIVE_LT_SUCCESSOR) {
1360  std::swap(c.fromEdge, c.toEdge);
1361  std::swap(c.fromLane, c.toLane);
1362  std::swap(c.fromCP, c.toCP);
1363  }
1364  if (edges.find(c.fromEdge) == edges.end()) {
1365  WRITE_ERRORF(TL("While setting connections: incoming road '%' is not known."), c.fromEdge);
1366  } else {
1367  OpenDriveEdge* src = edges.find(c.fromEdge)->second;
1368  src->connections.insert(c);
1369 #ifdef DEBUG_CONNECTIONS
1370  if (DEBUG_COND(src)) {
1371  std::cout << "insertConRight from=" << src->id << "_" << c.fromLane << " to=" << c.toEdge << "_" << c.toLane << "\n";
1372  }
1373 #endif
1374  }
1375  }
1376  }
1377  if (laneSection.lanesByDir.find(OPENDRIVE_TAG_LEFT) != laneSection.lanesByDir.end()) {
1378  const std::vector<OpenDriveLane>& lanes = laneSection.lanesByDir.find(OPENDRIVE_TAG_LEFT)->second;
1379  for (std::vector<OpenDriveLane>::const_iterator j = lanes.begin(); j != lanes.end(); ++j) {
1380  if (!myImportAllTypes && laneMap.find((*j).id) == laneMap.end()) {
1381  continue;
1382  }
1383  Connection c;
1384  c.toEdge = e.id;
1385  c.toLane = (*j).id;
1386  c.toCP = OPENDRIVE_CP_END;
1387  c.fromLane = l.linkType == OPENDRIVE_LT_SUCCESSOR ? (*j).successor : (*j).predecessor;
1388  c.fromEdge = connectedEdge;
1389  c.fromCP = l.contactPoint;
1390  c.all = false;
1391  if (l.linkType != OPENDRIVE_LT_SUCCESSOR) {
1392  std::swap(c.fromEdge, c.toEdge);
1393  std::swap(c.fromLane, c.toLane);
1394  std::swap(c.fromCP, c.toCP);
1395  }
1396  if (edges.find(c.fromEdge) == edges.end()) {
1397  WRITE_ERRORF(TL("While setting connections: incoming road '%' is not known."), c.fromEdge);
1398  } else {
1399  OpenDriveEdge* src = edges.find(c.fromEdge)->second;
1400  src->connections.insert(c);
1401 #ifdef DEBUG_CONNECTIONS
1402  if (DEBUG_COND(src)) {
1403  std::cout << "insertConLeft from=" << src->id << "_" << c.fromLane << " to=" << c.toEdge << "_" << c.toLane << "\n";
1404  }
1405 #endif
1406  }
1407  }
1408  }
1409  }
1410 }
1411 
1412 
1413 std::string NIImporter_OpenDrive::revertID(const std::string& id) {
1414  if (id[0] == '-') {
1415  return id.substr(1);
1416  }
1417  return "-" + id;
1418 }
1419 
1420 
1421 NBNode*
1422 NIImporter_OpenDrive::getOrBuildNode(const std::string& id, const Position& pos,
1423  NBNodeCont& nc) {
1424  if (nc.retrieve(id) == nullptr) {
1425  // not yet built; build now
1426  if (!nc.insert(id, pos)) {
1427  // !!! clean up
1428  throw ProcessError(TLF("Could not add node '%'.", id));
1429  }
1430  }
1431  return nc.retrieve(id);
1432 }
1433 
1434 
1435 void
1437  const std::string& nodeID, NIImporter_OpenDrive::LinkType lt, std::vector<NodeSet>& joinedNodeIDs) {
1438  NBNode* n = nc.retrieve(nodeID);
1439  if (n == nullptr) {
1440  throw ProcessError(TLF("Could not find node '%'.", nodeID));
1441  }
1442  NBNode* toJoin = nullptr;
1443  if (lt == OPENDRIVE_LT_SUCCESSOR) {
1444  if (e.to != nullptr && e.to != n) {
1445  toJoin = e.to;
1446  }
1447  e.to = n;
1448  } else {
1449  if (e.from != nullptr && e.from != n) {
1450  toJoin = e.from;
1451  }
1452  e.from = n;
1453  }
1454  if (toJoin != nullptr) {
1455  // join nodes
1456  NodeSet* set1 = nullptr;
1457  NodeSet* set2 = nullptr;
1458  for (NodeSet& joined : joinedNodeIDs) {
1459  if (joined.count(toJoin) != 0) {
1460  set1 = &joined;
1461  }
1462  if (joined.count(n) != 0) {
1463  set2 = &joined;
1464  }
1465  }
1466  if (set1 == nullptr && set2 == nullptr) {
1467  joinedNodeIDs.push_back(NodeSet());
1468  joinedNodeIDs.back().insert(n);
1469  joinedNodeIDs.back().insert(toJoin);
1470  } else if (set1 == nullptr && set2 != nullptr) {
1471  set2->insert(toJoin);
1472  } else if (set1 != nullptr && set2 == nullptr) {
1473  set1->insert(n);
1474  } else {
1475  set1->insert(set2->begin(), set2->end());
1476  joinedNodeIDs.erase(std::find(joinedNodeIDs.begin(), joinedNodeIDs.end(), *set2));
1477  }
1478  }
1479 }
1480 
1481 bool
1483  if (e.elevations.size() > 1) {
1484  return true;
1485  }
1486  for (OpenDriveElevation& el : e.elevations) {
1487  if (el.c != 0 || el.d != 0) {
1488  return true;
1489  }
1490  }
1491  return false;
1492 }
1493 
1494 void
1495 NIImporter_OpenDrive::computeShapes(std::map<std::string, OpenDriveEdge*>& edges) {
1497  const double res = oc.getFloat("opendrive.curve-resolution");
1498  for (std::map<std::string, OpenDriveEdge*>::iterator i = edges.begin(); i != edges.end(); ++i) {
1499  OpenDriveEdge& e = *(*i).second;
1501  const double lineRes = hasNonLinearElevation(e) ? res : -1;
1502  Position last;
1503  for (std::vector<OpenDriveGeometry>::iterator j = e.geometries.begin(); j != e.geometries.end(); ++j) {
1504  OpenDriveGeometry& g = *j;
1505  PositionVector geom;
1506  switch (g.type) {
1507  case OPENDRIVE_GT_UNKNOWN:
1508  break;
1509  case OPENDRIVE_GT_LINE:
1510  geom = geomFromLine(e, g, lineRes);
1511  break;
1512  case OPENDRIVE_GT_SPIRAL:
1513  geom = geomFromSpiral(e, g, res);
1514  break;
1515  case OPENDRIVE_GT_ARC:
1516  geom = geomFromArc(e, g, res);
1517  break;
1518  case OPENDRIVE_GT_POLY3:
1519  geom = geomFromPoly(e, g, res);
1520  break;
1522  geom = geomFromParamPoly(e, g, res);
1523  break;
1524  default:
1525  break;
1526  }
1527  if (e.geom.size() > 0 && prevType == OPENDRIVE_GT_LINE) {
1528  // remove redundant end point of the previous geometry segment
1529  // (the start point of the current segment should have the same value)
1530  // this avoids geometry errors due to imprecision
1531  if (!e.geom.back().almostSame(geom.front())) {
1532  const int index = (int)(j - e.geometries.begin());
1533  WRITE_WARNINGF(TL("Mismatched geometry for edge '%' between geometry segments % and %."), e.id, index - 1, index);
1534  }
1535  e.geom.pop_back();
1536  }
1537  //std::cout << " adding geometry to road=" << e.id << " old=" << e.geom << " new=" << geom << "\n";
1538  for (PositionVector::iterator k = geom.begin(); k != geom.end(); ++k) {
1539  last = *k;
1541  }
1542  prevType = g.type;
1543  }
1544  if (e.geom.size() == 1 && e.geom.front() != last) {
1545  // avoid length-1 geometry due to almostSame check
1546  e.geom.push_back(last);
1547  }
1548 #ifdef DEBUG_SHAPE
1549  if (DEBUG_COND3(e.id)) {
1550  std::cout << " initialGeom=" << e.geom << "\n";
1551  }
1552 #endif
1553  if (oc.exists("geometry.min-dist") && !oc.isDefault("geometry.min-dist")) {
1554  // simplify geometry for both directions consistently but ensure
1555  // that start and end angles are preserved
1556  if (e.geom.size() > 4) {
1557  e.geom.removeDoublePoints(oc.getFloat("geometry.min-dist"), true, 1, 1, true);
1558  }
1559  }
1560 #ifdef DEBUG_SHAPE
1561  if (DEBUG_COND3(e.id)) {
1562  std::cout << " reducedGeom=" << e.geom << "\n";
1563  }
1564 #endif
1566  WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), e.id);
1567  }
1568  // add z-data
1569  int k = 0;
1570  double pos = 0;
1571  //std::cout << " edge=" << e.id << " geom.size=" << e.geom.size() << " geom.len=" << e.geom.length2D() << " ele.size=" << e.elevations.size() << "\n";
1572  if (!oc.getBool("flatten")) {
1573  for (std::vector<OpenDriveElevation>::iterator j = e.elevations.begin(); j != e.elevations.end(); ++j) {
1574  const OpenDriveElevation& el = *j;
1575  const double sNext = (j + 1) == e.elevations.end() ? std::numeric_limits<double>::max() : (*(j + 1)).s;
1576  while (k < (int)e.geom.size() && pos < sNext) {
1577  const double z = el.computeAt(pos);
1578  //std::cout << " edge=" << e.id << " k=" << k << " sNext=" << sNext << " pos=" << pos << " z=" << z << " el.s=" << el.s << " el.a=" << el.a << " el.b=" << el.b << " el.c=" << el.c << " el.d=" << el.d << "\n";
1579  e.geom[k].add(0, 0, z);
1580  k++;
1581  if (k < (int)e.geom.size()) {
1582  // XXX pos understimates the actual position since the
1583  // actual geometry between k-1 and k could be curved
1584  pos += e.geom[k - 1].distanceTo2D(e.geom[k]);
1585  }
1586  }
1587  }
1588  }
1589  // add laneoffset
1590  if (e.offsets.size() > 0) {
1592  }
1593  //std::cout << " loaded geometry " << e.id << "=" << e.geom << "\n";
1594  }
1595 }
1596 
1597 
1598 std::vector<double>
1599 NIImporter_OpenDrive::discretizeOffsets(PositionVector& geom, const std::vector<OpenDriveLaneOffset>& offsets, const std::string& id) {
1600  UNUSED_PARAMETER(id);
1601  std::vector<double> laneOffsets;
1602  // make sure there are intermediate points for each offset-section
1603  for (const OpenDriveLaneOffset& el : offsets) {
1604  // check wether we need to insert a new point at dist
1605  Position pS = geom.positionAtOffset2D(el.s);
1606  int iS = geom.indexOfClosest(pS);
1607  // prevent close spacing to reduce impact of rounding errors in z-axis
1608  if (pS.distanceTo2D(geom[iS]) > POSITION_EPS) {
1609  geom.insertAtClosest(pS, false);
1610  //std::cout << " edge=" << e.id << " inserting pos=" << pS << " s=" << el.s << " iS=" << iS << " dist=" << pS.distanceTo2D(geom[iS]) << "\n";
1611  }
1612  }
1613  // XXX add further points for sections with non-constant offset
1614  // shift each point orthogonally by the specified offset
1615  int kk = 0;
1616  double ppos = 0;
1617  for (auto j = offsets.begin(); j != offsets.end(); ++j) {
1618  const OpenDriveLaneOffset& el = *j;
1619  const double sNext = (j + 1) == offsets.end() ? std::numeric_limits<double>::max() : (*(j + 1)).s;
1620  while (kk < (int)geom.size() && ppos < sNext) {
1621  const double offset = el.computeAt(ppos);
1622  laneOffsets.push_back(fabs(offset) > POSITION_EPS ? -offset : 0);
1623  kk++;
1624  if (kk < (int)geom.size()) {
1625  // XXX pos understimates the actual position since the
1626  // actual geometry between k-1 and k could be curved
1627  ppos += geom[kk - 1].distanceTo2D(geom[kk]);
1628  }
1629  }
1630  }
1631  return laneOffsets;
1632 }
1633 
1634 
1635 void
1636 NIImporter_OpenDrive::addOffsets(bool left, PositionVector& geom, const std::vector<OpenDriveWidth>& offsets, const std::string& id, std::vector<double>& result) {
1637  UNUSED_PARAMETER(id);
1638  // make sure there are intermediate points for each offset-section
1639  for (const OpenDriveLaneOffset& el : offsets) {
1640  // check wether we need to insert a new point at dist
1641  Position pS = geom.positionAtOffset2D(el.s);
1642  int iS = geom.indexOfClosest(pS);
1643  // prevent close spacing to reduce impact of rounding errors in z-axis
1644  if (pS.distanceTo2D(geom[iS]) > POSITION_EPS) {
1645  //std::cout << " edge=" << id << " inserting pos=" << pS << " s=" << el.s << " iS=" << iS << " dist=" << pS.distanceTo2D(geom[iS]) << "\n";
1646  int at = geom.insertAtClosest(pS, false);
1647  double interpolatedOffset = 0;
1648  if (at == 0) {
1649  interpolatedOffset = result.front();
1650  } else if (at == (int)geom.size() - 1) {
1651  interpolatedOffset = result.back();
1652  } else {
1653  interpolatedOffset = (result[at - 1] + result[at]) / 2;
1654  }
1655  result.insert(result.begin() + at, interpolatedOffset);
1656  }
1657  }
1658  // shift each point orthogonally by the specified offset
1659  int kk = 0;
1660  double ppos = 0;
1661  const int sign = left ? -1 : 1;
1662  for (auto j = offsets.begin(); j != offsets.end(); ++j) {
1663  const OpenDriveWidth& el = *j;
1664  const double sNext = (j + 1) == offsets.end() ? std::numeric_limits<double>::max() : (*(j + 1)).s;
1665  while (kk < (int)geom.size() && ppos < sNext) {
1666  const double offset = el.computeAt(ppos);
1667  result[kk] += fabs(offset) > POSITION_EPS ? sign * offset : 0;
1668  kk++;
1669  if (kk < (int)geom.size()) {
1670  // XXX pos understimates the actual position since the
1671  // actual geometry between k-1 and k could be curved
1672  ppos += geom[kk - 1].distanceTo2D(geom[kk]);
1673  }
1674  }
1675  }
1676 }
1677 
1678 
1679 void
1680 NIImporter_OpenDrive::revisitLaneSections(const NBTypeCont& tc, std::map<std::string, OpenDriveEdge*>& edges) {
1681  for (std::map<std::string, OpenDriveEdge*>::iterator i = edges.begin(); i != edges.end(); ++i) {
1682  OpenDriveEdge& e = *(*i).second;
1683 #ifdef DEBUG_VARIABLE_SPEED
1684  if (DEBUG_COND(&e)) {
1685  gDebugFlag1 = true;
1686  std::cout << "revisitLaneSections e=" << e.id << "\n";
1687  }
1688 #endif
1689  std::vector<OpenDriveLaneSection>& laneSections = e.laneSections;
1690  // split by speed limits or by access restrictions
1691  std::vector<OpenDriveLaneSection> newSections;
1692  for (std::vector<OpenDriveLaneSection>::iterator j = laneSections.begin(); j != laneSections.end(); ++j) {
1693  std::vector<OpenDriveLaneSection> splitSections;
1694  bool splitByAttrChange = (*j).buildAttributeChanges(tc, splitSections);
1695  if (!splitByAttrChange) {
1696  newSections.push_back(*j);
1697  } else {
1698  std::copy(splitSections.begin(), splitSections.end(), back_inserter(newSections));
1699  }
1700  }
1701 
1702  e.laneSections = newSections;
1703  laneSections = e.laneSections;
1704  double lastS = -1;
1705  // check whether the lane sections are in the right order
1706  bool sorted = true;
1707  for (std::vector<OpenDriveLaneSection>::const_iterator j = laneSections.begin(); j != laneSections.end() && sorted; ++j) {
1708  if ((*j).s <= lastS) {
1709  sorted = false;
1710  }
1711  lastS = (*j).s;
1712  }
1713  if (!sorted) {
1714  WRITE_WARNINGF(TL("The sections of edge '%' are not sorted properly."), e.id);
1715  sort(e.laneSections.begin(), e.laneSections.end(), sections_by_s_sorter());
1716  }
1717  // check whether no duplicates of s-value occur
1718  lastS = -1;
1719  laneSections = e.laneSections;
1720  for (std::vector<OpenDriveLaneSection>::iterator j = laneSections.begin(); j != laneSections.end();) {
1721  bool simlarToLast = fabs((*j).s - lastS) < POSITION_EPS;
1722  lastS = (*j).s;
1723  // keep all lane sections for connecting roads because they are
1724  // needed to establish connectivity (laneSectionsConnected)
1725  if (simlarToLast && !e.isInner) {
1726  WRITE_WARNINGF(TL("Almost duplicate s-value '%' for lane sections occurred at edge '%'; second entry was removed."), toString(lastS), e.id);
1727  j = laneSections.erase(j);
1728  } else {
1729  ++j;
1730  }
1731  }
1732 #ifdef DEBUG_VARIABLE_SPEED
1733  gDebugFlag1 = false;
1734 #endif
1735  }
1736 }
1737 
1738 
1741  UNUSED_PARAMETER(e);
1742  PositionVector ret;
1743  Position start(g.x, g.y);
1744  Position end = calculateStraightEndPoint(g.hdg, g.length, start);
1745  if (resolution > 0 && g.length > 0) {
1746  const int numPoints = (int)ceil(g.length / resolution) + 1;
1747  double dx = (end.x() - start.x()) / (numPoints - 1);
1748  double dy = (end.y() - start.y()) / (numPoints - 1);
1749  for (int i = 0; i < numPoints; i++) {
1750  ret.push_back(Position(g.x + i * dx, g.y + i * dy));
1751  }
1752  } else {
1753  ret.push_back(start);
1754  ret.push_back(end);
1755  }
1756  return ret;
1757 }
1758 
1759 
1762  UNUSED_PARAMETER(e);
1763  PositionVector ret;
1764  double curveStart = g.params[0];
1765  double curveEnd = g.params[1];
1766  try {
1767  double cDot = (curveEnd - curveStart) / g.length;
1768  if (cDot == 0 || g.length == 0) {
1769  WRITE_WARNINGF(TL("Could not compute spiral geometry for edge '%' (cDot=% length=%)."), e.id, toString(cDot), toString(g.length));
1770  ret.push_back(Position(g.x, g.y));
1771  return ret;
1772  }
1773  double sStart = curveStart / cDot;
1774  double sEnd = curveEnd / cDot;
1775  double x = 0;
1776  double y = 0;
1777  double t = 0;
1778  double tStart = 0;
1779  double s;
1780  odrSpiral(sStart, cDot, &x, &y, &tStart);
1781  for (s = sStart; s <= sEnd; s += resolution) {
1782  odrSpiral(s, cDot, &x, &y, &t);
1783  ret.push_back(Position(x, y));
1784  }
1785  if (s != sEnd /*&& ret.size() == 1*/) {
1786  odrSpiral(sEnd, cDot, &x, &y, &t);
1787  ret.push_back(Position(x, y));
1788  }
1789  //if (s != sEnd && ret.size() > 2) {
1790  // ret.pop_back();
1791  //}
1792  assert(ret.size() >= 2);
1793  assert(ret[0] != ret[1]);
1794  // shift start to coordinate origin
1795  PositionVector ret1 = ret;
1796  ret.add(ret.front() * -1);
1797  // rotate
1798  PositionVector ret2 = ret;
1799  ret.rotate2D(g.hdg - tStart);
1800 #ifdef DEBUG_SPIRAL
1801  std::cout
1802  << std::setprecision(4)
1803  << "edge=" << e.id << " s=" << g.s
1804  << " cStart=" << curveStart
1805  << " cEnd=" << curveEnd
1806  << " cDot=" << cDot
1807  << " sStart=" << sStart
1808  << " sEnd=" << sEnd
1809  << " g.hdg=" << GeomHelper::naviDegree(g.hdg)
1810  << " tStart=" << GeomHelper::naviDegree(tStart)
1811  << "\n beforeShift=" << ret1
1812  << "\n beforeRot=" << ret2
1813  << "\n";
1814 #endif
1815  // shift to geometry start
1816  ret.add(g.x, g.y, 0);
1817  } catch (const std::runtime_error& error) {
1818  WRITE_WARNINGF(TL("Could not compute spiral geometry for edge '%' (%)."), e.id, error.what());
1819  ret.push_back(Position(g.x, g.y));
1820  }
1821  return ret.getSubpart2D(0, g.length);
1822 }
1823 
1824 
1827  UNUSED_PARAMETER(e);
1828  PositionVector ret;
1829  double centerX = g.x;
1830  double centerY = g.y;
1831  // left: positive value
1832  double curvature = g.params[0];
1833  double radius = 1. / curvature;
1834  // center point
1835  calculateCurveCenter(&centerX, &centerY, radius, g.hdg);
1836  double endX = g.x;
1837  double endY = g.y;
1838  double startX = g.x;
1839  double startY = g.y;
1840  double geo_posS = g.s;
1841  double geo_posE = g.s;
1842  bool end = false;
1843  do {
1844  geo_posE += resolution;
1845  if (geo_posE - g.s > g.length) {
1846  geo_posE = g.s + g.length;
1847  }
1848  if (geo_posE - g.s > g.length) {
1849  geo_posE = g.s + g.length;
1850  }
1851  calcPointOnCurve(&endX, &endY, centerX, centerY, radius, geo_posE - geo_posS);
1852  ret.push_back(Position(startX, startY));
1853 
1854  startX = endX;
1855  startY = endY;
1856  geo_posS = geo_posE;
1857 
1858  if (geo_posE - (g.s + g.length) < 0.001 && geo_posE - (g.s + g.length) > -0.001) {
1859  end = true;
1860  }
1861  } while (!end);
1862  ret.push_back(Position(startX, startY));
1863  return ret.getSubpart2D(0, g.length);
1864 }
1865 
1866 
1869  UNUSED_PARAMETER(e);
1870  const double s = sin(g.hdg);
1871  const double c = cos(g.hdg);
1872  PositionVector ret;
1873  for (double off = 0; off < g.length + 2.; off += resolution) {
1874  double x = off;
1875  double y = g.params[0] + g.params[1] * off + g.params[2] * pow(off, 2.) + g.params[3] * pow(off, 3.);
1876  double xnew = x * c - y * s;
1877  double ynew = x * s + y * c;
1878  ret.push_back(Position(g.x + xnew, g.y + ynew));
1879  }
1880  return ret.getSubpart2D(0, g.length);
1881 }
1882 
1883 
1886  UNUSED_PARAMETER(e);
1887  const double s = sin(g.hdg);
1888  const double c = cos(g.hdg);
1889  const double pMax = g.params[8] <= 0 ? g.length : g.params[8];
1890  const double pStep = pMax / ceil(g.length / resolution);
1891  PositionVector ret;
1892  for (double p = 0; p <= pMax + pStep; p += pStep) {
1893  double x = g.params[0] + g.params[1] * p + g.params[2] * pow(p, 2.) + g.params[3] * pow(p, 3.);
1894  double y = g.params[4] + g.params[5] * p + g.params[6] * pow(p, 2.) + g.params[7] * pow(p, 3.);
1895  double xnew = x * c - y * s;
1896  double ynew = x * s + y * c;
1897  ret.push_back(Position(g.x + xnew, g.y + ynew));
1898  }
1899  return ret.getSubpart2D(0, g.length);
1900 }
1901 
1902 
1903 Position
1904 NIImporter_OpenDrive::calculateStraightEndPoint(double hdg, double length, const Position& start) {
1905  double normx = 1.0f;
1906  double normy = 0.0f;
1907  double x2 = normx * cos(hdg) - normy * sin(hdg);
1908  double y2 = normx * sin(hdg) + normy * cos(hdg);
1909  normx = x2 * length;
1910  normy = y2 * length;
1911  return Position(start.x() + normx, start.y() + normy);
1912 }
1913 
1914 
1915 void
1916 NIImporter_OpenDrive::calculateCurveCenter(double* ad_x, double* ad_y, double ad_radius, double ad_hdg) {
1917  double normX = 1.0;
1918  double normY = 0.0;
1919  double tmpX;
1920  double turn;
1921  if (ad_radius > 0) {
1922  turn = -1.0;
1923  } else {
1924  turn = 1.0;
1925  }
1926 
1927  tmpX = normX;
1928  normX = normX * cos(ad_hdg) + normY * sin(ad_hdg);
1929  normY = tmpX * sin(ad_hdg) + normY * cos(ad_hdg);
1930 
1931  tmpX = normX;
1932  normX = turn * normY;
1933  normY = -turn * tmpX;
1934 
1935  normX = fabs(ad_radius) * normX;
1936  normY = fabs(ad_radius) * normY;
1937 
1938  *ad_x += normX;
1939  *ad_y += normY;
1940 }
1941 
1942 
1943 void
1944 NIImporter_OpenDrive::calcPointOnCurve(double* ad_x, double* ad_y, double ad_centerX, double ad_centerY,
1945  double ad_r, double ad_length) {
1946  double rotAngle = ad_length / fabs(ad_r);
1947  double vx = *ad_x - ad_centerX;
1948  double vy = *ad_y - ad_centerY;
1949  double tmpx;
1950 
1951  double turn;
1952  if (ad_r > 0) {
1953  turn = -1; //left
1954  } else {
1955  turn = 1; //right
1956  }
1957  tmpx = vx;
1958  vx = vx * cos(rotAngle) + turn * vy * sin(rotAngle);
1959  vy = -1 * turn * tmpx * sin(rotAngle) + vy * cos(rotAngle);
1960  *ad_x = vx + ad_centerX;
1961  *ad_y = vy + ad_centerY;
1962 }
1963 
1964 
1965 // ---------------------------------------------------------------------------
1966 // section
1967 // ---------------------------------------------------------------------------
1969  lanesByDir[OPENDRIVE_TAG_LEFT] = std::vector<OpenDriveLane>();
1970  lanesByDir[OPENDRIVE_TAG_RIGHT] = std::vector<OpenDriveLane>();
1971  lanesByDir[OPENDRIVE_TAG_CENTER] = std::vector<OpenDriveLane>();
1972 }
1973 
1974 
1975 void
1977  discardedInnerWidthRight = 0;
1978  int sumoLane = 0;
1979  bool singleType = true;
1980  std::vector<std::string> types;
1981  const std::vector<OpenDriveLane>& dirLanesR = lanesByDir.find(OPENDRIVE_TAG_RIGHT)->second;
1982  for (std::vector<OpenDriveLane>::const_reverse_iterator i = dirLanesR.rbegin(); i != dirLanesR.rend(); ++i) {
1983  if (myImportAllTypes || (tc.knows((*i).type) && !tc.getEdgeTypeShallBeDiscarded((*i).type))) {
1984  discardedInnerWidthRight = 0;
1985  laneMap[(*i).id] = sumoLane++;
1986  types.push_back((*i).type);
1987  if (types.front() != types.back()) {
1988  singleType = false;
1989  }
1990  } else {
1991  discardedInnerWidthRight += (*i).width;
1992  }
1993  }
1994  discardedInnerWidthLeft = 0;
1995  rightLaneNumber = sumoLane;
1996  rightType = sumoLane > 0 ? (singleType ? types.front() : joinToString(types, "|")) : "";
1997  sumoLane = 0;
1998  singleType = true;
1999  types.clear();
2000  const std::vector<OpenDriveLane>& dirLanesL = lanesByDir.find(OPENDRIVE_TAG_LEFT)->second;
2001  for (std::vector<OpenDriveLane>::const_iterator i = dirLanesL.begin(); i != dirLanesL.end(); ++i) {
2002  if (myImportAllTypes || (tc.knows((*i).type) && !tc.getEdgeTypeShallBeDiscarded((*i).type))) {
2003  discardedInnerWidthLeft = 0;
2004  laneMap[(*i).id] = sumoLane++;
2005  types.push_back((*i).type);
2006  if (types.front() != types.back()) {
2007  singleType = false;
2008  }
2009  } else {
2010  discardedInnerWidthLeft += (*i).width;
2011  }
2012  }
2013  leftLaneNumber = sumoLane;
2014  leftType = sumoLane > 0 ? (singleType ? types.front() : joinToString(types, "|")) : "";
2015 }
2016 
2017 
2018 std::map<int, int>
2020  std::map<int, int> ret;
2021  const std::vector<OpenDriveLane>& dirLanes = lanesByDir.find(dir)->second;
2022  for (std::vector<OpenDriveLane>::const_reverse_iterator i = dirLanes.rbegin(); i != dirLanes.rend(); ++i) {
2023  std::map<int, int>::const_iterator toP = laneMap.find((*i).id);
2024  if (toP == laneMap.end()) {
2025  // the current lane is not available in SUMO
2026  continue;
2027  }
2028  int to = (*toP).second;
2029  int from = UNSET_CONNECTION;
2030  if ((*i).predecessor != UNSET_CONNECTION) {
2031  from = (*i).predecessor;
2032  }
2033  if (from != UNSET_CONNECTION) {
2034  std::map<int, int>::const_iterator fromP = prev.laneMap.find(from);
2035  if (fromP != prev.laneMap.end()) {
2036  from = (*fromP).second;
2037  } else {
2038  from = UNSET_CONNECTION;
2039  }
2040  }
2041  if (from != UNSET_CONNECTION && to != UNSET_CONNECTION) {
2042  if (ret.find(from) != ret.end()) {
2043 // WRITE_WARNING(TL("double connection"));
2044  }
2045  if (dir == OPENDRIVE_TAG_LEFT) {
2046  std::swap(from, to);
2047  }
2048  ret[from] = to;
2049  } else {
2050 // WRITE_WARNING(TL("missing connection"));
2051  }
2052  }
2053  return ret;
2054 }
2055 
2056 
2059  OpenDriveLaneSection ret(*this);
2060  ret.s += startPos;
2061  for (int k = 0; k < (int)ret.lanesByDir[OPENDRIVE_TAG_RIGHT].size(); ++k) {
2063  l.speed = 0;
2064  l.permission = 0;
2065  std::vector<std::pair<double, LaneAttributeChange> >::const_iterator it = std::find_if(l.attributeChanges.begin(), l.attributeChanges.end(), same_position_finder(startPos));
2066  if (it != l.attributeChanges.end()) {
2067  l.speed = (*it).second.speed;
2068  l.permission = l.computePermission(tc, (*it).second.allowed, (*it).second.denied);
2069  }
2070  }
2071  for (int k = 0; k < (int)ret.lanesByDir[OPENDRIVE_TAG_LEFT].size(); ++k) {
2073  l.speed = 0;
2074  l.permission = 0;
2075  std::vector<std::pair<double, LaneAttributeChange> >::const_iterator it = std::find_if(l.attributeChanges.begin(), l.attributeChanges.end(), same_position_finder(startPos));
2076  if (it != l.attributeChanges.end()) {
2077  l.speed = (*it).second.speed;
2078  l.permission = l.computePermission(tc, (*it).second.allowed, (*it).second.denied);
2079  }
2080  }
2081  return ret;
2082 }
2083 
2084 
2086 NIImporter_OpenDrive::OpenDriveLane::computePermission(const NBTypeCont& tc, const std::vector<std::string>& allowed,
2087  const std::vector<std::string>& denied) const {
2088  SVCPermissions perms = tc.getEdgeTypePermissions(type);
2089  if (allowed.size() > 0 && denied.size() > 0) {
2090  WRITE_WARNING(TL("Will discard access settings as both denied and allowed classes have been specified."));
2091  } else if (allowed.size() > 0) {
2092  perms = SVC_IGNORING;
2093  for (const std::string& allow : allowed) {
2094  if (allow == "simulator") {
2095  perms = SVC_IGNORING;
2096  break;
2097  } else if (allow == "autonomousTraffic" || allow == "autonomous traffic" || allow == "throughTraffic") {
2098  perms = tc.getEdgeTypePermissions(type);
2099  break;
2100  } else if (allow == "pedestrian") {
2101  perms |= SVC_PEDESTRIAN;
2102  } else if (allow == "passengerCar") {
2103  perms |= SVC_PASSENGER;
2104  } else if (allow == "bus") {
2105  perms |= SVC_BUS;
2106  } else if (allow == "delivery") {
2107  perms |= SVC_DELIVERY;
2108  } else if (allow == "emergency") {
2109  perms |= SVC_EMERGENCY;
2110  } else if (allow == "taxi") {
2111  perms |= SVC_TAXI;
2112  } else if (allow == "bicycle") {
2113  perms |= SVC_BICYCLE;
2114  } else if (allow == "motorcycle") {
2115  perms |= SVC_MOTORCYCLE;
2116  } else if (allow == "truck" || allow == "trucks") {
2117  perms |= SVC_TRUCK;
2118  perms |= SVC_TRAILER;
2119  }
2120  }
2121  } else if (denied.size() > 0) {
2122  for (const std::string& deny : denied) {
2123  if (deny == "none") {
2124  perms = tc.getEdgeTypePermissions(type);
2125  break;
2126  } else if (deny == "autonomousTraffic" || deny == "autonomous traffic" || deny == "throughTraffic") {
2127  perms = SVC_IGNORING;
2128  break;
2129  } else if (deny == "pedestrian") {
2130  perms &= ~SVC_PEDESTRIAN;
2131  } else if (deny == "passengerCar") {
2132  perms &= ~SVC_PASSENGER;
2133  } else if (deny == "bus") {
2134  perms &= ~SVC_BUS;
2135  } else if (deny == "delivery") {
2136  perms &= ~SVC_DELIVERY;
2137  } else if (deny == "emergency") {
2138  perms &= ~SVC_EMERGENCY;
2139  } else if (deny == "taxi") {
2140  perms &= ~SVC_TAXI;
2141  } else if (deny == "bicycle") {
2142  perms &= ~SVC_BICYCLE;
2143  } else if (deny == "motorcycle") {
2144  perms &= ~SVC_MOTORCYCLE;
2145  } else if (deny == "truck" || deny == "trucks") {
2146  perms &= ~SVC_TRUCK;
2147  perms &= ~SVC_TRAILER;
2148  }
2149  }
2150  }
2151  return perms;
2152 }
2153 
2154 
2155 bool
2156 NIImporter_OpenDrive::OpenDriveLaneSection::buildAttributeChanges(const NBTypeCont& tc, std::vector<OpenDriveLaneSection>& newSections) {
2157  std::set<double> attributeChangePositions;
2158  // collect speed change and access restriction positions and apply initial values to the begin
2159  for (std::vector<OpenDriveLane>::iterator k = lanesByDir[OPENDRIVE_TAG_RIGHT].begin(); k != lanesByDir[OPENDRIVE_TAG_RIGHT].end(); ++k) {
2160  for (std::vector<std::pair<double, LaneAttributeChange> >::const_iterator l = (*k).attributeChanges.begin(); l != (*k).attributeChanges.end(); ++l) {
2161  attributeChangePositions.insert((*l).first);
2162  if ((*l).first == 0) {
2163  (*k).speed = (*l).second.speed;
2164  (*k).permission = (*k).computePermission(tc, (*l).second.allowed, (*l).second.denied);
2165  }
2166  }
2167  }
2168  for (std::vector<OpenDriveLane>::iterator k = lanesByDir[OPENDRIVE_TAG_LEFT].begin(); k != lanesByDir[OPENDRIVE_TAG_LEFT].end(); ++k) {
2169  for (std::vector<std::pair<double, LaneAttributeChange> >::const_iterator l = (*k).attributeChanges.begin(); l != (*k).attributeChanges.end(); ++l) {
2170  attributeChangePositions.insert((*l).first);
2171  if ((*l).first == 0) {
2172  (*k).speed = (*l).second.speed;
2173  (*k).permission = (*k).computePermission(tc, (*l).second.allowed, (*l).second.denied);
2174  }
2175  }
2176  }
2177 
2178  // do nothing if there is none
2179  if (attributeChangePositions.size() == 0) {
2180  return false;
2181  }
2182 
2183  if (*attributeChangePositions.begin() > 0) {
2184  attributeChangePositions.insert(0);
2185  }
2186 #ifdef DEBUG_VARIABLE_SPEED
2187  if (gDebugFlag1) std::cout
2188  << " buildSpeedChanges sectionStart=" << s
2189  << " speedChangePositions=" << joinToString(speedChangePositions, ", ")
2190  << "\n";
2191 #endif
2192  for (std::set<double>::iterator i = attributeChangePositions.begin(); i != attributeChangePositions.end(); ++i) {
2193  if (i == attributeChangePositions.begin()) {
2194  newSections.push_back(*this);
2195  } else {
2196  newSections.push_back(buildLaneSection(tc, *i));
2197  }
2198  }
2199  // propagate speeds and access restrictions
2200  for (int i = 0; i != (int)newSections.size(); ++i) {
2201  for (auto& k : newSections[i].lanesByDir) {
2202  for (int j = 0; j != (int)k.second.size(); ++j) {
2203  OpenDriveLane& l = k.second[j];
2204  if (l.speed == 0) {
2205  if (i > 0) {
2206  l.speed = newSections[i - 1].lanesByDir[k.first][j].speed;
2207  } else {
2208  tc.getEdgeTypeSpeed(l.type);
2209  }
2210  }
2211  if (l.permission == 0) {
2212  if (i > 0) {
2213  l.permission = newSections[i - 1].lanesByDir[k.first][j].permission;
2214  l.type = newSections[i - 1].lanesByDir[k.first][j].type;
2215  } else {
2217  }
2218  }
2219  }
2220  }
2221  }
2222  return true;
2223 }
2224 
2225 
2226 
2227 // ---------------------------------------------------------------------------
2228 // edge
2229 // ---------------------------------------------------------------------------
2230 int
2232  // for signal interpretations see https://de.wikipedia.org/wiki/Bildtafel_der_Verkehrszeichen_in_der_Bundesrepublik_Deutschland_seit_2013
2233  int prio = 1;
2234  for (std::vector<OpenDriveSignal>::const_iterator i = signals.begin(); i != signals.end(); ++i) {
2235  int tmp = 1;
2236  if ((*i).type == "301" || (*i).type == "306") { // priority road or local priority
2237  tmp = 2;
2238  }
2239  if ((*i).type == "205" /*|| (*i).type == "206"*/) { // yield or stop
2240  tmp = 0;
2241  }
2242  if (tmp != 1 && dir == OPENDRIVE_TAG_RIGHT && (*i).orientation > 0) {
2243  prio = tmp;
2244  }
2245  if (tmp != 1 && dir == OPENDRIVE_TAG_LEFT && (*i).orientation < 0) {
2246  prio = tmp;
2247  }
2248 
2249  }
2250  return prio;
2251 }
2252 
2253 
2254 
2255 // ---------------------------------------------------------------------------
2256 // loader methods
2257 // ---------------------------------------------------------------------------
2258 NIImporter_OpenDrive::NIImporter_OpenDrive(const NBTypeCont& tc, std::map<std::string, OpenDriveEdge*>& edges)
2260  myTypeContainer(tc), myCurrentEdge("", "", "", -1), myCurrentController("", ""), myEdges(edges), myOffset(0, 0),
2261  myUseCurrentNode(false) {
2262 }
2263 
2264 
2266 }
2267 
2268 
2269 void
2271  const SUMOSAXAttributes& attrs) {
2272  if (myUseCurrentNode) { // skip the parent node repeated in the included file
2273  myUseCurrentNode = false;
2274  myElementStack.push_back(element);
2275  return;
2276  }
2277  bool ok = true;
2278  switch (element) {
2279  case OPENDRIVE_TAG_HEADER: {
2280  int majorVersion = attrs.get<int>(OPENDRIVE_ATTR_REVMAJOR, nullptr, ok);
2281  int minorVersion = attrs.get<int>(OPENDRIVE_ATTR_REVMINOR, nullptr, ok);
2282  if (majorVersion == 1 && minorVersion > 4) { // disable flags only used for old 1.4 standard
2284  }
2285  /*
2286  if (majorVersion != 1 || minorVersion != 2) {
2287  // TODO: leave note of exceptions
2288  WRITE_WARNINGF(TL("Given openDrive file '%' uses version %.%;\n Version 1.2 is supported."), getFileName(), toString(majorVersion), toString(minorVersion));
2289  }
2290  */
2291  }
2292  break;
2293  case OPENDRIVE_TAG_OFFSET: {
2294  double x = attrs.get<double>(OPENDRIVE_ATTR_X, "offset", ok);
2295  double y = attrs.get<double>(OPENDRIVE_ATTR_Y, "offset", ok);
2296  myOffset.set(x, y);
2299  }
2300  }
2301  break;
2302  case OPENDRIVE_TAG_ROAD: {
2303  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, nullptr, ok);
2304  std::string streetName = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, nullptr, ok, "", false);
2305  std::string junction = attrs.get<std::string>(OPENDRIVE_ATTR_JUNCTION, id.c_str(), ok);
2306  double length = attrs.get<double>(OPENDRIVE_ATTR_LENGTH, id.c_str(), ok);
2307  myCurrentEdge = OpenDriveEdge(id, streetName, junction, length);
2308  }
2309  break;
2311  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_ROAD) {
2312  std::string elementType = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTTYPE, myCurrentEdge.id.c_str(), ok);
2313  std::string elementID = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTID, myCurrentEdge.id.c_str(), ok);
2314  std::string contactPoint = attrs.hasAttribute(OPENDRIVE_ATTR_CONTACTPOINT)
2315  ? attrs.get<std::string>(OPENDRIVE_ATTR_CONTACTPOINT, myCurrentEdge.id.c_str(), ok)
2316  : "end";
2317  addLink(OPENDRIVE_LT_PREDECESSOR, elementType, elementID, contactPoint);
2318  }
2319  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_LANE) {
2320  int no = attrs.get<int>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2321  OpenDriveLane& l = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back();
2322  l.predecessor = no;
2323  }
2324  }
2325  break;
2326  case OPENDRIVE_TAG_SUCCESSOR: {
2327  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_ROAD) {
2328  std::string elementType = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTTYPE, myCurrentEdge.id.c_str(), ok);
2329  std::string elementID = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTID, myCurrentEdge.id.c_str(), ok);
2330  std::string contactPoint = attrs.hasAttribute(OPENDRIVE_ATTR_CONTACTPOINT)
2331  ? attrs.get<std::string>(OPENDRIVE_ATTR_CONTACTPOINT, myCurrentEdge.id.c_str(), ok)
2332  : "start";
2333  addLink(OPENDRIVE_LT_SUCCESSOR, elementType, elementID, contactPoint);
2334  }
2335  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_LANE) {
2336  int no = attrs.get<int>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2337  OpenDriveLane& l = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back();
2338  l.successor = no;
2339  }
2340  }
2341  break;
2342  case OPENDRIVE_TAG_GEOMETRY: {
2343  double length = attrs.get<double>(OPENDRIVE_ATTR_LENGTH, myCurrentEdge.id.c_str(), ok);
2344  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2345  double x = attrs.get<double>(OPENDRIVE_ATTR_X, myCurrentEdge.id.c_str(), ok);
2346  double y = attrs.get<double>(OPENDRIVE_ATTR_Y, myCurrentEdge.id.c_str(), ok);
2347  double hdg = attrs.get<double>(OPENDRIVE_ATTR_HDG, myCurrentEdge.id.c_str(), ok);
2348  myCurrentEdge.geometries.push_back(OpenDriveGeometry(length, s, x, y, hdg));
2349  }
2350  break;
2351  case OPENDRIVE_TAG_ELEVATION: {
2352  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2353  double a = attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok);
2354  double b = attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok);
2355  double c = attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok);
2356  double d = attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok);
2357  myCurrentEdge.elevations.push_back(OpenDriveElevation(s, a, b, c, d));
2358  }
2359  break;
2360  case OPENDRIVE_TAG_LINE: {
2361  if (myElementStack.size() > 0 && myElementStack.back() == OPENDRIVE_TAG_GEOMETRY) {
2362  std::vector<double> vals;
2364  }
2365  }
2366  break;
2367  case OPENDRIVE_TAG_SPIRAL: {
2368  std::vector<double> vals;
2369  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CURVSTART, myCurrentEdge.id.c_str(), ok));
2370  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CURVEND, myCurrentEdge.id.c_str(), ok));
2372  }
2373  break;
2374  case OPENDRIVE_TAG_ARC: {
2375  std::vector<double> vals;
2376  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CURVATURE, myCurrentEdge.id.c_str(), ok));
2378  }
2379  break;
2380  case OPENDRIVE_TAG_POLY3: {
2381  std::vector<double> vals;
2382  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok));
2383  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok));
2384  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok));
2385  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok));
2387  }
2388  break;
2389  case OPENDRIVE_TAG_PARAMPOLY3: {
2390  std::vector<double> vals;
2391  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_AU, myCurrentEdge.id.c_str(), ok));
2392  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_BU, myCurrentEdge.id.c_str(), ok));
2393  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CU, myCurrentEdge.id.c_str(), ok));
2394  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_DU, myCurrentEdge.id.c_str(), ok));
2395  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_AV, myCurrentEdge.id.c_str(), ok));
2396  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_BV, myCurrentEdge.id.c_str(), ok));
2397  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CV, myCurrentEdge.id.c_str(), ok));
2398  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_DV, myCurrentEdge.id.c_str(), ok));
2399  const std::string pRange = attrs.getOpt<std::string>(OPENDRIVE_ATTR_PRANGE, myCurrentEdge.id.c_str(), ok, "normalized", false);
2400  if (pRange == "normalized") {
2401  vals.push_back(1.0);
2402  } else if (pRange == "arcLength") {
2403  vals.push_back(-1.0);
2404  } else {
2405  WRITE_WARNINGF(TL("Ignoring invalid pRange value '%' for road '%'."), pRange, myCurrentEdge.id);
2406  vals.push_back(1.0);
2407  }
2409  }
2410  break;
2412  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2413  if (myCurrentEdge.laneSections.size() > 0) {
2414  myCurrentEdge.laneSections.back().length = s - myCurrentEdge.laneSections.back().s;
2415  }
2417 
2418  // possibly updated by the next laneSection
2419  myCurrentEdge.laneSections.back().length = myCurrentEdge.length - s;
2420  }
2421  break;
2422  case OPENDRIVE_TAG_LANEOFFSET: {
2423  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2424  double a = attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok);
2425  double b = attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok);
2426  double c = attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok);
2427  double d = attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok);
2428  myCurrentEdge.offsets.push_back(OpenDriveLaneOffset(s, a, b, c, d));
2429  }
2430  break;
2431  case OPENDRIVE_TAG_LEFT:
2433  break;
2434  case OPENDRIVE_TAG_CENTER:
2436  break;
2437  case OPENDRIVE_TAG_RIGHT:
2439  break;
2440  case OPENDRIVE_TAG_LANE: {
2441  std::string type = attrs.get<std::string>(OPENDRIVE_ATTR_TYPE, myCurrentEdge.id.c_str(), ok);
2442  int id = attrs.get<int>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2443  std::string level = attrs.hasAttribute(OPENDRIVE_ATTR_LEVEL)
2444  ? attrs.get<std::string>(OPENDRIVE_ATTR_LEVEL, myCurrentEdge.id.c_str(), ok)
2445  : "";
2447  ls.lanesByDir[myCurrentLaneDirection].push_back(OpenDriveLane(id, level, type));
2448  }
2449  break;
2450  case OPENDRIVE_TAG_SIGNAL: {
2451  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2452  std::string type = attrs.get<std::string>(OPENDRIVE_ATTR_TYPE, myCurrentEdge.id.c_str(), ok);
2453  std::string name = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, myCurrentEdge.id.c_str(), ok, "", false);
2454  const std::string orientation = attrs.get<std::string>(OPENDRIVE_ATTR_ORIENTATION, myCurrentEdge.id.c_str(), ok);
2455  int orientationCode = orientation == "-" ? -1 : orientation == "+" ? 1 : 0;
2456  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2457  bool dynamic = attrs.get<std::string>(OPENDRIVE_ATTR_DYNAMIC, myCurrentEdge.id.c_str(), ok) == "no" ? false : true;
2458  OpenDriveSignal signal = OpenDriveSignal(id, type, name, orientationCode, dynamic, s);
2459  myCurrentEdge.signals.push_back(signal);
2460  mySignals[id] = signal;
2461  }
2462  break;
2464  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2465  const std::string orientation = attrs.get<std::string>(OPENDRIVE_ATTR_ORIENTATION, myCurrentEdge.id.c_str(), ok);
2466  int orientationCode = orientation == "-" ? -1 : orientation == "+" ? 1 : 0;
2467  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2468  OpenDriveSignal signal = OpenDriveSignal(id, "", "", orientationCode, false, s);
2469  myCurrentEdge.signals.push_back(signal);
2470  }
2471  break;
2472  case OPENDRIVE_TAG_CONTROLLER: {
2473  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, nullptr, ok);
2474  std::string name = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, nullptr, ok, "", false);
2476  }
2477  break;
2478  case OPENDRIVE_TAG_CONTROL: {
2479  std::string signalID = attrs.get<std::string>(OPENDRIVE_ATTR_SIGNALID, myCurrentController.id.c_str(), ok);
2480  myCurrentController.signalIDs.push_back(signalID);
2481  if (mySignals.find(signalID) != mySignals.end()) {
2482  mySignals[signalID].controller = myCurrentController.id;
2483  } else {
2484  WRITE_WARNINGF(TL("Ignoring missing signal '%' in controller '%'."), signalID, myCurrentController.id);
2485  }
2486  }
2487  break;
2488  case OPENDRIVE_TAG_VALIDITY: {
2489  int fromLane = attrs.get<int>(OPENDRIVE_ATTR_FROMLANE, myCurrentEdge.id.c_str(), ok);
2490  int toLane = attrs.get<int>(OPENDRIVE_ATTR_TOLANE, myCurrentEdge.id.c_str(), ok);
2491  if (myElementStack.size() >= 1 && (myElementStack.back() == OPENDRIVE_TAG_SIGNAL
2493  myCurrentEdge.signals.back().minLane = fromLane;
2494  myCurrentEdge.signals.back().maxLane = toLane;
2495  }
2496  }
2497  break;
2499  myCurrentJunctionID = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentJunctionID.c_str(), ok);
2500  break;
2501  case OPENDRIVE_TAG_CONNECTION: {
2502  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentJunctionID.c_str(), ok);
2503  myCurrentIncomingRoad = attrs.get<std::string>(OPENDRIVE_ATTR_INCOMINGROAD, myCurrentJunctionID.c_str(), ok);
2505  std::string cp = attrs.get<std::string>(OPENDRIVE_ATTR_CONTACTPOINT, myCurrentJunctionID.c_str(), ok);
2507  myConnectionWasEmpty = true;
2508  }
2509  break;
2510  case OPENDRIVE_TAG_LANELINK: {
2511  int from = attrs.get<int>(OPENDRIVE_ATTR_FROM, myCurrentJunctionID.c_str(), ok);
2512  int to = attrs.get<int>(OPENDRIVE_ATTR_TO, myCurrentJunctionID.c_str(), ok);
2513  Connection c;
2515  c.toEdge = myCurrentConnectingRoad;
2516  c.fromLane = from;
2517  c.toLane = to;
2518  c.fromCP = OPENDRIVE_CP_END;
2519  c.toCP = myCurrentContactPoint;
2520  c.all = false;
2521  if (myEdges.find(c.fromEdge) == myEdges.end()) {
2522  WRITE_ERRORF(TL("In laneLink-element: incoming road '%' is not known."), c.fromEdge);
2523  } else {
2524  OpenDriveEdge* e = myEdges.find(c.fromEdge)->second;
2525  e->connections.insert(c);
2526  myConnectionWasEmpty = false;
2527  }
2528  }
2529  break;
2530  case OPENDRIVE_TAG_WIDTH: {
2531  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 1] == OPENDRIVE_TAG_LANE) {
2532  const double s = attrs.get<double>(OPENDRIVE_ATTR_SOFFSET, myCurrentEdge.id.c_str(), ok);
2533  const double a = attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok);
2534  const double b = attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok);
2535  const double c = attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok);
2536  const double d = attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok);
2537  OpenDriveLane& l = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back();
2538  l.width = MAX2(l.width, a);
2539  l.widthData.push_back(OpenDriveWidth(s, a, b, c, d));
2540 #ifdef DEBUG_VARIABLE_WIDTHS
2541  if (DEBUG_COND(&myCurrentEdge)) {
2542  std::cout << " road=" << myCurrentEdge.id
2543  << std::setprecision(gPrecision)
2544  << " junction=" << myCurrentEdge.junction
2545  << " section=" << myCurrentEdge.laneSections.size() - 1
2546  << " dir=" << myCurrentLaneDirection << " lane=" << l.id
2547  << " type=" << l.type
2548  << " width=" << l.width
2549  << " a=" << a
2550  << " b=" << b
2551  << " c=" << c
2552  << " d=" << d
2553  << " s=" << s
2554  << " entries=" << l.widthData.size()
2555  << "\n";
2556  }
2557 #endif
2558  }
2559  }
2560  break;
2561  case OPENDRIVE_TAG_ACCESS: {
2562  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 1] == OPENDRIVE_TAG_LANE) {
2563  const double pos = attrs.get<double>(OPENDRIVE_ATTR_SOFFSET, myCurrentEdge.id.c_str(), ok);
2564  std::string rule = attrs.getOpt<std::string>(OPENDRIVE_ATTR_RULE, nullptr, ok, "allow", false); // OpenDRIVE 1.4 without rule value
2565  std::string vClass = attrs.get<std::string>(OPENDRIVE_ATTR_RESTRICTION, myCurrentEdge.id.c_str(), ok);
2566 
2567  std::vector < std::pair<double, LaneAttributeChange >>& attributeChanges = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back().attributeChanges;
2568  std::vector<std::pair<double, LaneAttributeChange> >::iterator i = std::find_if(attributeChanges.begin(), attributeChanges.end(), same_position_finder(pos));
2569  if (i != attributeChanges.end()) {
2570  if (rule == "allow") {
2571  (*i).second.allowed.push_back(vClass);
2572  } else if (rule == "deny") {
2573  (*i).second.denied.push_back(vClass);
2574  }
2575  } else {
2577  if (rule == "allow") {
2578  lac.allowed.push_back(vClass);
2579  } else if (rule == "deny") {
2580  lac.denied.push_back(vClass);
2581  }
2582  attributeChanges.push_back(std::make_pair(pos, lac));
2583  }
2584  }
2585  }
2586  break;
2587  case OPENDRIVE_TAG_SPEED: {
2588  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 1] == OPENDRIVE_TAG_LANE) {
2589  double speed = attrs.get<double>(OPENDRIVE_ATTR_MAX, myCurrentEdge.id.c_str(), ok);
2590  double pos = attrs.get<double>(OPENDRIVE_ATTR_SOFFSET, myCurrentEdge.id.c_str(), ok);
2591  // required for xodr v1.4
2592  const std::string unit = attrs.getOpt<std::string>(OPENDRIVE_ATTR_UNIT, myCurrentEdge.id.c_str(), ok, "", false);
2593  // now convert the speed to reasonable default SI [m/s]
2594  if (!unit.empty()) {
2595  // something to be done at all ?
2596  if (unit == "km/h") {
2597  speed /= 3.6;
2598  }
2599  if (unit == "mph") {
2600  speed *= 1.609344 / 3.6;
2601  }
2602  // IGNORING unknown units.
2603  }
2604  std::vector < std::pair<double, LaneAttributeChange >>& attributeChanges = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back().attributeChanges;
2605  std::vector<std::pair<double, LaneAttributeChange> >::iterator i = std::find_if(attributeChanges.begin(), attributeChanges.end(), same_position_finder(pos));
2606  if (i != attributeChanges.end()) {
2607  (*i).second.speed = speed;
2608  } else {
2610  attributeChanges.push_back(std::make_pair(pos, lac));
2611  }
2612  }
2613  }
2614  break;
2615  case OPENDRIVE_TAG_OBJECT: {
2616  if (!attrs.hasAttribute(OPENDRIVE_ATTR_ID)) {
2617  WRITE_WARNINGF(TL("Ignoring object without id at edge '%'."), toString(myCurrentEdge.id));
2618  break;
2619  }
2620  OpenDriveObject o;
2621  o.id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, 0, ok);
2622  o.type = attrs.getOpt<std::string>(OPENDRIVE_ATTR_TYPE, o.id.c_str(), ok, "", false);
2623  o.name = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, o.id.c_str(), ok, "", false);
2624  o.s = attrs.get<double>(OPENDRIVE_ATTR_S, o.id.c_str(), ok);
2625  o.t = attrs.get<double>(OPENDRIVE_ATTR_T, o.id.c_str(), ok);
2626  o.width = attrs.getOpt<double>(OPENDRIVE_ATTR_WIDTH, o.id.c_str(), ok, -1);
2627  o.length = attrs.getOpt<double>(OPENDRIVE_ATTR_LENGTH, o.id.c_str(), ok, -1);
2628  o.radius = attrs.getOpt<double>(OPENDRIVE_ATTR_RADIUS, o.id.c_str(), ok, -1);
2629  o.hdg = attrs.getOpt<double>(OPENDRIVE_ATTR_HDG, o.id.c_str(), ok, 0);
2630  myCurrentEdge.objects.push_back(o);
2631  }
2632  break;
2633  case OPENDRIVE_TAG_REPEAT: {
2634  if (myCurrentEdge.objects.empty()) {
2635  WRITE_ERRORF(TL("Repeat without object at edge '%'."), toString(myCurrentEdge.id));
2636  ok = false;
2637  } else {
2639  const std::string baseID = o.id;
2640  double dist = attrs.get<double>(OPENDRIVE_ATTR_DISTANCE, o.id.c_str(), ok);
2641  if (dist == 0) {
2642  // continuous feature. Split into parts (XXX exmport as a single polygon #5235)
2643  dist = OptionsCont::getOptions().getFloat("opendrive.curve-resolution");
2644  }
2645 
2646  myCurrentEdge.objects.pop_back();
2647  const double length = attrs.get<double>(OPENDRIVE_ATTR_LENGTH, o.id.c_str(), ok);
2648  o.s = attrs.getOpt<double>(OPENDRIVE_ATTR_S, o.id.c_str(), ok, o.s);
2649  double wStart = attrs.getOpt<double>(OPENDRIVE_ATTR_WIDTHSTART, o.id.c_str(), ok, o.width);
2650  double wEnd = attrs.getOpt<double>(OPENDRIVE_ATTR_WIDTHEND, o.id.c_str(), ok, o.width);
2651  double tStart = attrs.getOpt<double>(OPENDRIVE_ATTR_TSTART, o.id.c_str(), ok, o.t);
2652  double tEnd = attrs.getOpt<double>(OPENDRIVE_ATTR_TEND, o.id.c_str(), ok, o.t);
2653  int index = 0;
2654  for (double x = 0; x <= length + NUMERICAL_EPS; x += dist) {
2655  o.id = baseID + "#" + toString(index++);
2656  const double a = x / length;
2657  o.width = wStart * (1 - a) + wEnd * a;
2658  o.t = tStart * (1 - a) + tEnd * a;
2659  myCurrentEdge.objects.push_back(o);
2660  o.s += dist;
2661  }
2662  }
2663  }
2664  break;
2665  case OPENDRIVE_TAG_INCLUDE: {
2666  std::string includedFile = attrs.get<std::string>(OPENDRIVE_ATTR_FILE, 0, ok);
2667  if (!FileHelpers::isAbsolute(includedFile)) {
2668  includedFile = FileHelpers::getConfigurationRelative(getFileName(), includedFile);
2669  }
2670  PROGRESS_BEGIN_MESSAGE("Parsing included opendrive from '" + includedFile + "'");
2671  myUseCurrentNode = true;
2672  XMLSubSys::runParser(*this, includedFile);
2674  }
2675  break;
2676  default:
2677  break;
2678  }
2679  myElementStack.push_back(element);
2680 }
2681 
2682 
2683 void
2684 NIImporter_OpenDrive::myCharacters(int element, const std::string& cdata) {
2685  if (element == OPENDRIVE_TAG_GEOREFERENCE) {
2686  size_t i = cdata.find("+proj");
2687  if (i != std::string::npos) {
2688  const std::string proj = cdata.substr(i);
2689  if (proj != "") {
2690  GeoConvHelper* result = nullptr;
2691  Boundary convBoundary;
2692  Boundary origBoundary;
2693  // XXX read values from the header
2694  convBoundary.add(Position(0, 0));
2695  origBoundary.add(Position(0, 0));
2696  try {
2697  result = new GeoConvHelper(proj, myOffset, origBoundary, convBoundary);
2698  GeoConvHelper::setLoaded(*result);
2699  } catch (ProcessError& e) {
2700  WRITE_ERRORF(TL("Could not set projection (%). This can be ignored with --ignore-errors."), std::string(e.what()));
2701  }
2702  }
2703  } else {
2704  WRITE_WARNINGF(TL("geoReference format '%' currently not supported"), cdata);
2705  }
2706  needsCharacterData(false);
2707  }
2708 }
2709 
2710 
2711 void
2713  myElementStack.pop_back();
2714  switch (element) {
2715  case OPENDRIVE_TAG_ROAD:
2717  break;
2719  if (myConnectionWasEmpty) {
2720  Connection c;
2723  c.fromLane = 0;
2724  c.toLane = 0;
2727  c.all = true;
2728  if (myEdges.find(c.fromEdge) == myEdges.end()) {
2729  WRITE_ERRORF(TL("In laneLink-element: incoming road '%' is not known."), c.fromEdge);
2730  } else {
2731  OpenDriveEdge* e = myEdges.find(c.fromEdge)->second;
2732  e->connections.insert(c);
2733  }
2734  }
2735  break;
2736  case OPENDRIVE_TAG_CONTROLLER: {
2738  }
2739  break;
2741  myCurrentEdge.laneSections.back().buildLaneMapping(myTypeContainer);
2742  }
2743  break;
2744  case OPENDRIVE_TAG_SIGNAL:
2747  int intType = -1;
2748  try {
2749  intType = StringUtils::toInt(myCurrentEdge.signals.back().type);
2750  } catch (NumberFormatException&) {
2751  break;
2752  } catch (EmptyData&) {
2753  break;
2754  }
2755  if (intType < 1000001 || (intType > 1000013 && intType != 1000020) || intType == 1000008) {
2756  // not a traffic_light (Section 6.11)
2757  break;
2758  }
2759  double s = myCurrentEdge.signals.back().s;
2760  int minLane = myCurrentEdge.signals.back().minLane;
2761  int maxLane = myCurrentEdge.signals.back().maxLane;
2762  bool foundDrivingType = false;
2764  if (ls.s <= s && ls.s + ls.length > s) {
2765  if (myCurrentEdge.signals.back().orientation < 0) {
2767  if ((minLane < 0 && l.id >= minLane && l.id <= maxLane) && l.type == "driving") {
2768  foundDrivingType = true;
2769  }
2770  }
2771  } else if (myCurrentEdge.signals.back().orientation > 0) { // 0 = center is never used for driving
2773  if ((minLane > 0 && l.id >= minLane && l.id <= maxLane) && l.type == "driving") {
2774  foundDrivingType = true;
2775  }
2776  }
2777  }
2778  }
2779  }
2780  if (!foundDrivingType) { // reject signal / signal reference if not on driving lane
2781  myCurrentEdge.signals.pop_back();
2782  }
2783  }
2784  }
2785  break;
2786  default:
2787  break;
2788  }
2789 }
2790 
2791 
2792 
2793 void
2794 NIImporter_OpenDrive::addLink(LinkType lt, const std::string& elementType,
2795  const std::string& elementID,
2796  const std::string& contactPoint) {
2797  OpenDriveLink l(lt, elementID);
2798  // elementType
2799  if (elementType == "road") {
2801  } else if (elementType == "junction") {
2803  }
2804  // contact point
2805  if (contactPoint == "start") {
2807  } else if (contactPoint == "end") {
2809  }
2810  // add
2811  myCurrentEdge.links.push_back(l);
2812 }
2813 
2814 
2815 void
2816 NIImporter_OpenDrive::addGeometryShape(GeometryType type, const std::vector<double>& vals) {
2817  // checks
2818  if (myCurrentEdge.geometries.size() == 0) {
2819  throw ProcessError(TLF("Mismatching parenthesis in geometry definition for road '%'", myCurrentEdge.id));
2820  }
2822  if (last.type != OPENDRIVE_GT_UNKNOWN) {
2823  throw ProcessError(TLF("Double geometry information for road '%'", myCurrentEdge.id));
2824  }
2825  // set
2826  last.type = type;
2827  last.params = vals;
2828 }
2829 
2830 
2831 bool
2833  if (c1.fromEdge != c2.fromEdge) {
2834  return c1.fromEdge < c2.fromEdge;
2835  }
2836  if (c1.toEdge != c2.toEdge) {
2837  return c1.toEdge < c2.toEdge;
2838  }
2839  if (c1.fromLane != c2.fromLane) {
2840  return c1.fromLane < c2.fromLane;
2841  }
2842  return c1.toLane < c2.toLane;
2843 }
2844 
2845 void
2847 #ifdef DEBUG_VARIABLE_WIDTHS
2848  if (DEBUG_COND(e)) {
2849  gDebugFlag1 = true;
2850  std::cout << "sanitizeWidths e=" << e->id << " sections=" << e->laneSections.size() << "\n";
2851  }
2852 #endif
2853  for (OpenDriveLaneSection& sec : e->laneSections) {
2854  // filter widths within the current section (#5888).
2855  // @note, Short laneSections could also be worth filtering alltogether
2856  if (sec.rightLaneNumber > 0) {
2858  }
2859  if (sec.leftLaneNumber > 0) {
2861  }
2862  }
2863 }
2864 
2865 void
2866 NIImporter_OpenDrive::sanitizeWidths(std::vector<OpenDriveLane>& lanes, double length) {
2867  for (OpenDriveLane& l : lanes) {
2868  if (l.widthData.size() > 0) {
2869  auto& wd = l.widthData;
2870  const double threshold = POSITION_EPS;
2871  double maxNoShort = -std::numeric_limits<double>::max();
2872  double seen = 0;
2873  for (int i = 0; i < (int)wd.size(); i++) {
2874  const double wdLength = i < (int)wd.size() - 1 ? wd[i + 1].s - wd[i].s : length - seen;
2875  seen += wdLength;
2876  if (wdLength > threshold) {
2877  maxNoShort = MAX2(maxNoShort, wd[i].a);
2878  }
2879  }
2880  if (maxNoShort > 0) {
2881  l.width = maxNoShort;
2882  }
2883  }
2884  }
2885 }
2886 
2887 
2888 void
2890  std::vector<OpenDriveLaneSection> newSections;
2891 #ifdef DEBUG_VARIABLE_WIDTHS
2892  if (DEBUG_COND(e)) {
2893  gDebugFlag1 = true;
2894  std::cout << "splitMinWidths e=" << e->id << " sections=" << e->laneSections.size() << "\n";
2895  }
2896 #endif
2897  for (std::vector<OpenDriveLaneSection>::iterator j = e->laneSections.begin(); j != e->laneSections.end(); ++j) {
2898  OpenDriveLaneSection& sec = *j;
2899  std::vector<double> splitPositions;
2900  const double sectionEnd = (j + 1) == e->laneSections.end() ? e->length : (*(j + 1)).s;
2901  const int section = (int)(j - e->laneSections.begin());
2902 #ifdef DEBUG_VARIABLE_WIDTHS
2903  if (DEBUG_COND(e)) {
2904  std::cout << " findWidthSplit section=" << section << " sectionStart=" << sec.s << " sectionOrigStart=" << sec.sOrig << " sectionEnd=" << sectionEnd << "\n";
2905  }
2906 #endif
2907  if (sec.rightLaneNumber > 0) {
2908  findWidthSplit(tc, sec.lanesByDir[OPENDRIVE_TAG_RIGHT], section, sec.sOrig, sectionEnd, splitPositions);
2909  }
2910  if (sec.leftLaneNumber > 0) {
2911  findWidthSplit(tc, sec.lanesByDir[OPENDRIVE_TAG_LEFT], section, sec.sOrig, sectionEnd, splitPositions);
2912  }
2913  newSections.push_back(sec);
2914  std::sort(splitPositions.begin(), splitPositions.end());
2915  // filter out tiny splits
2916  double prevSplit = sec.s;
2917  for (std::vector<double>::iterator it = splitPositions.begin(); it != splitPositions.end();) {
2918  if ((*it) - prevSplit < minDist || sectionEnd - (*it) < minDist) {
2919  // avoid tiny (or duplicate) splits
2920 #ifdef DEBUG_VARIABLE_WIDTHS
2921  if (DEBUG_COND(e)) {
2922  std::cout << " skip close split=" << (*it) << " prevSplit=" << prevSplit << "\n";
2923  }
2924 #endif
2925  it = splitPositions.erase(it);
2926  } else if ((*it) < sec.s) {
2927  // avoid splits for another section
2928 #ifdef DEBUG_VARIABLE_WIDTHS
2929  if (DEBUG_COND(e)) {
2930  std::cout << " skip early split=" << (*it) << " s=" << sec.s << "\n";
2931  }
2932 #endif
2933  it = splitPositions.erase(it);
2934  } else {
2935  prevSplit = *it;
2936  it++;
2937  }
2938  }
2939 
2940  if (splitPositions.size() > 0) {
2941 #ifdef DEBUG_VARIABLE_WIDTHS
2942  if (DEBUG_COND(e)) {
2943  std::cout << " road=" << e->id << " splitMinWidths section=" << section
2944  << " start=" << sec.s
2945  << " origStart=" << sec.sOrig
2946  << " end=" << sectionEnd << " minDist=" << minDist
2947  << " splitPositions=" << toString(splitPositions) << "\n";
2948  }
2949 #endif
2950 #ifdef DEBUG_VARIABLE_WIDTHS
2951  if (DEBUG_COND(e)) {
2952  std::cout << "first section...\n";
2953  }
2954 #endif
2955  recomputeWidths(newSections.back(), sec.sOrig, splitPositions.front(), sec.sOrig, sectionEnd);
2956  for (std::vector<double>::iterator it = splitPositions.begin(); it != splitPositions.end(); ++it) {
2957  OpenDriveLaneSection secNew = sec;
2958  secNew.s = *it;
2959 #ifdef DEBUG_VARIABLE_WIDTHS
2960  if (DEBUG_COND(e)) {
2961  std::cout << "splitAt " << secNew.s << "\n";
2962  }
2963 #endif
2964  newSections.push_back(secNew);
2965  if (secNew.rightLaneNumber > 0) {
2966  setStraightConnections(newSections.back().lanesByDir[OPENDRIVE_TAG_RIGHT]);
2967  }
2968  if (secNew.leftLaneNumber > 0) {
2969  setStraightConnections(newSections.back().lanesByDir[OPENDRIVE_TAG_LEFT]);
2970  }
2971  double end = (it + 1) == splitPositions.end() ? sectionEnd : *(it + 1);
2972  recomputeWidths(newSections.back(), secNew.s, end, sec.sOrig, sectionEnd);
2973  }
2974  }
2975  }
2976  gDebugFlag1 = false;
2977  e->laneSections = newSections;
2978 }
2979 
2980 
2981 void
2982 NIImporter_OpenDrive::findWidthSplit(const NBTypeCont& tc, std::vector<OpenDriveLane>& lanes,
2983  int section, double sectionStart, double sectionEnd,
2984  std::vector<double>& splitPositions) {
2985  UNUSED_PARAMETER(section);
2986  for (std::vector<OpenDriveLane>::iterator k = lanes.begin(); k != lanes.end(); ++k) {
2987  OpenDriveLane& l = *k;
2989  if (l.widthData.size() > 0 && tc.knows(l.type) && !tc.getEdgeTypeShallBeDiscarded(l.type) && permissions != 0) {
2990  double sPrev = l.widthData.front().s;
2991  double wPrev = l.widthData.front().computeAt(sPrev);
2992  if (gDebugFlag1) std::cout
2993  << "findWidthSplit section=" << section
2994  << " sectionStart=" << sectionStart
2995  << " sectionEnd=" << sectionEnd
2996  << " lane=" << l.id
2997  << " type=" << l.type
2998  << " widthEntries=" << l.widthData.size() << "\n"
2999  << " s=" << sPrev
3000  << " w=" << wPrev
3001  << "\n";
3002  for (std::vector<OpenDriveWidth>::iterator it_w = l.widthData.begin(); it_w != l.widthData.end(); ++it_w) {
3003  double sEnd = (it_w + 1) != l.widthData.end() ? (*(it_w + 1)).s : sectionEnd - sectionStart;
3004  double w = (*it_w).computeAt(sEnd);
3005  if (gDebugFlag1) std::cout
3006  << " sEnd=" << sEnd
3007  << " s=" << (*it_w).s
3008  << " a=" << (*it_w).a << " b=" << (*it_w).b << " c=" << (*it_w).c << " d=" << (*it_w).d
3009  << " w=" << w
3010  << "\n";
3011  const double changeDist = fabs(myMinWidth - wPrev);
3012  if (((wPrev < myMinWidth) && (w > myMinWidth))
3013  || ((wPrev > myMinWidth) && (w < myMinWidth))) {
3014  double splitPos = sPrev + (sEnd - sPrev) / fabs(w - wPrev) * changeDist;
3015  double wSplit = (*it_w).computeAt(splitPos);
3016  if (gDebugFlag1) {
3017  std::cout << " candidate splitPos=" << splitPos << " w=" << wSplit << "\n";
3018  }
3019  // ensure that the thin part is actually thin enough
3020  while (wSplit > myMinWidth) {
3021  if (wPrev < myMinWidth) {
3022  // getting wider
3023  splitPos -= POSITION_EPS;
3024  if (splitPos < sPrev) {
3025  if (gDebugFlag1) {
3026  std::cout << " aborting search splitPos=" << splitPos << " wSplit=" << wSplit << " sPrev=" << sPrev << " wPrev=" << wPrev << "\n";
3027  }
3028  splitPos = sPrev;
3029  break;
3030  }
3031  } else {
3032  // getting thinner
3033  splitPos += POSITION_EPS;
3034  if (splitPos > sEnd) {
3035  if (gDebugFlag1) {
3036  std::cout << " aborting search splitPos=" << splitPos << " wSplit=" << wSplit << " sEnd=" << sEnd << " w=" << w << "\n";
3037  }
3038  splitPos = sEnd;
3039  break;
3040  }
3041  }
3042  wSplit = (*it_w).computeAt(splitPos);
3043  if (gDebugFlag1) {
3044  std::cout << " refined splitPos=" << splitPos << " w=" << wSplit << "\n";
3045  }
3046  }
3047  splitPositions.push_back(sectionStart + splitPos);
3048  }
3049  // //wPrev = wSplit;
3050  //} else if ((fabs(wPrev) < NUMERICAL_EPS && w > POSITION_EPS)
3051  // || (wPrev > POSITION_EPS && fabs(w) < NUMERICAL_EPS)) {
3052  // splitPositions.push_back(sectionStart + sPrev);
3053  // if (gDebugFlag1) std::cout << " laneDisappears candidate splitPos=" << sPrev << " wPrev=" << wPrev << " w=" << w<< "\n";
3054  //}
3055  wPrev = w;
3056  sPrev = sEnd;
3057  }
3058  }
3059  }
3060 }
3061 
3062 
3063 void
3064 NIImporter_OpenDrive::setStraightConnections(std::vector<OpenDriveLane>& lanes) {
3065  for (std::vector<OpenDriveLane>::iterator k = lanes.begin(); k != lanes.end(); ++k) {
3066  (*k).predecessor = (*k).id;
3067  }
3068 }
3069 
3070 
3071 void
3072 NIImporter_OpenDrive::recomputeWidths(OpenDriveLaneSection& sec, double start, double end, double sectionStart, double sectionEnd) {
3073  if (sec.rightLaneNumber > 0) {
3074  recomputeWidths(sec.lanesByDir[OPENDRIVE_TAG_RIGHT], start, end, sectionStart, sectionEnd);
3075  }
3076  if (sec.leftLaneNumber > 0) {
3077  recomputeWidths(sec.lanesByDir[OPENDRIVE_TAG_LEFT], start, end, sectionStart, sectionEnd);
3078  }
3079 }
3080 
3081 
3082 void
3083 NIImporter_OpenDrive::recomputeWidths(std::vector<OpenDriveLane>& lanes, double start, double end, double sectionStart, double sectionEnd) {
3084  for (std::vector<OpenDriveLane>::iterator k = lanes.begin(); k != lanes.end(); ++k) {
3085  OpenDriveLane& l = *k;
3086  if (l.widthData.size() > 0) {
3087 #ifdef DEBUG_VARIABLE_WIDTHS
3088  if (gDebugFlag1) std::cout
3089  << "recomputeWidths lane=" << l.id
3090  << " type=" << l.type
3091  << " start=" << start
3092  << " end=" << end
3093  << " sectionStart=" << sectionStart
3094  << " sectionEnd=" << sectionEnd
3095  << " widthEntries=" << l.widthData.size() << "\n"
3096  << "\n";
3097 #endif
3098  l.width = 0;
3099  double sPrev = l.widthData.front().s;
3100  double sPrevAbs = sPrev + sectionStart;
3101  for (std::vector<OpenDriveWidth>::iterator it_w = l.widthData.begin(); it_w != l.widthData.end(); ++it_w) {
3102  double sEnd = (it_w + 1) != l.widthData.end() ? (*(it_w + 1)).s : sectionEnd - sectionStart;
3103  double sEndAbs = sEnd + sectionStart;
3104 #ifdef DEBUG_VARIABLE_WIDTHS
3105  if (gDebugFlag1) std::cout
3106  << " sPrev=" << sPrev << " sPrevAbs=" << sPrevAbs
3107  << " sEnd=" << sEnd << " sEndAbs=" << sEndAbs
3108  << " widthData s=" << (*it_w).s
3109  << " a=" << (*it_w).a
3110  << " b=" << (*it_w).b
3111  << " c=" << (*it_w).c
3112  << " d=" << (*it_w).d
3113  << "\n";
3114 #endif
3115  if (sPrevAbs <= start && sEndAbs >= start) {
3116 #ifdef DEBUG_VARIABLE_WIDTHS
3117  if (gDebugFlag1) {
3118  std::cout << " atStart=" << start << " pos=" << start - sectionStart << " w=" << (*it_w).computeAt(start - sectionStart) << "\n";
3119  }
3120 #endif
3121  l.width = MAX2(l.width, (*it_w).computeAt(start - sectionStart));
3122  }
3123  if (sPrevAbs <= end && sEndAbs >= end) {
3124 #ifdef DEBUG_VARIABLE_WIDTHS
3125  if (gDebugFlag1) {
3126  std::cout << " atEnd=" << end << " pos=" << end - sectionStart << " w=" << (*it_w).computeAt(end - sectionStart) << "\n";
3127  }
3128 #endif
3129  l.width = MAX2(l.width, (*it_w).computeAt(end - sectionStart));
3130  }
3131  if (start <= sPrevAbs && end >= sPrevAbs) {
3132 #ifdef DEBUG_VARIABLE_WIDTHS
3133  if (gDebugFlag1) {
3134  std::cout << " atSPrev=" << sPrev << " w=" << (*it_w).computeAt(sPrev) << "\n";
3135  }
3136 #endif
3137  l.width = MAX2(l.width, (*it_w).computeAt(sPrev));
3138  }
3139  if (start <= sEndAbs && end >= sEndAbs) {
3140 #ifdef DEBUG_VARIABLE_WIDTHS
3141  if (gDebugFlag1) {
3142  std::cout << " atSEnd=" << sEnd << " w=" << (*it_w).computeAt(sEnd) << "\n";
3143  }
3144 #endif
3145  l.width = MAX2(l.width, (*it_w).computeAt(sEnd));
3146  }
3147 #ifdef DEBUG_VARIABLE_WIDTHS
3148  if (gDebugFlag1) {
3149  std::cout << " sPrev=" << sPrev << " sEnd=" << sEnd << " l.width=" << l.width << "\n";
3150  }
3151 #endif
3152  sPrev = sEnd;
3153  sPrevAbs = sEndAbs;
3154  }
3155  }
3156  }
3157 }
3158 
3159 /****************************************************************************/
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:295
#define TL(string)
Definition: MsgHandler.h:315
#define PROGRESS_DONE_MESSAGE()
Definition: MsgHandler.h:300
#define TLF(string,...)
Definition: MsgHandler.h:317
#define PROGRESS_BEGIN_MESSAGE(msg)
Definition: MsgHandler.h:299
std::set< NBNode *, ComparatorIdLess > NodeSet
Definition: NBCont.h:52
@ KEEPCLEAR_UNSPECIFIED
Definition: NBCont.h:61
#define DEBUG_COND3(roadID)
#define DEBUG_COND(road)
bool operator<(const NIImporter_OpenDrive::Connection &c1, const NIImporter_OpenDrive::Connection &c2)
#define DEBUG_COND2(edgeID)
#define UNSET_CONNECTION
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ SVC_TRUCK
vehicle is a large transport vehicle
@ SVC_IGNORING
vehicles ignoring classes
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_TRAILER
vehicle is a large transport vehicle
@ SVC_DELIVERY
vehicle is a small delivery vehicle
@ SVC_MOTORCYCLE
vehicle is a motorcycle
@ SVC_EMERGENCY
public emergency vehicles
@ SVC_AUTHORITY
authorities vehicles
@ SVC_TAXI
vehicle is a taxi
@ SVC_BUS
vehicle is a bus
@ SVC_PEDESTRIAN
pedestrian
TrafficLightType
const std::string SUMO_PARAM_ORIGID
int gPrecision
the precision for floating point outputs
Definition: StdDefs.cpp:26
bool gDebugFlag1
global utility flags for debugging
Definition: StdDefs.cpp:37
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:30
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:283
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
void add(double x, double y, double z=0)
Makes the boundary include the given coordinate.
Definition: Boundary.cpp:78
static bool isAbsolute(const std::string &path)
Returns the information whether the given path is absolute.
static std::string getConfigurationRelative(const std::string &configPath, const std::string &path)
Returns the second path as a relative path to the first file.
A handler which converts occurring elements and attributes into enums.
void needsCharacterData(const bool value=true)
void error(const XERCES_CPP_NAMESPACE::SAXParseException &exception)
Handler for XML-errors.
void setFileName(const std::string &name)
Sets the current file name.
const std::string & getFileName() const
returns the current file name
static methods for processing the coordinates conversion for the current net
Definition: GeoConvHelper.h:53
void cartesian2geo(Position &cartesian) const
Converts the given cartesian (shifted) position to its geo (lat/long) representation.
void moveConvertedBy(double x, double y)
Shifts the converted boundary by the given amounts.
static GeoConvHelper & getLoaded()
the coordinate transformation that was loaded fron an input file
Definition: GeoConvHelper.h:89
static int getNumLoaded()
Definition: GeoConvHelper.h:93
static void setLoaded(const GeoConvHelper &loaded)
sets the coordinate transformation loaded from a location element
bool usingGeoProjection() const
Returns whether a transformation from geo to metric coordinates will be performed.
static double naviDegree(const double angle)
Definition: GeomHelper.cpp:191
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
Definition: NBEdgeCont.cpp:281
bool wasIgnored(std::string id) const
Returns whether the edge with the id was ignored during parsing.
Definition: NBEdgeCont.h:481
bool insert(NBEdge *edge, bool ignorePrunning=false)
Adds an edge to the dictionary.
Definition: NBEdgeCont.cpp:182
The representation of a single edge during network building.
Definition: NBEdge.h:92
static const double UNSPECIFIED_FRICTION
unspecified lane friction
Definition: NBEdge.h:351
bool addLane2LaneConnection(int fromLane, NBEdge *dest, int toLane, Lane2LaneInfoType type, bool mayUseSameDestination=false, bool mayDefinitelyPass=false, KeepClear keepClear=KEEPCLEAR_UNSPECIFIED, double contPos=UNSPECIFIED_CONTPOS, double visibility=UNSPECIFIED_VISIBILITY_DISTANCE, double speed=UNSPECIFIED_SPEED, double friction=UNSPECIFIED_FRICTION, double length=myDefaultConnectionLength, const PositionVector &customShape=PositionVector::EMPTY, const bool uncontrolled=UNSPECIFIED_CONNECTION_UNCONTROLLED, SVCPermissions permissions=SVC_UNSPECIFIED, const bool indirectLeft=false, const std::string &edgeType="", SVCPermissions changeLeft=SVC_UNSPECIFIED, SVCPermissions changeRight=SVC_UNSPECIFIED, bool postProcess=false)
Adds a connection between the specified this edge's lane and an approached one.
Definition: NBEdge.cpp:1098
const std::string & getID() const
Definition: NBEdge.h:1522
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:542
static const double UNSPECIFIED_LOADED_LENGTH
no length override given
Definition: NBEdge.h:360
static const double UNSPECIFIED_CONTPOS
unspecified internal junction position
Definition: NBEdge.h:354
static const double UNSPECIFIED_VISIBILITY_DISTANCE
unspecified foe visibility for connections
Definition: NBEdge.h:357
static const double UNSPECIFIED_SPEED
unspecified lane speed
Definition: NBEdge.h:348
@ USER
The connection was given by the user.
@ VALIDATED
The connection was computed and validated.
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition: NBEdge.h:342
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:1035
static const double UNSPECIFIED_OFFSET
unspecified lane offset
Definition: NBEdge.h:345
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1422
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:535
Instance responsible for building networks.
Definition: NBNetBuilder.h:107
static bool transformCoordinates(PositionVector &from, bool includeInBoundary=true, GeoConvHelper *from_srs=nullptr)
NBTypeCont & getTypeCont()
Returns a reference to the type container.
Definition: NBNetBuilder.h:149
NBEdgeCont & getEdgeCont()
Definition: NBNetBuilder.h:139
NBNodeCont & getNodeCont()
Returns a reference to the node container.
Definition: NBNetBuilder.h:144
NBTrafficLightLogicCont & getTLLogicCont()
Returns a reference to the traffic light logics container.
Definition: NBNetBuilder.h:154
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:57
std::string createClusterId(const NodeSet &cluster, const std::string &prefix="cluster_")
generate id from cluster node ids
Definition: NBNodeCont.h:136
bool insert(const std::string &id, const Position &position, NBDistrict *district=0)
Inserts a node into the map.
Definition: NBNodeCont.cpp:87
NBNode * retrieve(const std::string &id) const
Returns the node with the given name.
Definition: NBNodeCont.cpp:116
bool extract(NBNode *node, bool remember=false)
Removes the given node but does not delete it.
Definition: NBNodeCont.cpp:157
Represents a single node (junction) during network building.
Definition: NBNode.h:66
const std::set< NBTrafficLightDefinition * > & getControllingTLS() const
Returns the traffic lights that were assigned to this node (The set of tls that control this node)
Definition: NBNode.h:336
void addTrafficLight(NBTrafficLightDefinition *tlDef)
Adds a traffic light to the list of traffic lights that control this node.
Definition: NBNode.cpp:396
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition: NBNode.h:331
A traffic light logics which must be computed (only nodes/edges are given)
Definition: NBOwnTLDef.h:44
The base class for traffic light logic definitions.
bool insert(NBTrafficLightDefinition *logic, bool forceInsert=false)
Adds a logic definition to the dictionary.
A storage for available edgeTypes of edges.
Definition: NBTypeCont.h:52
double getEdgeTypeMaxWidth(const std::string &edgeType) const
Returns the maximum edge/lane widths of the given edgeType.
Definition: NBTypeCont.cpp:536
bool getEdgeTypeShallBeDiscarded(const std::string &edgeType) const
Returns the information whether edges of this edgeType shall be discarded.
Definition: NBTypeCont.cpp:526
double getEdgeTypeSpeed(const std::string &edgeType) const
Returns the maximal velocity for the given edgeType [m/s].
Definition: NBTypeCont.cpp:503
double getEdgeTypeWidth(const std::string &edgeType) const
Returns the lane width for the given edgeType [m].
Definition: NBTypeCont.cpp:564
SVCPermissions getEdgeTypePermissions(const std::string &edgeType) const
Returns allowed vehicle classes for the given edgeType.
Definition: NBTypeCont.cpp:552
double getEdgeTypeWidthResolution(const std::string &edgeType) const
Returns the resolution for interpreting edge/lane widths of the given edgeType.
Definition: NBTypeCont.cpp:531
bool knows(const std::string &edgeType) const
Returns whether the named edgeType is in the container.
Definition: NBTypeCont.cpp:303
A class for sorting lane sections by their s-value.
Importer for networks stored in openDrive format.
static void loadNetwork(const OptionsCont &oc, NBNetBuilder &nb)
Loads content of the optionally given SUMO file.
static void recomputeWidths(OpenDriveLaneSection &sec, double start, double end, double sectionStart, double sectionEnd)
static std::vector< double > discretizeOffsets(PositionVector &geom, const std::vector< OpenDriveLaneOffset > &offsets, const std::string &id)
transform Poly3 into a list of offsets, adding intermediate points to geom if needed
static void addOffsets(bool left, PositionVector &geom, const std::vector< OpenDriveLaneOffset > &offsets, const std::string &id, std::vector< double > &result)
std::map< std::string, OpenDriveSignal > & getSignals()
static void writeRoadObjects(const OpenDriveEdge *e)
static std::pair< NBEdge *, NBEdge * > retrieveSignalEdges(NBNetBuilder &nb, const std::string &fromID, const std::string &toID, const std::string &junction)
static PositionVector geomFromParamPoly(const OpenDriveEdge &e, const OpenDriveGeometry &g, double resolution)
void myEndElement(int element)
Called when a closing tag occurs.
static SequentialStringBijection::Entry openDriveAttrs[]
The names of openDrive-XML attributes (for passing to GenericSAXHandler)
static void calcPointOnCurve(double *ad_x, double *ad_y, double ad_centerX, double ad_centerY, double ad_r, double ad_length)
OpenDriveXMLTag
Numbers representing openDrive-XML - element names.
void addGeometryShape(GeometryType type, const std::vector< double > &vals)
static void setStraightConnections(std::vector< OpenDriveLane > &lanes)
OpenDriveController myCurrentController
const OpenDriveController & getController(std::string signalID)
static void setLaneAttributes(const OpenDriveEdge *e, NBEdge::Lane &sumoLane, const OpenDriveLane &odLane, bool saveOrigIDs, const NBTypeCont &tc)
std::vector< int > myElementStack
static void buildConnectionsToOuter(const Connection &c, const std::map< std::string, OpenDriveEdge * > &innerEdges, const std::map< std::string, OpenDriveEdge * > &edges, const NBTypeCont &tc, std::vector< Connection > &into, std::set< Connection > &seen)
void myStartElement(int element, const SUMOSAXAttributes &attrs)
Called on the opening of a tag;.
std::map< std::string, OpenDriveSignal > mySignals
static bool laneSectionsConnected(OpenDriveEdge *edge, int in, int out)
void addLink(LinkType lt, const std::string &elementType, const std::string &elementID, const std::string &contactPoint)
static OpenDriveController myDummyController
static PositionVector geomFromSpiral(const OpenDriveEdge &e, const OpenDriveGeometry &g, double resolution)
static PositionVector geomFromLine(const OpenDriveEdge &e, const OpenDriveGeometry &g, double resolution)
static NBNode * getOrBuildNode(const std::string &id, const Position &pos, NBNodeCont &nc)
Builds a node or returns the already built.
const NBTypeCont & myTypeContainer
NIImporter_OpenDrive(const NBTypeCont &tc, std::map< std::string, OpenDriveEdge * > &edges)
Constructor.
static Position calculateStraightEndPoint(double hdg, double length, const Position &start)
OpenDriveXMLTag myCurrentLaneDirection
static PositionVector geomFromPoly(const OpenDriveEdge &e, const OpenDriveGeometry &g, double resolution)
static void revisitLaneSections(const NBTypeCont &tc, std::map< std::string, OpenDriveEdge * > &edges)
Rechecks lane sections of the given edges.
static bool myIgnoreMisplacedSignals
static void sanitizeWidths(OpenDriveEdge *e)
GeometryType
OpenDrive geometry type enumeration.
static void computeShapes(std::map< std::string, OpenDriveEdge * > &edges)
Computes a polygon representation of each edge's geometry.
static void calculateCurveCenter(double *ad_x, double *ad_y, double ad_radius, double ad_hdg)
static std::string revertID(const std::string &id)
static void setEdgeLinks2(OpenDriveEdge &e, const std::map< std::string, OpenDriveEdge * > &edges)
static void splitMinWidths(OpenDriveEdge *e, const NBTypeCont &tc, double minDist)
std::map< std::string, OpenDriveController > myControllers
void myCharacters(int element, const std::string &chars)
Callback method for characters to implement by derived classes.
static NBTrafficLightDefinition * getTLSSecure(NBEdge *inEdge, NBNetBuilder &nb)
Poly3 OpenDriveElevation
LaneOffset has the same fields as Elevation.
std::map< std::string, OpenDriveController > & getControllers()
ContactPoint myCurrentContactPoint
static void findWidthSplit(const NBTypeCont &tc, std::vector< OpenDriveLane > &lanes, int section, double sectionStart, double sectionEnd, std::vector< double > &splitPositions)
static SequentialStringBijection::Entry openDriveTags[]
The names of openDrive-XML elements (for passing to GenericSAXHandler)
static PositionVector geomFromArc(const OpenDriveEdge &e, const OpenDriveGeometry &g, double resolution)
static void setNodeSecure(NBNodeCont &nc, OpenDriveEdge &e, const std::string &nodeID, NIImporter_OpenDrive::LinkType lt, std::vector< NodeSet > &joinedNodeIDs)
LinkType
OpenDrive link type enumeration.
static bool hasNonLinearElevation(OpenDriveEdge &e)
std::map< std::string, OpenDriveEdge * > & myEdges
int getTLIndexForController(std::string controllerID)
const std::string & getID() const
Returns the id.
Definition: Named.h:74
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 isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
bool exists(const std::string &name) const
Returns the information whether the named option is known.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
bool isUsableFileList(const std::string &name) const
Checks whether the named option is usable as a file list (with at least a single file)
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
bool writeXMLHeader(const std::string &rootElement, const std::string &schemaFile, std::map< SumoXMLAttr, std::string > attrs=std::map< SumoXMLAttr, std::string >(), bool includeConfig=true)
Writes an XML header with optional configuration.
C++ TraCI client API implementation.
Definition: POI.h:34
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
A point-of-interest.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
void set(double x, double y)
set positions x and y
Definition: Position.h:85
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:271
double x() const
Returns the x-position.
Definition: Position.h:55
void add(const Position &pos)
Adds the given position to this one.
Definition: Position.h:132
void mul(double val)
Multiplies position with the given value.
Definition: Position.h:105
double y() const
Returns the y-position.
Definition: Position.h:60
A list of positions.
double length2D() const
Returns the length.
void append(const PositionVector &v, double sameThreshold=2.0)
void move2sideCustom(std::vector< double > amount, double maxExtension=100)
move position vector to side using a custom offset for each geometry point
void rotate2D(double angle)
double rotationAtOffset(double pos) const
Returns the rotation at the given length.
Position positionAtOffset(double pos, double lateralOffset=0) const
Returns the position at the given length.
void add(double xoff, double yoff, double zoff)
int indexOfClosest(const Position &p, bool twoD=false) const
void move2side(double amount, double maxExtension=100)
move position vector to side using certain amount
bool almostSame(const PositionVector &v2, double maxDiv=POSITION_EPS) const
check if the two vectors have the same length and pairwise similar positions
PositionVector getSubpart2D(double beginOffset, double endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
int insertAtClosest(const Position &p, bool interpolateZ)
inserts p between the two closest positions
void push_back_noDoublePos(const Position &p)
insert in back a non double position
void removeDoublePoints(double minDist=POSITION_EPS, bool assertLength=false, int beginOffset=0, int endOffset=0, bool resample=false)
Removes positions if too near.
PositionVector reverse() const
reverse position vector
Position positionAtOffset2D(double pos, double lateralOffset=0) const
Returns the position at the given length.
static const RGBColor YELLOW
Definition: RGBColor.h:188
void writeXML(OutputDevice &out, bool geo=false) const
Definition: SUMOPolygon.cpp:98
Encapsulated SAX-Attributes.
T getOpt(int attr, const char *objectid, bool &ok, T defaultValue=T(), bool report=true) const
Tries to read given attribute assuming it is an int.
T get(int attr, const char *objectid, bool &ok, bool report=true) const
Tries to read given attribute assuming it is an int.
virtual bool hasAttribute(int id) const =0
Returns the information whether the named (by its enum-value) attribute is within the current list.
static StringBijection< TrafficLightType > TrafficLightTypes
traffic light types
static StringBijection< POIIcon > POIIcons
POI icon values.
const std::string & getString(const T key) const
T get(const std::string &str) const
static int toInt(const std::string &sData)
converts a string into the integer value described by it by calling the char-type converter,...
static bool runParser(GenericSAXHandler &handler, const std::string &file, const bool isNet=false, const bool isRoute=false, const bool isExternal=false, const bool catchExceptions=true)
Runs the given handler on the given file; returns if everything's ok.
Definition: XMLSubSys.cpp:148
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:21884
static double cn[6]
Definition: odrSpiral.cpp:68
void odrSpiral(double s, double cDot, double *x, double *y, double *t)
Definition: odrSpiral.cpp:236
A structure which describes a connection between edges or lanes.
Definition: NBEdge.h:201
An (internal) definition of a single lane of an edge.
Definition: NBEdge.h:143
double width
This lane's width.
Definition: NBEdge.h:176
PositionVector customShape
A custom shape for this lane set by the user.
Definition: NBEdge.h:189
std::string type
the type of this lane
Definition: NBEdge.h:192
double speed
The speed allowed on this lane.
Definition: NBEdge.h:151
SVCPermissions permissions
List of vehicle types that are allowed on this lane.
Definition: NBEdge.h:157
A connection between two roads.
Attribute set applied at a certain position along a lane.
Representation of a signal group.
Representation of an openDrive "link".
double length
The length of the edge.
std::string id
The id of the edge.
std::string junction
The id of the junction the edge belongs to.
std::string streetName
The road name of the edge.
int getPriority(OpenDriveXMLTag dir) const
Returns the edge's priority, regarding the direction.
std::vector< OpenDriveLink > links
std::vector< OpenDriveSignal > signals
std::vector< OpenDriveLaneSection > laneSections
std::vector< OpenDriveLaneOffset > offsets
std::vector< OpenDriveObject > objects
std::vector< OpenDriveGeometry > geometries
std::vector< OpenDriveElevation > elevations
Representation of an OpenDrive geometry part.
std::vector< OpenDriveWidth > widthData
std::vector< std::pair< double, LaneAttributeChange > > attributeChanges
List of permission and speed changes.
double speed
The lane's speed (set in post-processing)
SVCPermissions computePermission(const NBTypeCont &tc, const std::vector< std::string > &allowed, const std::vector< std::string > &denied) const
compute the actual SUMO lane permissions given the lane type as a start solution
SVCPermissions permission
The access permissions (set in post-processing)
OpenDriveLaneSection buildLaneSection(const NBTypeCont &tc, double startPos)
bool buildAttributeChanges(const NBTypeCont &tc, std::vector< OpenDriveLaneSection > &newSections)
double length
The length of this lane section.
std::map< OpenDriveXMLTag, std::vector< OpenDriveLane > > lanesByDir
The lanes, sorted by their direction.
std::map< int, int > laneMap
A mapping from OpenDrive to SUMO-index (the first is signed, the second unsigned)
int rightLaneNumber
The number of lanes on the right and on the left side, respectively.
double sOrig
The original starting offset of this lane section (differs from s if the section had to be split)
void buildLaneMapping(const NBTypeCont &tc)
Build the mapping from OpenDrive to SUMO lanes.
std::map< int, int > getInnerConnections(OpenDriveXMLTag dir, const OpenDriveLaneSection &prev)
Returns the links from the previous to this lane section.
double s
The starting offset of this lane section.
std::string controller
the controller ID
double computeAt(double pos) const