LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_OpenDrive.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 69.6 % 1502 1045
Test Date: 2025-11-14 15:59:05 Functions: 87.2 % 47 41

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

Generated by: LCOV version 2.0-1