LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_OpenDrive.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1027 1492 68.8 %
Date: 2024-05-02 15:31:40 Functions: 41 47 87.2 %

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

Generated by: LCOV version 1.14