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  const 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  const PositionVector geom = geomWithOffset.getSubpart2D(sB, sE).simplified2(false);
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 (const OpenDriveLane& odl : lanes) {
531  std::map<int, int>::const_iterator lp = (*j).laneMap.find(odl.id);
532  if (lp != (*j).laneMap.end()) {
533  int sumoLaneIndex = lp->second;
534  setLaneAttributes(e, currRight->getLaneStruct(sumoLaneIndex), odl, saveOrigIDs, tc);
535  laneIndexMap[std::make_pair(currRight, sumoLaneIndex)] = odl.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, odl.widthData, e->id + "_" + toString(odl.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_VULNERABLE) != 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 (const 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 (const auto& i : edges) {
1499  OpenDriveEdge& e = *i.second;
1501  const double lineRes = hasNonLinearElevation(e) ? res : -1;
1502  Position last;
1503  int index = 0;
1504  for (const OpenDriveGeometry& g : e.geometries) {
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  WRITE_WARNINGF(TL("Mismatched geometry for edge '%' between geometry segments % and %."), e.id, index - 1, index);
1533  }
1534  e.geom.pop_back();
1535  }
1536  //std::cout << " adding geometry to road=" << e.id << " old=" << e.geom << " new=" << geom << "\n";
1537  for (PositionVector::iterator k = geom.begin(); k != geom.end(); ++k) {
1538  last = *k;
1540  }
1541  prevType = g.type;
1542  index++;
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  e.geom = e.geom.simplified2(false);
1561 #ifdef DEBUG_SHAPE
1562  if (DEBUG_COND3(e.id)) {
1563  std::cout << " reducedGeom=" << e.geom << "\n";
1564  }
1565 #endif
1567  WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), e.id);
1568  }
1569  // add z-data
1570  int k = 0;
1571  double pos = 0;
1572  //std::cout << " edge=" << e.id << " geom.size=" << e.geom.size() << " geom.len=" << e.geom.length2D() << " ele.size=" << e.elevations.size() << "\n";
1573  if (!oc.getBool("flatten")) {
1574  for (std::vector<OpenDriveElevation>::iterator j = e.elevations.begin(); j != e.elevations.end(); ++j) {
1575  const OpenDriveElevation& el = *j;
1576  const double sNext = (j + 1) == e.elevations.end() ? std::numeric_limits<double>::max() : (*(j + 1)).s;
1577  while (k < (int)e.geom.size() && pos < sNext) {
1578  const double z = el.computeAt(pos);
1579  //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";
1580  e.geom[k].add(0, 0, z);
1581  k++;
1582  if (k < (int)e.geom.size()) {
1583  // XXX pos understimates the actual position since the
1584  // actual geometry between k-1 and k could be curved
1585  pos += e.geom[k - 1].distanceTo2D(e.geom[k]);
1586  }
1587  }
1588  }
1589  }
1590  // add laneoffset
1591  if (e.offsets.size() > 0) {
1593  }
1594  //std::cout << " loaded geometry " << e.id << "=" << e.geom << "\n";
1595  }
1596 }
1597 
1598 
1599 std::vector<double>
1600 NIImporter_OpenDrive::discretizeOffsets(PositionVector& geom, const std::vector<OpenDriveLaneOffset>& offsets, const std::string& id) {
1601  UNUSED_PARAMETER(id);
1602  std::vector<double> laneOffsets;
1603  // make sure there are intermediate points for each offset-section
1604  for (const OpenDriveLaneOffset& el : offsets) {
1605  // check wether we need to insert a new point at dist
1606  Position pS = geom.positionAtOffset2D(el.s);
1607  int iS = geom.indexOfClosest(pS);
1608  // prevent close spacing to reduce impact of rounding errors in z-axis
1609  if (pS.distanceTo2D(geom[iS]) > POSITION_EPS) {
1610  geom.insertAtClosest(pS, false);
1611  //std::cout << " edge=" << e.id << " inserting pos=" << pS << " s=" << el.s << " iS=" << iS << " dist=" << pS.distanceTo2D(geom[iS]) << "\n";
1612  }
1613  }
1614  // XXX add further points for sections with non-constant offset
1615  // shift each point orthogonally by the specified offset
1616  int kk = 0;
1617  double ppos = 0;
1618  for (auto j = offsets.begin(); j != offsets.end(); ++j) {
1619  const OpenDriveLaneOffset& el = *j;
1620  const double sNext = (j + 1) == offsets.end() ? std::numeric_limits<double>::max() : (*(j + 1)).s;
1621  while (kk < (int)geom.size() && ppos < sNext) {
1622  const double offset = el.computeAt(ppos);
1623  laneOffsets.push_back(fabs(offset) > POSITION_EPS ? -offset : 0);
1624  kk++;
1625  if (kk < (int)geom.size()) {
1626  // XXX pos understimates the actual position since the
1627  // actual geometry between k-1 and k could be curved
1628  ppos += geom[kk - 1].distanceTo2D(geom[kk]);
1629  }
1630  }
1631  }
1632  return laneOffsets;
1633 }
1634 
1635 
1636 void
1637 NIImporter_OpenDrive::addOffsets(bool left, PositionVector& geom, const std::vector<OpenDriveWidth>& offsets, const std::string& id, std::vector<double>& result) {
1638  UNUSED_PARAMETER(id);
1639  // make sure there are intermediate points for each offset-section
1640  for (const OpenDriveLaneOffset& el : offsets) {
1641  // check wether we need to insert a new point at dist
1642  Position pS = geom.positionAtOffset2D(el.s);
1643  int iS = geom.indexOfClosest(pS);
1644  // prevent close spacing to reduce impact of rounding errors in z-axis
1645  if (pS.distanceTo2D(geom[iS]) > POSITION_EPS) {
1646  //std::cout << " edge=" << id << " inserting pos=" << pS << " s=" << el.s << " iS=" << iS << " dist=" << pS.distanceTo2D(geom[iS]) << "\n";
1647  int at = geom.insertAtClosest(pS, false);
1648  double interpolatedOffset = 0;
1649  if (at == 0) {
1650  interpolatedOffset = result.front();
1651  } else if (at == (int)geom.size() - 1) {
1652  interpolatedOffset = result.back();
1653  } else {
1654  interpolatedOffset = (result[at - 1] + result[at]) / 2;
1655  }
1656  result.insert(result.begin() + at, interpolatedOffset);
1657  }
1658  }
1659  // shift each point orthogonally by the specified offset
1660  int kk = 0;
1661  double ppos = 0;
1662  const int sign = left ? -1 : 1;
1663  for (auto j = offsets.begin(); j != offsets.end(); ++j) {
1664  const OpenDriveWidth& el = *j;
1665  const double sNext = (j + 1) == offsets.end() ? std::numeric_limits<double>::max() : (*(j + 1)).s;
1666  while (kk < (int)geom.size() && ppos < sNext) {
1667  const double offset = el.computeAt(ppos);
1668  result[kk] += fabs(offset) > POSITION_EPS ? sign * offset : 0;
1669  kk++;
1670  if (kk < (int)geom.size()) {
1671  // XXX pos understimates the actual position since the
1672  // actual geometry between k-1 and k could be curved
1673  ppos += geom[kk - 1].distanceTo2D(geom[kk]);
1674  }
1675  }
1676  }
1677 }
1678 
1679 
1680 void
1681 NIImporter_OpenDrive::revisitLaneSections(const NBTypeCont& tc, std::map<std::string, OpenDriveEdge*>& edges) {
1682  for (const auto& i : edges) {
1683  OpenDriveEdge& e = *i.second;
1684 #ifdef DEBUG_VARIABLE_SPEED
1685  if (DEBUG_COND(&e)) {
1686  gDebugFlag1 = true;
1687  std::cout << "revisitLaneSections e=" << e.id << "\n";
1688  }
1689 #endif
1690  // split by speed limits or by access restrictions
1691  std::vector<OpenDriveLaneSection> newSections;
1692  for (OpenDriveLaneSection& section : e.laneSections) {
1693  std::vector<OpenDriveLaneSection> splitSections;
1694  const bool splitByAttrChange = section.buildAttributeChanges(tc, splitSections);
1695  if (!splitByAttrChange) {
1696  newSections.push_back(section);
1697  } else {
1698  std::copy(splitSections.begin(), splitSections.end(), back_inserter(newSections));
1699  }
1700  }
1701 
1702  e.laneSections = newSections;
1703  double lastS = -1.;
1704  // check whether the lane sections are in the right order
1705  bool sorted = true;
1706  for (const OpenDriveLaneSection& section : e.laneSections) {
1707  if (section.s <= lastS) {
1708  sorted = false;
1709  break;
1710  }
1711  lastS = section.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 duplicate s-values occur
1718  // but keep all lane sections for connecting roads because they are
1719  // needed to establish connectivity (laneSectionsConnected)
1720  // TODO recheck whether removing short sections is a good idea at all: once we parse linkage info, it will be lost.
1721  if (e.laneSections.size() > 1 && !e.isInner) {
1722  for (std::vector<OpenDriveLaneSection>::iterator j = e.laneSections.begin(); j != e.laneSections.end() - 1;) {
1723  if ((j + 1)->s - j->s < POSITION_EPS) {
1724  WRITE_WARNINGF(TL("Almost duplicate s-value '%' for lane sections occurred at edge '%'; first entry was removed."), toString(j->s), e.id);
1725  j = e.laneSections.erase(j);
1726  } else {
1727  ++j;
1728  }
1729  }
1730  }
1731 #ifdef DEBUG_VARIABLE_SPEED
1732  gDebugFlag1 = false;
1733 #endif
1734  }
1735 }
1736 
1737 
1740  UNUSED_PARAMETER(e);
1741  PositionVector ret;
1742  Position start(g.x, g.y);
1743  Position end = calculateStraightEndPoint(g.hdg, g.length, start);
1744  if (resolution > 0 && g.length > 0) {
1745  const int numPoints = (int)ceil(g.length / resolution) + 1;
1746  double dx = (end.x() - start.x()) / (numPoints - 1);
1747  double dy = (end.y() - start.y()) / (numPoints - 1);
1748  for (int i = 0; i < numPoints; i++) {
1749  ret.push_back(Position(g.x + i * dx, g.y + i * dy));
1750  }
1751  } else {
1752  ret.push_back(start);
1753  ret.push_back(end);
1754  }
1755  return ret;
1756 }
1757 
1758 
1761  UNUSED_PARAMETER(e);
1762  PositionVector ret;
1763  double curveStart = g.params[0];
1764  double curveEnd = g.params[1];
1765  try {
1766  double cDot = (curveEnd - curveStart) / g.length;
1767  if (cDot == 0 || g.length == 0) {
1768  WRITE_WARNINGF(TL("Could not compute spiral geometry for edge '%' (cDot=% length=%)."), e.id, toString(cDot), toString(g.length));
1769  ret.push_back(Position(g.x, g.y));
1770  return ret;
1771  }
1772  double sStart = curveStart / cDot;
1773  double sEnd = curveEnd / cDot;
1774  double x = 0;
1775  double y = 0;
1776  double t = 0;
1777  double tStart = 0;
1778  double s;
1779  odrSpiral(sStart, cDot, &x, &y, &tStart);
1780  for (s = sStart; s <= sEnd; s += resolution) {
1781  odrSpiral(s, cDot, &x, &y, &t);
1782  ret.push_back(Position(x, y));
1783  }
1784  if (s != sEnd /*&& ret.size() == 1*/) {
1785  odrSpiral(sEnd, cDot, &x, &y, &t);
1786  ret.push_back(Position(x, y));
1787  }
1788  //if (s != sEnd && ret.size() > 2) {
1789  // ret.pop_back();
1790  //}
1791  assert(ret.size() >= 2);
1792  assert(ret[0] != ret[1]);
1793  // shift start to coordinate origin
1794  PositionVector ret1 = ret;
1795  ret.add(ret.front() * -1);
1796  // rotate
1797  PositionVector ret2 = ret;
1798  ret.rotate2D(g.hdg - tStart);
1799 #ifdef DEBUG_SPIRAL
1800  std::cout
1801  << std::setprecision(4)
1802  << "edge=" << e.id << " s=" << g.s
1803  << " cStart=" << curveStart
1804  << " cEnd=" << curveEnd
1805  << " cDot=" << cDot
1806  << " sStart=" << sStart
1807  << " sEnd=" << sEnd
1808  << " g.hdg=" << GeomHelper::naviDegree(g.hdg)
1809  << " tStart=" << GeomHelper::naviDegree(tStart)
1810  << "\n beforeShift=" << ret1
1811  << "\n beforeRot=" << ret2
1812  << "\n";
1813 #endif
1814  // shift to geometry start
1815  ret.add(g.x, g.y, 0);
1816  } catch (const std::runtime_error& error) {
1817  WRITE_WARNINGF(TL("Could not compute spiral geometry for edge '%' (%)."), e.id, error.what());
1818  ret.push_back(Position(g.x, g.y));
1819  }
1820  return ret.getSubpart2D(0, g.length);
1821 }
1822 
1823 
1826  UNUSED_PARAMETER(e);
1827  PositionVector ret;
1828  double centerX = g.x;
1829  double centerY = g.y;
1830  // left: positive value
1831  double curvature = g.params[0];
1832  double radius = 1. / curvature;
1833  // center point
1834  calculateCurveCenter(&centerX, &centerY, radius, g.hdg);
1835  double endX = g.x;
1836  double endY = g.y;
1837  double startX = g.x;
1838  double startY = g.y;
1839  double geo_posS = g.s;
1840  double geo_posE = g.s;
1841  bool end = false;
1842  do {
1843  geo_posE += resolution;
1844  if (geo_posE - g.s > g.length) {
1845  geo_posE = g.s + g.length;
1846  }
1847  if (geo_posE - g.s > g.length) {
1848  geo_posE = g.s + g.length;
1849  }
1850  calcPointOnCurve(&endX, &endY, centerX, centerY, radius, geo_posE - geo_posS);
1851  ret.push_back(Position(startX, startY));
1852 
1853  startX = endX;
1854  startY = endY;
1855  geo_posS = geo_posE;
1856 
1857  if (geo_posE - (g.s + g.length) < 0.001 && geo_posE - (g.s + g.length) > -0.001) {
1858  end = true;
1859  }
1860  } while (!end);
1861  ret.push_back(Position(startX, startY));
1862  return ret.getSubpart2D(0, g.length);
1863 }
1864 
1865 
1868  UNUSED_PARAMETER(e);
1869  const double s = sin(g.hdg);
1870  const double c = cos(g.hdg);
1871  PositionVector ret;
1872  for (double off = 0; off < g.length + 2.; off += resolution) {
1873  double x = off;
1874  double y = g.params[0] + g.params[1] * off + g.params[2] * pow(off, 2.) + g.params[3] * pow(off, 3.);
1875  double xnew = x * c - y * s;
1876  double ynew = x * s + y * c;
1877  ret.push_back(Position(g.x + xnew, g.y + ynew));
1878  }
1879  return ret.getSubpart2D(0, g.length);
1880 }
1881 
1882 
1885  UNUSED_PARAMETER(e);
1886  const double s = sin(g.hdg);
1887  const double c = cos(g.hdg);
1888  const double pMax = g.params[8] <= 0 ? g.length : g.params[8];
1889  const double pStep = pMax / ceil(g.length / resolution);
1890  PositionVector ret;
1891  for (double p = 0; p <= pMax + pStep; p += pStep) {
1892  double x = g.params[0] + g.params[1] * p + g.params[2] * pow(p, 2.) + g.params[3] * pow(p, 3.);
1893  double y = g.params[4] + g.params[5] * p + g.params[6] * pow(p, 2.) + g.params[7] * pow(p, 3.);
1894  double xnew = x * c - y * s;
1895  double ynew = x * s + y * c;
1896  ret.push_back(Position(g.x + xnew, g.y + ynew));
1897  }
1898  return ret.getSubpart2D(0, g.length);
1899 }
1900 
1901 
1902 Position
1903 NIImporter_OpenDrive::calculateStraightEndPoint(double hdg, double length, const Position& start) {
1904  double normx = 1.0f;
1905  double normy = 0.0f;
1906  double x2 = normx * cos(hdg) - normy * sin(hdg);
1907  double y2 = normx * sin(hdg) + normy * cos(hdg);
1908  normx = x2 * length;
1909  normy = y2 * length;
1910  return Position(start.x() + normx, start.y() + normy);
1911 }
1912 
1913 
1914 void
1915 NIImporter_OpenDrive::calculateCurveCenter(double* ad_x, double* ad_y, double ad_radius, double ad_hdg) {
1916  double normX = 1.0;
1917  double normY = 0.0;
1918  double tmpX;
1919  double turn;
1920  if (ad_radius > 0) {
1921  turn = -1.0;
1922  } else {
1923  turn = 1.0;
1924  }
1925 
1926  tmpX = normX;
1927  normX = normX * cos(ad_hdg) + normY * sin(ad_hdg);
1928  normY = tmpX * sin(ad_hdg) + normY * cos(ad_hdg);
1929 
1930  tmpX = normX;
1931  normX = turn * normY;
1932  normY = -turn * tmpX;
1933 
1934  normX = fabs(ad_radius) * normX;
1935  normY = fabs(ad_radius) * normY;
1936 
1937  *ad_x += normX;
1938  *ad_y += normY;
1939 }
1940 
1941 
1942 void
1943 NIImporter_OpenDrive::calcPointOnCurve(double* ad_x, double* ad_y, double ad_centerX, double ad_centerY,
1944  double ad_r, double ad_length) {
1945  double rotAngle = ad_length / fabs(ad_r);
1946  double vx = *ad_x - ad_centerX;
1947  double vy = *ad_y - ad_centerY;
1948  double tmpx;
1949 
1950  double turn;
1951  if (ad_r > 0) {
1952  turn = -1; //left
1953  } else {
1954  turn = 1; //right
1955  }
1956  tmpx = vx;
1957  vx = vx * cos(rotAngle) + turn * vy * sin(rotAngle);
1958  vy = -1 * turn * tmpx * sin(rotAngle) + vy * cos(rotAngle);
1959  *ad_x = vx + ad_centerX;
1960  *ad_y = vy + ad_centerY;
1961 }
1962 
1963 
1964 // ---------------------------------------------------------------------------
1965 // section
1966 // ---------------------------------------------------------------------------
1968  lanesByDir[OPENDRIVE_TAG_LEFT] = std::vector<OpenDriveLane>();
1969  lanesByDir[OPENDRIVE_TAG_RIGHT] = std::vector<OpenDriveLane>();
1970  lanesByDir[OPENDRIVE_TAG_CENTER] = std::vector<OpenDriveLane>();
1971 }
1972 
1973 
1974 void
1976  discardedInnerWidthRight = 0;
1977  int sumoLane = 0;
1978  bool singleType = true;
1979  std::vector<std::string> types;
1980  const std::vector<OpenDriveLane>& dirLanesR = lanesByDir.find(OPENDRIVE_TAG_RIGHT)->second;
1981  for (std::vector<OpenDriveLane>::const_reverse_iterator i = dirLanesR.rbegin(); i != dirLanesR.rend(); ++i) {
1982  if (myImportAllTypes || (tc.knows((*i).type) && !tc.getEdgeTypeShallBeDiscarded((*i).type))) {
1983  discardedInnerWidthRight = 0;
1984  laneMap[(*i).id] = sumoLane++;
1985  types.push_back((*i).type);
1986  if (types.front() != types.back()) {
1987  singleType = false;
1988  }
1989  } else {
1990  discardedInnerWidthRight += (*i).width;
1991  }
1992  }
1993  discardedInnerWidthLeft = 0;
1994  rightLaneNumber = sumoLane;
1995  rightType = sumoLane > 0 ? (singleType ? types.front() : joinToString(types, "|")) : "";
1996  sumoLane = 0;
1997  singleType = true;
1998  types.clear();
1999  const std::vector<OpenDriveLane>& dirLanesL = lanesByDir.find(OPENDRIVE_TAG_LEFT)->second;
2000  for (std::vector<OpenDriveLane>::const_iterator i = dirLanesL.begin(); i != dirLanesL.end(); ++i) {
2001  if (myImportAllTypes || (tc.knows((*i).type) && !tc.getEdgeTypeShallBeDiscarded((*i).type))) {
2002  discardedInnerWidthLeft = 0;
2003  laneMap[(*i).id] = sumoLane++;
2004  types.push_back((*i).type);
2005  if (types.front() != types.back()) {
2006  singleType = false;
2007  }
2008  } else {
2009  discardedInnerWidthLeft += (*i).width;
2010  }
2011  }
2012  leftLaneNumber = sumoLane;
2013  leftType = sumoLane > 0 ? (singleType ? types.front() : joinToString(types, "|")) : "";
2014 }
2015 
2016 
2017 std::map<int, int>
2019  std::map<int, int> ret;
2020  const std::vector<OpenDriveLane>& dirLanes = lanesByDir.find(dir)->second;
2021  for (std::vector<OpenDriveLane>::const_reverse_iterator i = dirLanes.rbegin(); i != dirLanes.rend(); ++i) {
2022  std::map<int, int>::const_iterator toP = laneMap.find((*i).id);
2023  if (toP == laneMap.end()) {
2024  // the current lane is not available in SUMO
2025  continue;
2026  }
2027  int to = (*toP).second;
2028  int from = UNSET_CONNECTION;
2029  if ((*i).predecessor != UNSET_CONNECTION) {
2030  from = (*i).predecessor;
2031  }
2032  if (from != UNSET_CONNECTION) {
2033  std::map<int, int>::const_iterator fromP = prev.laneMap.find(from);
2034  if (fromP != prev.laneMap.end()) {
2035  from = (*fromP).second;
2036  } else {
2037  from = UNSET_CONNECTION;
2038  }
2039  }
2040  if (from != UNSET_CONNECTION && to != UNSET_CONNECTION) {
2041  if (ret.find(from) != ret.end()) {
2042 // WRITE_WARNING(TL("double connection"));
2043  }
2044  if (dir == OPENDRIVE_TAG_LEFT) {
2045  std::swap(from, to);
2046  }
2047  ret[from] = to;
2048  } else {
2049 // WRITE_WARNING(TL("missing connection"));
2050  }
2051  }
2052  return ret;
2053 }
2054 
2055 
2058  OpenDriveLaneSection ret(*this);
2059  ret.s += startPos;
2060  for (int k = 0; k < (int)ret.lanesByDir[OPENDRIVE_TAG_RIGHT].size(); ++k) {
2062  l.speed = 0;
2063  l.permission = 0;
2064  std::vector<std::pair<double, LaneAttributeChange> >::const_iterator it = std::find_if(l.attributeChanges.begin(), l.attributeChanges.end(), same_position_finder(startPos));
2065  if (it != l.attributeChanges.end()) {
2066  l.speed = (*it).second.speed;
2067  l.permission = l.computePermission(tc, (*it).second.allowed, (*it).second.denied);
2068  }
2069  }
2070  for (int k = 0; k < (int)ret.lanesByDir[OPENDRIVE_TAG_LEFT].size(); ++k) {
2072  l.speed = 0;
2073  l.permission = 0;
2074  std::vector<std::pair<double, LaneAttributeChange> >::const_iterator it = std::find_if(l.attributeChanges.begin(), l.attributeChanges.end(), same_position_finder(startPos));
2075  if (it != l.attributeChanges.end()) {
2076  l.speed = (*it).second.speed;
2077  l.permission = l.computePermission(tc, (*it).second.allowed, (*it).second.denied);
2078  }
2079  }
2080  return ret;
2081 }
2082 
2083 
2085 NIImporter_OpenDrive::OpenDriveLane::computePermission(const NBTypeCont& tc, const std::vector<std::string>& allowed,
2086  const std::vector<std::string>& denied) const {
2087  SVCPermissions perms = tc.getEdgeTypePermissions(type);
2088  if (allowed.size() > 0 && denied.size() > 0) {
2089  WRITE_WARNING(TL("Will discard access settings as both denied and allowed classes have been specified."));
2090  } else if (allowed.size() > 0) {
2091  perms = SVC_IGNORING;
2092  for (const std::string& allow : allowed) {
2093  if (allow == "simulator") {
2094  perms = SVC_IGNORING;
2095  break;
2096  } else if (allow == "autonomousTraffic" || allow == "autonomous traffic" || allow == "throughTraffic") {
2097  perms = tc.getEdgeTypePermissions(type);
2098  break;
2099  } else if (allow == "pedestrian") {
2100  perms |= SVC_PEDESTRIAN;
2101  } else if (allow == "passengerCar") {
2102  perms |= SVC_PASSENGER;
2103  } else if (allow == "bus") {
2104  perms |= SVC_BUS;
2105  } else if (allow == "delivery") {
2106  perms |= SVC_DELIVERY;
2107  } else if (allow == "emergency") {
2108  perms |= SVC_EMERGENCY;
2109  } else if (allow == "taxi") {
2110  perms |= SVC_TAXI;
2111  } else if (allow == "bicycle") {
2112  perms |= SVC_BICYCLE;
2113  } else if (allow == "motorcycle") {
2114  perms |= SVC_MOTORCYCLE;
2115  } else if (allow == "truck" || allow == "trucks") {
2116  perms |= SVC_TRUCK;
2117  perms |= SVC_TRAILER;
2118  }
2119  }
2120  } else if (denied.size() > 0) {
2121  for (const std::string& deny : denied) {
2122  if (deny == "none") {
2123  perms = tc.getEdgeTypePermissions(type);
2124  break;
2125  } else if (deny == "autonomousTraffic" || deny == "autonomous traffic" || deny == "throughTraffic") {
2126  perms = SVC_IGNORING;
2127  break;
2128  } else if (deny == "pedestrian") {
2129  perms &= ~SVC_PEDESTRIAN;
2130  } else if (deny == "passengerCar") {
2131  perms &= ~SVC_PASSENGER;
2132  } else if (deny == "bus") {
2133  perms &= ~SVC_BUS;
2134  } else if (deny == "delivery") {
2135  perms &= ~SVC_DELIVERY;
2136  } else if (deny == "emergency") {
2137  perms &= ~SVC_EMERGENCY;
2138  } else if (deny == "taxi") {
2139  perms &= ~SVC_TAXI;
2140  } else if (deny == "bicycle") {
2141  perms &= ~SVC_BICYCLE;
2142  } else if (deny == "motorcycle") {
2143  perms &= ~SVC_MOTORCYCLE;
2144  } else if (deny == "truck" || deny == "trucks") {
2145  perms &= ~SVC_TRUCK;
2146  perms &= ~SVC_TRAILER;
2147  }
2148  }
2149  }
2150  return perms;
2151 }
2152 
2153 
2154 bool
2155 NIImporter_OpenDrive::OpenDriveLaneSection::buildAttributeChanges(const NBTypeCont& tc, std::vector<OpenDriveLaneSection>& newSections) {
2156  std::set<double> attributeChangePositions;
2157  // collect speed change and access restriction positions and apply initial values to the begin
2158  for (std::vector<OpenDriveLane>::iterator k = lanesByDir[OPENDRIVE_TAG_RIGHT].begin(); k != lanesByDir[OPENDRIVE_TAG_RIGHT].end(); ++k) {
2159  for (std::vector<std::pair<double, LaneAttributeChange> >::const_iterator l = (*k).attributeChanges.begin(); l != (*k).attributeChanges.end(); ++l) {
2160  attributeChangePositions.insert((*l).first);
2161  if ((*l).first == 0) {
2162  (*k).speed = (*l).second.speed;
2163  (*k).permission = (*k).computePermission(tc, (*l).second.allowed, (*l).second.denied);
2164  }
2165  }
2166  }
2167  for (std::vector<OpenDriveLane>::iterator k = lanesByDir[OPENDRIVE_TAG_LEFT].begin(); k != lanesByDir[OPENDRIVE_TAG_LEFT].end(); ++k) {
2168  for (std::vector<std::pair<double, LaneAttributeChange> >::const_iterator l = (*k).attributeChanges.begin(); l != (*k).attributeChanges.end(); ++l) {
2169  attributeChangePositions.insert((*l).first);
2170  if ((*l).first == 0) {
2171  (*k).speed = (*l).second.speed;
2172  (*k).permission = (*k).computePermission(tc, (*l).second.allowed, (*l).second.denied);
2173  }
2174  }
2175  }
2176 
2177  // do nothing if there is none
2178  if (attributeChangePositions.size() == 0) {
2179  return false;
2180  }
2181 
2182  if (*attributeChangePositions.begin() > 0) {
2183  attributeChangePositions.insert(0);
2184  }
2185 #ifdef DEBUG_VARIABLE_SPEED
2186  if (gDebugFlag1) std::cout
2187  << " buildSpeedChanges sectionStart=" << s
2188  << " speedChangePositions=" << joinToString(speedChangePositions, ", ")
2189  << "\n";
2190 #endif
2191  for (std::set<double>::iterator i = attributeChangePositions.begin(); i != attributeChangePositions.end(); ++i) {
2192  if (i == attributeChangePositions.begin()) {
2193  newSections.push_back(*this);
2194  } else {
2195  newSections.push_back(buildLaneSection(tc, *i));
2196  }
2197  }
2198  // propagate speeds and access restrictions
2199  for (int i = 0; i != (int)newSections.size(); ++i) {
2200  for (auto& k : newSections[i].lanesByDir) {
2201  for (int j = 0; j != (int)k.second.size(); ++j) {
2202  OpenDriveLane& l = k.second[j];
2203  if (l.speed == 0) {
2204  if (i > 0) {
2205  l.speed = newSections[i - 1].lanesByDir[k.first][j].speed;
2206  } else {
2207  tc.getEdgeTypeSpeed(l.type);
2208  }
2209  }
2210  if (l.permission == 0) {
2211  if (i > 0) {
2212  l.permission = newSections[i - 1].lanesByDir[k.first][j].permission;
2213  l.type = newSections[i - 1].lanesByDir[k.first][j].type;
2214  } else {
2216  }
2217  }
2218  }
2219  }
2220  }
2221  return true;
2222 }
2223 
2224 
2225 
2226 // ---------------------------------------------------------------------------
2227 // edge
2228 // ---------------------------------------------------------------------------
2229 int
2231  // for signal interpretations see https://de.wikipedia.org/wiki/Bildtafel_der_Verkehrszeichen_in_der_Bundesrepublik_Deutschland_seit_2013
2232  int prio = 1;
2233  for (std::vector<OpenDriveSignal>::const_iterator i = signals.begin(); i != signals.end(); ++i) {
2234  int tmp = 1;
2235  if ((*i).type == "301" || (*i).type == "306") { // priority road or local priority
2236  tmp = 2;
2237  }
2238  if ((*i).type == "205" /*|| (*i).type == "206"*/) { // yield or stop
2239  tmp = 0;
2240  }
2241  if (tmp != 1 && dir == OPENDRIVE_TAG_RIGHT && (*i).orientation > 0) {
2242  prio = tmp;
2243  }
2244  if (tmp != 1 && dir == OPENDRIVE_TAG_LEFT && (*i).orientation < 0) {
2245  prio = tmp;
2246  }
2247 
2248  }
2249  return prio;
2250 }
2251 
2252 
2253 
2254 // ---------------------------------------------------------------------------
2255 // loader methods
2256 // ---------------------------------------------------------------------------
2257 NIImporter_OpenDrive::NIImporter_OpenDrive(const NBTypeCont& tc, std::map<std::string, OpenDriveEdge*>& edges)
2259  myTypeContainer(tc), myCurrentEdge("", "", "", -1), myCurrentController("", ""), myEdges(edges), myOffset(0, 0),
2260  myUseCurrentNode(false) {
2261 }
2262 
2263 
2265 }
2266 
2267 
2268 void
2270  const SUMOSAXAttributes& attrs) {
2271  if (myUseCurrentNode) { // skip the parent node repeated in the included file
2272  myUseCurrentNode = false;
2273  myElementStack.push_back(element);
2274  return;
2275  }
2276  bool ok = true;
2277  switch (element) {
2278  case OPENDRIVE_TAG_HEADER: {
2279  int majorVersion = attrs.get<int>(OPENDRIVE_ATTR_REVMAJOR, nullptr, ok);
2280  int minorVersion = attrs.get<int>(OPENDRIVE_ATTR_REVMINOR, nullptr, ok);
2281  if (majorVersion == 1 && minorVersion > 4) { // disable flags only used for old 1.4 standard
2283  }
2284  /*
2285  if (majorVersion != 1 || minorVersion != 2) {
2286  // TODO: leave note of exceptions
2287  WRITE_WARNINGF(TL("Given openDrive file '%' uses version %.%;\n Version 1.2 is supported."), getFileName(), toString(majorVersion), toString(minorVersion));
2288  }
2289  */
2290  }
2291  break;
2292  case OPENDRIVE_TAG_OFFSET: {
2293  double x = attrs.get<double>(OPENDRIVE_ATTR_X, "offset", ok);
2294  double y = attrs.get<double>(OPENDRIVE_ATTR_Y, "offset", ok);
2295  myOffset.set(x, y);
2298  }
2299  }
2300  break;
2301  case OPENDRIVE_TAG_ROAD: {
2302  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, nullptr, ok);
2303  std::string streetName = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, nullptr, ok, "", false);
2304  std::string junction = attrs.get<std::string>(OPENDRIVE_ATTR_JUNCTION, id.c_str(), ok);
2305  double length = attrs.get<double>(OPENDRIVE_ATTR_LENGTH, id.c_str(), ok);
2306  myCurrentEdge = OpenDriveEdge(id, streetName, junction, length);
2307  }
2308  break;
2310  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_ROAD) {
2311  std::string elementType = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTTYPE, myCurrentEdge.id.c_str(), ok);
2312  std::string elementID = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTID, myCurrentEdge.id.c_str(), ok);
2313  std::string contactPoint = attrs.hasAttribute(OPENDRIVE_ATTR_CONTACTPOINT)
2314  ? attrs.get<std::string>(OPENDRIVE_ATTR_CONTACTPOINT, myCurrentEdge.id.c_str(), ok)
2315  : "end";
2316  addLink(OPENDRIVE_LT_PREDECESSOR, elementType, elementID, contactPoint);
2317  }
2318  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_LANE) {
2319  int no = attrs.get<int>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2320  OpenDriveLane& l = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back();
2321  l.predecessor = no;
2322  }
2323  }
2324  break;
2325  case OPENDRIVE_TAG_SUCCESSOR: {
2326  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_ROAD) {
2327  std::string elementType = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTTYPE, myCurrentEdge.id.c_str(), ok);
2328  std::string elementID = attrs.get<std::string>(OPENDRIVE_ATTR_ELEMENTID, myCurrentEdge.id.c_str(), ok);
2329  std::string contactPoint = attrs.hasAttribute(OPENDRIVE_ATTR_CONTACTPOINT)
2330  ? attrs.get<std::string>(OPENDRIVE_ATTR_CONTACTPOINT, myCurrentEdge.id.c_str(), ok)
2331  : "start";
2332  addLink(OPENDRIVE_LT_SUCCESSOR, elementType, elementID, contactPoint);
2333  }
2334  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 2] == OPENDRIVE_TAG_LANE) {
2335  int no = attrs.get<int>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2336  OpenDriveLane& l = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back();
2337  l.successor = no;
2338  }
2339  }
2340  break;
2341  case OPENDRIVE_TAG_GEOMETRY: {
2342  double length = attrs.get<double>(OPENDRIVE_ATTR_LENGTH, myCurrentEdge.id.c_str(), ok);
2343  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2344  double x = attrs.get<double>(OPENDRIVE_ATTR_X, myCurrentEdge.id.c_str(), ok);
2345  double y = attrs.get<double>(OPENDRIVE_ATTR_Y, myCurrentEdge.id.c_str(), ok);
2346  double hdg = attrs.get<double>(OPENDRIVE_ATTR_HDG, myCurrentEdge.id.c_str(), ok);
2347  myCurrentEdge.geometries.push_back(OpenDriveGeometry(length, s, x, y, hdg));
2348  }
2349  break;
2350  case OPENDRIVE_TAG_ELEVATION: {
2351  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2352  double a = attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok);
2353  double b = attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok);
2354  double c = attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok);
2355  double d = attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok);
2356  myCurrentEdge.elevations.push_back(OpenDriveElevation(s, a, b, c, d));
2357  }
2358  break;
2359  case OPENDRIVE_TAG_LINE: {
2360  if (myElementStack.size() > 0 && myElementStack.back() == OPENDRIVE_TAG_GEOMETRY) {
2361  std::vector<double> vals;
2363  }
2364  }
2365  break;
2366  case OPENDRIVE_TAG_SPIRAL: {
2367  std::vector<double> vals;
2368  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CURVSTART, myCurrentEdge.id.c_str(), ok));
2369  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CURVEND, myCurrentEdge.id.c_str(), ok));
2371  }
2372  break;
2373  case OPENDRIVE_TAG_ARC: {
2374  std::vector<double> vals;
2375  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CURVATURE, myCurrentEdge.id.c_str(), ok));
2377  }
2378  break;
2379  case OPENDRIVE_TAG_POLY3: {
2380  std::vector<double> vals;
2381  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok));
2382  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok));
2383  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok));
2384  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok));
2386  }
2387  break;
2388  case OPENDRIVE_TAG_PARAMPOLY3: {
2389  std::vector<double> vals;
2390  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_AU, myCurrentEdge.id.c_str(), ok));
2391  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_BU, myCurrentEdge.id.c_str(), ok));
2392  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CU, myCurrentEdge.id.c_str(), ok));
2393  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_DU, myCurrentEdge.id.c_str(), ok));
2394  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_AV, myCurrentEdge.id.c_str(), ok));
2395  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_BV, myCurrentEdge.id.c_str(), ok));
2396  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_CV, myCurrentEdge.id.c_str(), ok));
2397  vals.push_back(attrs.get<double>(OPENDRIVE_ATTR_DV, myCurrentEdge.id.c_str(), ok));
2398  const std::string pRange = attrs.getOpt<std::string>(OPENDRIVE_ATTR_PRANGE, myCurrentEdge.id.c_str(), ok, "normalized", false);
2399  if (pRange == "normalized") {
2400  vals.push_back(1.0);
2401  } else if (pRange == "arcLength") {
2402  vals.push_back(-1.0);
2403  } else {
2404  WRITE_WARNINGF(TL("Ignoring invalid pRange value '%' for road '%'."), pRange, myCurrentEdge.id);
2405  vals.push_back(1.0);
2406  }
2408  }
2409  break;
2411  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2412  if (myCurrentEdge.laneSections.size() > 0) {
2413  myCurrentEdge.laneSections.back().length = s - myCurrentEdge.laneSections.back().s;
2414  }
2416 
2417  // possibly updated by the next laneSection
2418  myCurrentEdge.laneSections.back().length = myCurrentEdge.length - s;
2419  }
2420  break;
2421  case OPENDRIVE_TAG_LANEOFFSET: {
2422  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2423  double a = attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok);
2424  double b = attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok);
2425  double c = attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok);
2426  double d = attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok);
2427  myCurrentEdge.offsets.push_back(OpenDriveLaneOffset(s, a, b, c, d));
2428  }
2429  break;
2430  case OPENDRIVE_TAG_LEFT:
2432  break;
2433  case OPENDRIVE_TAG_CENTER:
2435  break;
2436  case OPENDRIVE_TAG_RIGHT:
2438  break;
2439  case OPENDRIVE_TAG_LANE: {
2440  std::string type = attrs.get<std::string>(OPENDRIVE_ATTR_TYPE, myCurrentEdge.id.c_str(), ok);
2441  int id = attrs.get<int>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2442  std::string level = attrs.hasAttribute(OPENDRIVE_ATTR_LEVEL)
2443  ? attrs.get<std::string>(OPENDRIVE_ATTR_LEVEL, myCurrentEdge.id.c_str(), ok)
2444  : "";
2446  ls.lanesByDir[myCurrentLaneDirection].push_back(OpenDriveLane(id, level, type));
2447  }
2448  break;
2449  case OPENDRIVE_TAG_SIGNAL: {
2450  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2451  std::string type = attrs.get<std::string>(OPENDRIVE_ATTR_TYPE, myCurrentEdge.id.c_str(), ok);
2452  std::string name = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, myCurrentEdge.id.c_str(), ok, "", false);
2453  const std::string orientation = attrs.get<std::string>(OPENDRIVE_ATTR_ORIENTATION, myCurrentEdge.id.c_str(), ok);
2454  int orientationCode = orientation == "-" ? -1 : orientation == "+" ? 1 : 0;
2455  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2456  bool dynamic = attrs.get<std::string>(OPENDRIVE_ATTR_DYNAMIC, myCurrentEdge.id.c_str(), ok) == "no" ? false : true;
2457  OpenDriveSignal signal = OpenDriveSignal(id, type, name, orientationCode, dynamic, s);
2458  myCurrentEdge.signals.push_back(signal);
2459  mySignals[id] = signal;
2460  }
2461  break;
2463  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentEdge.id.c_str(), ok);
2464  const std::string orientation = attrs.get<std::string>(OPENDRIVE_ATTR_ORIENTATION, myCurrentEdge.id.c_str(), ok);
2465  int orientationCode = orientation == "-" ? -1 : orientation == "+" ? 1 : 0;
2466  double s = attrs.get<double>(OPENDRIVE_ATTR_S, myCurrentEdge.id.c_str(), ok);
2467  OpenDriveSignal signal = OpenDriveSignal(id, "", "", orientationCode, false, s);
2468  myCurrentEdge.signals.push_back(signal);
2469  }
2470  break;
2471  case OPENDRIVE_TAG_CONTROLLER: {
2472  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, nullptr, ok);
2473  std::string name = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, nullptr, ok, "", false);
2475  }
2476  break;
2477  case OPENDRIVE_TAG_CONTROL: {
2478  std::string signalID = attrs.get<std::string>(OPENDRIVE_ATTR_SIGNALID, myCurrentController.id.c_str(), ok);
2479  myCurrentController.signalIDs.push_back(signalID);
2480  if (mySignals.find(signalID) != mySignals.end()) {
2481  mySignals[signalID].controller = myCurrentController.id;
2482  } else {
2483  WRITE_WARNINGF(TL("Ignoring missing signal '%' in controller '%'."), signalID, myCurrentController.id);
2484  }
2485  }
2486  break;
2487  case OPENDRIVE_TAG_VALIDITY: {
2488  int fromLane = attrs.get<int>(OPENDRIVE_ATTR_FROMLANE, myCurrentEdge.id.c_str(), ok);
2489  int toLane = attrs.get<int>(OPENDRIVE_ATTR_TOLANE, myCurrentEdge.id.c_str(), ok);
2490  if (myElementStack.size() >= 1 && (myElementStack.back() == OPENDRIVE_TAG_SIGNAL
2492  myCurrentEdge.signals.back().minLane = fromLane;
2493  myCurrentEdge.signals.back().maxLane = toLane;
2494  }
2495  }
2496  break;
2498  myCurrentJunctionID = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentJunctionID.c_str(), ok);
2499  break;
2500  case OPENDRIVE_TAG_CONNECTION: {
2501  std::string id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, myCurrentJunctionID.c_str(), ok);
2502  myCurrentIncomingRoad = attrs.get<std::string>(OPENDRIVE_ATTR_INCOMINGROAD, myCurrentJunctionID.c_str(), ok);
2504  std::string cp = attrs.get<std::string>(OPENDRIVE_ATTR_CONTACTPOINT, myCurrentJunctionID.c_str(), ok);
2506  myConnectionWasEmpty = true;
2507  }
2508  break;
2509  case OPENDRIVE_TAG_LANELINK: {
2510  int from = attrs.get<int>(OPENDRIVE_ATTR_FROM, myCurrentJunctionID.c_str(), ok);
2511  int to = attrs.get<int>(OPENDRIVE_ATTR_TO, myCurrentJunctionID.c_str(), ok);
2512  Connection c;
2514  c.toEdge = myCurrentConnectingRoad;
2515  c.fromLane = from;
2516  c.toLane = to;
2517  c.fromCP = OPENDRIVE_CP_END;
2518  c.toCP = myCurrentContactPoint;
2519  c.all = false;
2520  if (myEdges.find(c.fromEdge) == myEdges.end()) {
2521  WRITE_ERRORF(TL("In laneLink-element: incoming road '%' is not known."), c.fromEdge);
2522  } else {
2523  OpenDriveEdge* e = myEdges.find(c.fromEdge)->second;
2524  e->connections.insert(c);
2525  myConnectionWasEmpty = false;
2526  }
2527  }
2528  break;
2529  case OPENDRIVE_TAG_WIDTH: {
2530  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 1] == OPENDRIVE_TAG_LANE) {
2531  const double s = attrs.get<double>(OPENDRIVE_ATTR_SOFFSET, myCurrentEdge.id.c_str(), ok);
2532  const double a = attrs.get<double>(OPENDRIVE_ATTR_A, myCurrentEdge.id.c_str(), ok);
2533  const double b = attrs.get<double>(OPENDRIVE_ATTR_B, myCurrentEdge.id.c_str(), ok);
2534  const double c = attrs.get<double>(OPENDRIVE_ATTR_C, myCurrentEdge.id.c_str(), ok);
2535  const double d = attrs.get<double>(OPENDRIVE_ATTR_D, myCurrentEdge.id.c_str(), ok);
2536  OpenDriveLane& l = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back();
2537  l.width = MAX2(l.width, a);
2538  l.widthData.push_back(OpenDriveWidth(s, a, b, c, d));
2539 #ifdef DEBUG_VARIABLE_WIDTHS
2540  if (DEBUG_COND(&myCurrentEdge)) {
2541  std::cout << " road=" << myCurrentEdge.id
2542  << std::setprecision(gPrecision)
2543  << " junction=" << myCurrentEdge.junction
2544  << " section=" << myCurrentEdge.laneSections.size() - 1
2545  << " dir=" << myCurrentLaneDirection << " lane=" << l.id
2546  << " type=" << l.type
2547  << " width=" << l.width
2548  << " a=" << a
2549  << " b=" << b
2550  << " c=" << c
2551  << " d=" << d
2552  << " s=" << s
2553  << " entries=" << l.widthData.size()
2554  << "\n";
2555  }
2556 #endif
2557  }
2558  }
2559  break;
2560  case OPENDRIVE_TAG_ACCESS: {
2561  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 1] == OPENDRIVE_TAG_LANE) {
2562  const double pos = attrs.get<double>(OPENDRIVE_ATTR_SOFFSET, myCurrentEdge.id.c_str(), ok);
2563  std::string rule = attrs.getOpt<std::string>(OPENDRIVE_ATTR_RULE, nullptr, ok, "allow", false); // OpenDRIVE 1.4 without rule value
2564  std::string vClass = attrs.get<std::string>(OPENDRIVE_ATTR_RESTRICTION, myCurrentEdge.id.c_str(), ok);
2565 
2566  std::vector < std::pair<double, LaneAttributeChange >>& attributeChanges = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back().attributeChanges;
2567  std::vector<std::pair<double, LaneAttributeChange> >::iterator i = std::find_if(attributeChanges.begin(), attributeChanges.end(), same_position_finder(pos));
2568  if (i != attributeChanges.end()) {
2569  if (rule == "allow") {
2570  (*i).second.allowed.push_back(vClass);
2571  } else if (rule == "deny") {
2572  (*i).second.denied.push_back(vClass);
2573  }
2574  } else {
2576  if (rule == "allow") {
2577  lac.allowed.push_back(vClass);
2578  } else if (rule == "deny") {
2579  lac.denied.push_back(vClass);
2580  }
2581  attributeChanges.push_back(std::make_pair(pos, lac));
2582  }
2583  }
2584  }
2585  break;
2586  case OPENDRIVE_TAG_SPEED: {
2587  if (myElementStack.size() >= 2 && myElementStack[myElementStack.size() - 1] == OPENDRIVE_TAG_LANE) {
2588  double speed = attrs.get<double>(OPENDRIVE_ATTR_MAX, myCurrentEdge.id.c_str(), ok);
2589  double pos = attrs.get<double>(OPENDRIVE_ATTR_SOFFSET, myCurrentEdge.id.c_str(), ok);
2590  // required for xodr v1.4
2591  const std::string unit = attrs.getOpt<std::string>(OPENDRIVE_ATTR_UNIT, myCurrentEdge.id.c_str(), ok, "", false);
2592  // now convert the speed to reasonable default SI [m/s]
2593  if (!unit.empty()) {
2594  // something to be done at all ?
2595  if (unit == "km/h") {
2596  speed /= 3.6;
2597  }
2598  if (unit == "mph") {
2599  speed *= 1.609344 / 3.6;
2600  }
2601  // IGNORING unknown units.
2602  }
2603  std::vector < std::pair<double, LaneAttributeChange >>& attributeChanges = myCurrentEdge.laneSections.back().lanesByDir[myCurrentLaneDirection].back().attributeChanges;
2604  std::vector<std::pair<double, LaneAttributeChange> >::iterator i = std::find_if(attributeChanges.begin(), attributeChanges.end(), same_position_finder(pos));
2605  if (i != attributeChanges.end()) {
2606  (*i).second.speed = speed;
2607  } else {
2609  attributeChanges.push_back(std::make_pair(pos, lac));
2610  }
2611  }
2612  }
2613  break;
2614  case OPENDRIVE_TAG_OBJECT: {
2615  if (!attrs.hasAttribute(OPENDRIVE_ATTR_ID)) {
2616  WRITE_WARNINGF(TL("Ignoring object without id at edge '%'."), toString(myCurrentEdge.id));
2617  break;
2618  }
2619  OpenDriveObject o;
2620  o.id = attrs.get<std::string>(OPENDRIVE_ATTR_ID, 0, ok);
2621  o.type = attrs.getOpt<std::string>(OPENDRIVE_ATTR_TYPE, o.id.c_str(), ok, "", false);
2622  o.name = attrs.getOpt<std::string>(OPENDRIVE_ATTR_NAME, o.id.c_str(), ok, "", false);
2623  o.s = attrs.get<double>(OPENDRIVE_ATTR_S, o.id.c_str(), ok);
2624  o.t = attrs.get<double>(OPENDRIVE_ATTR_T, o.id.c_str(), ok);
2625  o.width = attrs.getOpt<double>(OPENDRIVE_ATTR_WIDTH, o.id.c_str(), ok, -1);
2626  o.length = attrs.getOpt<double>(OPENDRIVE_ATTR_LENGTH, o.id.c_str(), ok, -1);
2627  o.radius = attrs.getOpt<double>(OPENDRIVE_ATTR_RADIUS, o.id.c_str(), ok, -1);
2628  o.hdg = attrs.getOpt<double>(OPENDRIVE_ATTR_HDG, o.id.c_str(), ok, 0);
2629  myCurrentEdge.objects.push_back(o);
2630  }
2631  break;
2632  case OPENDRIVE_TAG_REPEAT: {
2633  if (myCurrentEdge.objects.empty()) {
2634  WRITE_ERRORF(TL("Repeat without object at edge '%'."), toString(myCurrentEdge.id));
2635  ok = false;
2636  } else {
2638  const std::string baseID = o.id;
2639  double dist = attrs.get<double>(OPENDRIVE_ATTR_DISTANCE, o.id.c_str(), ok);
2640  if (dist == 0) {
2641  // continuous feature. Split into parts (XXX exmport as a single polygon #5235)
2642  dist = OptionsCont::getOptions().getFloat("opendrive.curve-resolution");
2643  }
2644 
2645  myCurrentEdge.objects.pop_back();
2646  const double length = attrs.get<double>(OPENDRIVE_ATTR_LENGTH, o.id.c_str(), ok);
2647  o.s = attrs.getOpt<double>(OPENDRIVE_ATTR_S, o.id.c_str(), ok, o.s);
2648  double wStart = attrs.getOpt<double>(OPENDRIVE_ATTR_WIDTHSTART, o.id.c_str(), ok, o.width);
2649  double wEnd = attrs.getOpt<double>(OPENDRIVE_ATTR_WIDTHEND, o.id.c_str(), ok, o.width);
2650  double tStart = attrs.getOpt<double>(OPENDRIVE_ATTR_TSTART, o.id.c_str(), ok, o.t);
2651  double tEnd = attrs.getOpt<double>(OPENDRIVE_ATTR_TEND, o.id.c_str(), ok, o.t);
2652  int index = 0;
2653  for (double x = 0; x <= length + NUMERICAL_EPS; x += dist) {
2654  o.id = baseID + "#" + toString(index++);
2655  const double a = x / length;
2656  o.width = wStart * (1 - a) + wEnd * a;
2657  o.t = tStart * (1 - a) + tEnd * a;
2658  myCurrentEdge.objects.push_back(o);
2659  o.s += dist;
2660  }
2661  }
2662  }
2663  break;
2664  case OPENDRIVE_TAG_INCLUDE: {
2665  std::string includedFile = attrs.get<std::string>(OPENDRIVE_ATTR_FILE, 0, ok);
2666  if (!FileHelpers::isAbsolute(includedFile)) {
2667  includedFile = FileHelpers::getConfigurationRelative(getFileName(), includedFile);
2668  }
2669  PROGRESS_BEGIN_MESSAGE("Parsing included opendrive from '" + includedFile + "'");
2670  myUseCurrentNode = true;
2671  XMLSubSys::runParser(*this, includedFile);
2673  }
2674  break;
2675  default:
2676  break;
2677  }
2678  myElementStack.push_back(element);
2679 }
2680 
2681 
2682 void
2683 NIImporter_OpenDrive::myCharacters(int element, const std::string& cdata) {
2684  if (element == OPENDRIVE_TAG_GEOREFERENCE) {
2685  size_t i = cdata.find("+proj");
2686  if (i != std::string::npos) {
2687  const std::string proj = cdata.substr(i);
2688  if (proj != "") {
2689  GeoConvHelper* result = nullptr;
2690  Boundary convBoundary;
2691  Boundary origBoundary;
2692  // XXX read values from the header
2693  convBoundary.add(Position(0, 0));
2694  origBoundary.add(Position(0, 0));
2695  try {
2696  result = new GeoConvHelper(proj, myOffset, origBoundary, convBoundary);
2697  GeoConvHelper::setLoaded(*result);
2698  } catch (ProcessError& e) {
2699  WRITE_ERRORF(TL("Could not set projection (%). This can be ignored with --ignore-errors."), std::string(e.what()));
2700  }
2701  }
2702  } else {
2703  WRITE_WARNINGF(TL("geoReference format '%' currently not supported"), cdata);
2704  }
2705  needsCharacterData(false);
2706  }
2707 }
2708 
2709 
2710 void
2712  myElementStack.pop_back();
2713  switch (element) {
2714  case OPENDRIVE_TAG_ROAD:
2716  break;
2718  if (myConnectionWasEmpty) {
2719  Connection c;
2722  c.fromLane = 0;
2723  c.toLane = 0;
2726  c.all = true;
2727  if (myEdges.find(c.fromEdge) == myEdges.end()) {
2728  WRITE_ERRORF(TL("In laneLink-element: incoming road '%' is not known."), c.fromEdge);
2729  } else {
2730  OpenDriveEdge* e = myEdges.find(c.fromEdge)->second;
2731  e->connections.insert(c);
2732  }
2733  }
2734  break;
2735  case OPENDRIVE_TAG_CONTROLLER: {
2737  }
2738  break;
2740  myCurrentEdge.laneSections.back().buildLaneMapping(myTypeContainer);
2741  }
2742  break;
2743  case OPENDRIVE_TAG_SIGNAL:
2746  int intType = -1;
2747  try {
2748  intType = StringUtils::toInt(myCurrentEdge.signals.back().type);
2749  } catch (NumberFormatException&) {
2750  break;
2751  } catch (EmptyData&) {
2752  break;
2753  }
2754  if (intType < 1000001 || (intType > 1000013 && intType != 1000020) || intType == 1000008) {
2755  // not a traffic_light (Section 6.11)
2756  break;
2757  }
2758  double s = myCurrentEdge.signals.back().s;
2759  int minLane = myCurrentEdge.signals.back().minLane;
2760  int maxLane = myCurrentEdge.signals.back().maxLane;
2761  bool foundDrivingType = false;
2763  if (ls.s <= s && ls.s + ls.length > s) {
2764  if (myCurrentEdge.signals.back().orientation < 0) {
2766  if ((minLane < 0 && l.id >= minLane && l.id <= maxLane) && l.type == "driving") {
2767  foundDrivingType = true;
2768  }
2769  }
2770  } else if (myCurrentEdge.signals.back().orientation > 0) { // 0 = center is never used for driving
2772  if ((minLane > 0 && l.id >= minLane && l.id <= maxLane) && l.type == "driving") {
2773  foundDrivingType = true;
2774  }
2775  }
2776  }
2777  }
2778  }
2779  if (!foundDrivingType) { // reject signal / signal reference if not on driving lane
2780  myCurrentEdge.signals.pop_back();
2781  }
2782  }
2783  }
2784  break;
2785  default:
2786  break;
2787  }
2788 }
2789 
2790 
2791 
2792 void
2793 NIImporter_OpenDrive::addLink(LinkType lt, const std::string& elementType,
2794  const std::string& elementID,
2795  const std::string& contactPoint) {
2796  OpenDriveLink l(lt, elementID);
2797  // elementType
2798  if (elementType == "road") {
2800  } else if (elementType == "junction") {
2802  }
2803  // contact point
2804  if (contactPoint == "start") {
2806  } else if (contactPoint == "end") {
2808  }
2809  // add
2810  myCurrentEdge.links.push_back(l);
2811 }
2812 
2813 
2814 void
2815 NIImporter_OpenDrive::addGeometryShape(GeometryType type, const std::vector<double>& vals) {
2816  // checks
2817  if (myCurrentEdge.geometries.size() == 0) {
2818  throw ProcessError(TLF("Mismatching parenthesis in geometry definition for road '%'", myCurrentEdge.id));
2819  }
2821  if (last.type != OPENDRIVE_GT_UNKNOWN) {
2822  throw ProcessError(TLF("Double geometry information for road '%'", myCurrentEdge.id));
2823  }
2824  // set
2825  last.type = type;
2826  last.params = vals;
2827 }
2828 
2829 
2830 bool
2832  if (c1.fromEdge != c2.fromEdge) {
2833  return c1.fromEdge < c2.fromEdge;
2834  }
2835  if (c1.toEdge != c2.toEdge) {
2836  return c1.toEdge < c2.toEdge;
2837  }
2838  if (c1.fromLane != c2.fromLane) {
2839  return c1.fromLane < c2.fromLane;
2840  }
2841  return c1.toLane < c2.toLane;
2842 }
2843 
2844 void
2846 #ifdef DEBUG_VARIABLE_WIDTHS
2847  if (DEBUG_COND(e)) {
2848  gDebugFlag1 = true;
2849  std::cout << "sanitizeWidths e=" << e->id << " sections=" << e->laneSections.size() << "\n";
2850  }
2851 #endif
2852  for (OpenDriveLaneSection& sec : e->laneSections) {
2853  // filter widths within the current section (#5888).
2854  // @note, Short laneSections could also be worth filtering alltogether
2855  if (sec.rightLaneNumber > 0) {
2857  }
2858  if (sec.leftLaneNumber > 0) {
2860  }
2861  }
2862 }
2863 
2864 void
2865 NIImporter_OpenDrive::sanitizeWidths(std::vector<OpenDriveLane>& lanes, double length) {
2866  for (OpenDriveLane& l : lanes) {
2867  if (l.widthData.size() > 0) {
2868  auto& wd = l.widthData;
2869  const double threshold = POSITION_EPS;
2870  double maxNoShort = -std::numeric_limits<double>::max();
2871  double seen = 0;
2872  for (int i = 0; i < (int)wd.size(); i++) {
2873  const double wdLength = i < (int)wd.size() - 1 ? wd[i + 1].s - wd[i].s : length - seen;
2874  seen += wdLength;
2875  if (wdLength > threshold) {
2876  maxNoShort = MAX2(maxNoShort, wd[i].a);
2877  }
2878  }
2879  if (maxNoShort > 0) {
2880  l.width = maxNoShort;
2881  }
2882  }
2883  }
2884 }
2885 
2886 
2887 void
2889  std::vector<OpenDriveLaneSection> newSections;
2890 #ifdef DEBUG_VARIABLE_WIDTHS
2891  if (DEBUG_COND(e)) {
2892  gDebugFlag1 = true;
2893  std::cout << "splitMinWidths e=" << e->id << " sections=" << e->laneSections.size() << "\n";
2894  }
2895 #endif
2896  for (std::vector<OpenDriveLaneSection>::iterator j = e->laneSections.begin(); j != e->laneSections.end(); ++j) {
2897  OpenDriveLaneSection& sec = *j;
2898  std::vector<double> splitPositions;
2899  const double sectionEnd = (j + 1) == e->laneSections.end() ? e->length : (*(j + 1)).s;
2900  const int section = (int)(j - e->laneSections.begin());
2901 #ifdef DEBUG_VARIABLE_WIDTHS
2902  if (DEBUG_COND(e)) {
2903  std::cout << " findWidthSplit section=" << section << " sectionStart=" << sec.s << " sectionOrigStart=" << sec.sOrig << " sectionEnd=" << sectionEnd << "\n";
2904  }
2905 #endif
2906  if (sec.rightLaneNumber > 0) {
2907  findWidthSplit(tc, sec.lanesByDir[OPENDRIVE_TAG_RIGHT], section, sec.sOrig, sectionEnd, splitPositions);
2908  }
2909  if (sec.leftLaneNumber > 0) {
2910  findWidthSplit(tc, sec.lanesByDir[OPENDRIVE_TAG_LEFT], section, sec.sOrig, sectionEnd, splitPositions);
2911  }
2912  newSections.push_back(sec);
2913  std::sort(splitPositions.begin(), splitPositions.end());
2914  // filter out tiny splits
2915  double prevSplit = sec.s;
2916  for (std::vector<double>::iterator it = splitPositions.begin(); it != splitPositions.end();) {
2917  if ((*it) - prevSplit < minDist || sectionEnd - (*it) < minDist) {
2918  // avoid tiny (or duplicate) splits
2919 #ifdef DEBUG_VARIABLE_WIDTHS
2920  if (DEBUG_COND(e)) {
2921  std::cout << " skip close split=" << (*it) << " prevSplit=" << prevSplit << "\n";
2922  }
2923 #endif
2924  it = splitPositions.erase(it);
2925  } else if ((*it) < sec.s) {
2926  // avoid splits for another section
2927 #ifdef DEBUG_VARIABLE_WIDTHS
2928  if (DEBUG_COND(e)) {
2929  std::cout << " skip early split=" << (*it) << " s=" << sec.s << "\n";
2930  }
2931 #endif
2932  it = splitPositions.erase(it);
2933  } else {
2934  prevSplit = *it;
2935  it++;
2936  }
2937  }
2938 
2939  if (splitPositions.size() > 0) {
2940 #ifdef DEBUG_VARIABLE_WIDTHS
2941  if (DEBUG_COND(e)) {
2942  std::cout << " road=" << e->id << " splitMinWidths section=" << section
2943  << " start=" << sec.s
2944  << " origStart=" << sec.sOrig
2945  << " end=" << sectionEnd << " minDist=" << minDist
2946  << " splitPositions=" << toString(splitPositions) << "\n";
2947  }
2948 #endif
2949 #ifdef DEBUG_VARIABLE_WIDTHS
2950  if (DEBUG_COND(e)) {
2951  std::cout << "first section...\n";
2952  }
2953 #endif
2954  recomputeWidths(newSections.back(), sec.sOrig, splitPositions.front(), sec.sOrig, sectionEnd);
2955  for (std::vector<double>::iterator it = splitPositions.begin(); it != splitPositions.end(); ++it) {
2956  OpenDriveLaneSection secNew = sec;
2957  secNew.s = *it;
2958 #ifdef DEBUG_VARIABLE_WIDTHS
2959  if (DEBUG_COND(e)) {
2960  std::cout << "splitAt " << secNew.s << "\n";
2961  }
2962 #endif
2963  newSections.push_back(secNew);
2964  if (secNew.rightLaneNumber > 0) {
2965  setStraightConnections(newSections.back().lanesByDir[OPENDRIVE_TAG_RIGHT]);
2966  }
2967  if (secNew.leftLaneNumber > 0) {
2968  setStraightConnections(newSections.back().lanesByDir[OPENDRIVE_TAG_LEFT]);
2969  }
2970  double end = (it + 1) == splitPositions.end() ? sectionEnd : *(it + 1);
2971  recomputeWidths(newSections.back(), secNew.s, end, sec.sOrig, sectionEnd);
2972  }
2973  }
2974  }
2975  gDebugFlag1 = false;
2976  e->laneSections = newSections;
2977 }
2978 
2979 
2980 void
2981 NIImporter_OpenDrive::findWidthSplit(const NBTypeCont& tc, std::vector<OpenDriveLane>& lanes,
2982  int section, double sectionStart, double sectionEnd,
2983  std::vector<double>& splitPositions) {
2984  UNUSED_PARAMETER(section);
2985  for (const OpenDriveLane& l : lanes) {
2986  const SVCPermissions permissions = tc.getEdgeTypePermissions(l.type) & ~SVC_VULNERABLE;
2987  if (l.widthData.size() > 0 && tc.knows(l.type) && !tc.getEdgeTypeShallBeDiscarded(l.type) && permissions != 0) {
2988  double sPrev = l.widthData.front().s;
2989  double wPrev = l.widthData.front().computeAt(sPrev);
2990 #ifdef DEBUG_VARIABLE_WIDTHS
2991  if (gDebugFlag1) std::cout
2992  << "findWidthSplit section=" << section
2993  << " sectionStart=" << sectionStart
2994  << " sectionEnd=" << sectionEnd
2995  << " lane=" << l.id
2996  << " type=" << l.type
2997  << " widthEntries=" << l.widthData.size() << "\n"
2998  << " s=" << sPrev
2999  << " w=" << wPrev
3000  << "\n";
3001 #endif
3002  for (std::vector<OpenDriveWidth>::const_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 #ifdef DEBUG_VARIABLE_WIDTHS
3006  if (gDebugFlag1) std::cout
3007  << " sEnd=" << sEnd
3008  << " s=" << (*it_w).s
3009  << " a=" << (*it_w).a << " b=" << (*it_w).b << " c=" << (*it_w).c << " d=" << (*it_w).d
3010  << " w=" << w
3011  << "\n";
3012 #endif
3013  const double changeDist = fabs(myMinWidth - wPrev);
3014  if (((wPrev < myMinWidth) && (w > myMinWidth))
3015  || ((wPrev > myMinWidth) && (w < myMinWidth))) {
3016  double splitPos = sPrev + (sEnd - sPrev) / fabs(w - wPrev) * changeDist;
3017  double wSplit = (*it_w).computeAt(splitPos);
3018 #ifdef DEBUG_VARIABLE_WIDTHS
3019  if (gDebugFlag1) {
3020  std::cout << " candidate splitPos=" << splitPos << " w=" << wSplit << "\n";
3021  }
3022 #endif
3023  // ensure that the thin part is actually thin enough
3024  while (wSplit > myMinWidth) {
3025  if (wPrev < myMinWidth) {
3026  // getting wider
3027  splitPos -= POSITION_EPS;
3028  if (splitPos < sPrev) {
3029 #ifdef DEBUG_VARIABLE_WIDTHS
3030  if (gDebugFlag1) {
3031  std::cout << " aborting search splitPos=" << splitPos << " wSplit=" << wSplit << " sPrev=" << sPrev << " wPrev=" << wPrev << "\n";
3032  }
3033 #endif
3034  splitPos = sPrev;
3035  break;
3036  }
3037  } else {
3038  // getting thinner
3039  splitPos += POSITION_EPS;
3040  if (splitPos > sEnd) {
3041 #ifdef DEBUG_VARIABLE_WIDTHS
3042  if (gDebugFlag1) {
3043  std::cout << " aborting search splitPos=" << splitPos << " wSplit=" << wSplit << " sEnd=" << sEnd << " w=" << w << "\n";
3044  }
3045 #endif
3046  splitPos = sEnd;
3047  break;
3048  }
3049  }
3050  wSplit = (*it_w).computeAt(splitPos);
3051 #ifdef DEBUG_VARIABLE_WIDTHS
3052  if (gDebugFlag1) {
3053  std::cout << " refined splitPos=" << splitPos << " w=" << wSplit << "\n";
3054  }
3055 #endif
3056  }
3057  splitPositions.push_back(sectionStart + splitPos);
3058  }
3059  // //wPrev = wSplit;
3060  //} else if ((fabs(wPrev) < NUMERICAL_EPS && w > POSITION_EPS)
3061  // || (wPrev > POSITION_EPS && fabs(w) < NUMERICAL_EPS)) {
3062  // splitPositions.push_back(sectionStart + sPrev);
3063  // if (gDebugFlag1) std::cout << " laneDisappears candidate splitPos=" << sPrev << " wPrev=" << wPrev << " w=" << w<< "\n";
3064  //}
3065  wPrev = w;
3066  sPrev = sEnd;
3067  }
3068  }
3069  }
3070 }
3071 
3072 
3073 void
3074 NIImporter_OpenDrive::setStraightConnections(std::vector<OpenDriveLane>& lanes) {
3075  for (std::vector<OpenDriveLane>::iterator k = lanes.begin(); k != lanes.end(); ++k) {
3076  (*k).predecessor = (*k).id;
3077  }
3078 }
3079 
3080 
3081 void
3082 NIImporter_OpenDrive::recomputeWidths(OpenDriveLaneSection& sec, double start, double end, double sectionStart, double sectionEnd) {
3083  if (sec.rightLaneNumber > 0) {
3084  recomputeWidths(sec.lanesByDir[OPENDRIVE_TAG_RIGHT], start, end, sectionStart, sectionEnd);
3085  }
3086  if (sec.leftLaneNumber > 0) {
3087  recomputeWidths(sec.lanesByDir[OPENDRIVE_TAG_LEFT], start, end, sectionStart, sectionEnd);
3088  }
3089 }
3090 
3091 
3092 void
3093 NIImporter_OpenDrive::recomputeWidths(std::vector<OpenDriveLane>& lanes, double start, double end, double sectionStart, double sectionEnd) {
3094  for (std::vector<OpenDriveLane>::iterator k = lanes.begin(); k != lanes.end(); ++k) {
3095  OpenDriveLane& l = *k;
3096  if (l.widthData.size() > 0) {
3097 #ifdef DEBUG_VARIABLE_WIDTHS
3098  if (gDebugFlag1) std::cout
3099  << "recomputeWidths lane=" << l.id
3100  << " type=" << l.type
3101  << " start=" << start
3102  << " end=" << end
3103  << " sectionStart=" << sectionStart
3104  << " sectionEnd=" << sectionEnd
3105  << " widthEntries=" << l.widthData.size() << "\n"
3106  << "\n";
3107 #endif
3108  l.width = 0;
3109  double sPrev = l.widthData.front().s;
3110  double sPrevAbs = sPrev + sectionStart;
3111  for (std::vector<OpenDriveWidth>::iterator it_w = l.widthData.begin(); it_w != l.widthData.end(); ++it_w) {
3112  double sEnd = (it_w + 1) != l.widthData.end() ? (*(it_w + 1)).s : sectionEnd - sectionStart;
3113  double sEndAbs = sEnd + sectionStart;
3114 #ifdef DEBUG_VARIABLE_WIDTHS
3115  if (gDebugFlag1) std::cout
3116  << " sPrev=" << sPrev << " sPrevAbs=" << sPrevAbs
3117  << " sEnd=" << sEnd << " sEndAbs=" << sEndAbs
3118  << " widthData s=" << (*it_w).s
3119  << " a=" << (*it_w).a
3120  << " b=" << (*it_w).b
3121  << " c=" << (*it_w).c
3122  << " d=" << (*it_w).d
3123  << "\n";
3124 #endif
3125  if (sPrevAbs <= start && sEndAbs >= start) {
3126 #ifdef DEBUG_VARIABLE_WIDTHS
3127  if (gDebugFlag1) {
3128  std::cout << " atStart=" << start << " pos=" << start - sectionStart << " w=" << (*it_w).computeAt(start - sectionStart) << "\n";
3129  }
3130 #endif
3131  l.width = MAX2(l.width, (*it_w).computeAt(start - sectionStart));
3132  }
3133  if (sPrevAbs <= end && sEndAbs >= end) {
3134 #ifdef DEBUG_VARIABLE_WIDTHS
3135  if (gDebugFlag1) {
3136  std::cout << " atEnd=" << end << " pos=" << end - sectionStart << " w=" << (*it_w).computeAt(end - sectionStart) << "\n";
3137  }
3138 #endif
3139  l.width = MAX2(l.width, (*it_w).computeAt(end - sectionStart));
3140  }
3141  if (start <= sPrevAbs && end >= sPrevAbs) {
3142 #ifdef DEBUG_VARIABLE_WIDTHS
3143  if (gDebugFlag1) {
3144  std::cout << " atSPrev=" << sPrev << " w=" << (*it_w).computeAt(sPrev) << "\n";
3145  }
3146 #endif
3147  l.width = MAX2(l.width, (*it_w).computeAt(sPrev));
3148  }
3149  if (start <= sEndAbs && end >= sEndAbs) {
3150 #ifdef DEBUG_VARIABLE_WIDTHS
3151  if (gDebugFlag1) {
3152  std::cout << " atSEnd=" << sEnd << " w=" << (*it_w).computeAt(sEnd) << "\n";
3153  }
3154 #endif
3155  l.width = MAX2(l.width, (*it_w).computeAt(sEnd));
3156  }
3157 #ifdef DEBUG_VARIABLE_WIDTHS
3158  if (gDebugFlag1) {
3159  std::cout << " sPrev=" << sPrev << " sEnd=" << sEnd << " l.width=" << l.width << "\n";
3160  }
3161 #endif
3162  sPrev = sEnd;
3163  sPrevAbs = sEndAbs;
3164  }
3165  }
3166  }
3167 }
3168 
3169 /****************************************************************************/
#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_VULNERABLE
@ 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:1126
const std::string & getID() const
Definition: NBEdge.h:1524
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:1037
static const double UNSPECIFIED_OFFSET
unspecified lane offset
Definition: NBEdge.h:345
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1424
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)
static bool hasNonLinearElevation(const OpenDriveEdge &e)
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.
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:276
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
const PositionVector simplified2(const bool closed, const double eps=NUMERICAL_EPS) const
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