LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_OpenStreetMap.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 89.1 % 1602 1428
Test Date: 2026-03-02 16:00:03 Functions: 87.5 % 40 35

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 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_OpenStreetMap.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @author  Walter Bamberger
      19              : /// @author  Gregor Laemmel
      20              : /// @author  Mirko Barthauer
      21              : /// @date    Mon, 14.04.2008
      22              : ///
      23              : // Importer for networks stored in OpenStreetMap format
      24              : /****************************************************************************/
      25              : #include <config.h>
      26              : #include <algorithm>
      27              : #include <set>
      28              : #include <functional>
      29              : #include <sstream>
      30              : #include <limits>
      31              : #include <utils/common/UtilExceptions.h>
      32              : #include <utils/common/StringUtils.h>
      33              : #include <utils/common/ToString.h>
      34              : #include <utils/common/MsgHandler.h>
      35              : #include <utils/common/StringUtils.h>
      36              : #include <utils/common/StringTokenizer.h>
      37              : #include <utils/common/FileHelpers.h>
      38              : #include <utils/geom/GeoConvHelper.h>
      39              : #include <utils/geom/GeomConvHelper.h>
      40              : #include <utils/options/OptionsCont.h>
      41              : #include <utils/xml/SUMOSAXHandler.h>
      42              : #include <utils/xml/SUMOSAXReader.h>
      43              : #include <utils/xml/SUMOXMLDefinitions.h>
      44              : #include <utils/xml/XMLSubSys.h>
      45              : #include <netbuild/NBEdge.h>
      46              : #include <netbuild/NBEdgeCont.h>
      47              : #include <netbuild/NBNode.h>
      48              : #include <netbuild/NBNodeCont.h>
      49              : #include <netbuild/NBNetBuilder.h>
      50              : #include <netbuild/NBOwnTLDef.h>
      51              : #include <netbuild/NBPTLine.h>
      52              : #include <netbuild/NBPTLineCont.h>
      53              : #include <netbuild/NBPTPlatform.h>
      54              : #include <netbuild/NBPTStop.h>
      55              : #include "NILoader.h"
      56              : #include "NIImporter_OpenStreetMap.h"
      57              : 
      58              : //#define DEBUG_LAYER_ELEVATION
      59              : //#define DEBUG_RAIL_DIRECTION
      60              : 
      61              : // ---------------------------------------------------------------------------
      62              : // static members
      63              : // ---------------------------------------------------------------------------
      64              : const double NIImporter_OpenStreetMap::MAXSPEED_UNGIVEN = -1;
      65              : 
      66              : const long long int NIImporter_OpenStreetMap::INVALID_ID = std::numeric_limits<long long int>::max();
      67              : bool NIImporter_OpenStreetMap::myAllAttributes(false);
      68              : std::set<std::string> NIImporter_OpenStreetMap::myExtraAttributes;
      69              : 
      70              : // ===========================================================================
      71              : // Private classes
      72              : // ===========================================================================
      73              : 
      74              : /** @brief Functor which compares two Edges
      75              :  */
      76              : class NIImporter_OpenStreetMap::CompareEdges {
      77              : public:
      78       424607 :     bool operator()(const Edge* e1, const Edge* e2) const {
      79       424607 :         if (e1->myHighWayType != e2->myHighWayType) {
      80       141885 :             return e1->myHighWayType > e2->myHighWayType;
      81              :         }
      82       282722 :         if (e1->myNoLanes != e2->myNoLanes) {
      83        13976 :             return e1->myNoLanes > e2->myNoLanes;
      84              :         }
      85       268746 :         if (e1->myNoLanesForward != e2->myNoLanesForward) {
      86          785 :             return e1->myNoLanesForward > e2->myNoLanesForward;
      87              :         }
      88       267961 :         if (e1->myMaxSpeed != e2->myMaxSpeed) {
      89        13242 :             return e1->myMaxSpeed > e2->myMaxSpeed;
      90              :         }
      91       254719 :         if (e1->myIsOneWay != e2->myIsOneWay) {
      92         9645 :             return e1->myIsOneWay > e2->myIsOneWay;
      93              :         }
      94              :         return e1->myCurrentNodes > e2->myCurrentNodes;
      95              :     }
      96              : };
      97              : 
      98              : // ===========================================================================
      99              : // method definitions
     100              : // ===========================================================================
     101              : // ---------------------------------------------------------------------------
     102              : // static methods
     103              : // ---------------------------------------------------------------------------
     104              : const std::string NIImporter_OpenStreetMap::compoundTypeSeparator("|"); //clang-tidy says: "compundTypeSeparator with
     105              : // static storage duration my throw an exception that cannot be caught
     106              : 
     107              : void
     108         2031 : NIImporter_OpenStreetMap::loadNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
     109         2031 :     NIImporter_OpenStreetMap importer;
     110         2031 :     importer.load(oc, nb);
     111         2031 : }
     112              : 
     113         2031 : NIImporter_OpenStreetMap::NIImporter_OpenStreetMap() = default;
     114              : 
     115         2031 : NIImporter_OpenStreetMap::~NIImporter_OpenStreetMap() {
     116              :     // delete nodes
     117       325151 :     for (auto myUniqueNode : myUniqueNodes) {
     118       323120 :         delete myUniqueNode;
     119              :     }
     120              :     // delete edges
     121        22655 :     for (auto& myEdge : myEdges) {
     122        20624 :         delete myEdge.second;
     123              :     }
     124              :     // delete platform shapes
     125         2500 :     for (auto& myPlatformShape : myPlatformShapes) {
     126          469 :         delete myPlatformShape.second;
     127              :     }
     128         2031 : }
     129              : 
     130              : void
     131         2031 : NIImporter_OpenStreetMap::load(const OptionsCont& oc, NBNetBuilder& nb) {
     132         4062 :     if (!oc.isSet("osm-files")) {
     133         1843 :         return;
     134              :     }
     135          380 :     const std::vector<std::string> files = oc.getStringVector("osm-files");
     136              :     std::vector<SUMOSAXReader*> readers;
     137              : 
     138          190 :     myImportLaneAccess = oc.getBool("osm.lane-access");
     139          190 :     myImportTurnSigns = oc.getBool("osm.turn-lanes");
     140          190 :     myImportSidewalks = oc.getBool("osm.sidewalks");
     141          190 :     myImportBikeAccess = oc.getBool("osm.bike-access");
     142          190 :     myImportCrossings = oc.getBool("osm.crossings");
     143          190 :     myOnewayDualSidewalk = oc.getBool("osm.oneway-reverse-sidewalk");
     144          190 :     myAnnotateDefaults = oc.getBool("osm.annotate-defaults");
     145              : 
     146          190 :     myAllAttributes = OptionsCont::getOptions().getBool("osm.all-attributes");
     147          380 :     std::vector<std::string> extra = OptionsCont::getOptions().getStringVector("osm.extra-attributes");
     148              :     myExtraAttributes.insert(extra.begin(), extra.end());
     149          380 :     if (myExtraAttributes.count("all") != 0) {
     150              :         // import all
     151              :         myExtraAttributes.clear();
     152              :     }
     153              : 
     154              :     // load nodes, first
     155          190 :     NodesHandler nodesHandler(myOSMNodes, myUniqueNodes, oc);
     156          382 :     for (const std::string& file : files) {
     157          388 :         if (!FileHelpers::isReadable(file)) {
     158            0 :             WRITE_ERRORF(TL("Could not open osm-file '%'."), file);
     159            0 :             return;
     160              :         }
     161          194 :         nodesHandler.setFileName(file);
     162              :         nodesHandler.resetHierarchy();
     163          582 :         const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing nodes from osm-file '" + file + "'");
     164          194 :         readers.push_back(XMLSubSys::getSAXReader(nodesHandler));
     165          775 :         if (!readers.back()->parseFirst(file) || !readers.back()->parseSection(SUMO_TAG_NODE) ||
     166          193 :                 MsgHandler::getErrorInstance()->wasInformed()) {
     167              :             return;
     168              :         }
     169          192 :         if (nodesHandler.getDuplicateNodes() > 0) {
     170           36 :             WRITE_MESSAGEF(TL("Found and substituted % osm nodes."), toString(nodesHandler.getDuplicateNodes()));
     171              :         }
     172          192 :         PROGRESS_TIME_MESSAGE(before);
     173              :     }
     174              : 
     175              :     // load edges, then
     176          188 :     EdgesHandler edgesHandler(myOSMNodes, myEdges, myPlatformShapes, nb.getTypeCont());
     177              :     int idx = 0;
     178          380 :     for (const std::string& file : files) {
     179          192 :         edgesHandler.setFileName(file);
     180          192 :         readers[idx]->setHandler(edgesHandler);
     181          576 :         const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing edges from osm-file '" + file + "'");
     182          192 :         if (!readers[idx]->parseSection(SUMO_TAG_WAY)) {
     183              :             // eof already reached, no relations
     184           70 :             delete readers[idx];
     185           70 :             readers[idx] = nullptr;
     186              :         }
     187          192 :         PROGRESS_TIME_MESSAGE(before);
     188          192 :         idx++;
     189              :     }
     190              : 
     191              :     /* Remove duplicate edges with the same shape and attributes */
     192          376 :     if (!oc.getBool("osm.skip-duplicates-check")) {
     193          188 :         int numRemoved = 0;
     194          564 :         PROGRESS_BEGIN_MESSAGE(TL("Removing duplicate edges"));
     195          188 :         if (myEdges.size() > 1) {
     196              :             std::set<const Edge*, CompareEdges> dupsFinder;
     197        21601 :             for (auto it = myEdges.begin(); it != myEdges.end();) {
     198        21425 :                 if (dupsFinder.count(it->second) > 0) {
     199          813 :                     numRemoved++;
     200          813 :                     delete it->second;
     201              :                     myEdges.erase(it++);
     202              :                 } else {
     203              :                     dupsFinder.insert(it->second);
     204              :                     it++;
     205              :                 }
     206              :             }
     207              :         }
     208          188 :         if (numRemoved > 0) {
     209           12 :             WRITE_MESSAGEF(TL("Removed % duplicate osm edges."), toString(numRemoved));
     210              :         }
     211          188 :         PROGRESS_DONE_MESSAGE();
     212              :     }
     213              : 
     214              :     /* Mark which nodes are used (by edges or traffic lights).
     215              :      * This is necessary to detect which OpenStreetMap nodes are for
     216              :      * geometry only */
     217              :     std::map<long long int, int> nodeUsage;
     218              :     // Mark which nodes are used by edges (begin and end)
     219        20812 :     for (const auto& edgeIt : myEdges) {
     220              :         assert(edgeIt.second->myCurrentIsRoad);
     221       135364 :         for (const long long int node : edgeIt.second->myCurrentNodes) {
     222       114740 :             nodeUsage[node]++;
     223              :         }
     224              :     }
     225              :     // Mark which nodes are used by traffic lights or are pedestrian crossings
     226       323227 :     for (const auto& nodesIt : myOSMNodes) {
     227       323039 :         if (nodesIt.second->tlsControlled || nodesIt.second->railwaySignal || (nodesIt.second->pedestrianCrossing && myImportCrossings) /* || nodesIt->second->railwayCrossing*/) {
     228              :             // If the key is not found in the map, the value is automatically
     229              :             // initialized with 0.
     230         3622 :             nodeUsage[nodesIt.first]++;
     231              :         }
     232              :     }
     233              : 
     234              :     /* Instantiate edges
     235              :      * Only those nodes in the middle of an edge which are used by more than
     236              :      * one edge are instantiated. Other nodes are considered as geometry nodes. */
     237              :     NBNodeCont& nc = nb.getNodeCont();
     238              :     NBTrafficLightLogicCont& tlsc = nb.getTLLogicCont();
     239        20812 :     for (const auto& edgeIt : myEdges) {
     240        20624 :         Edge* const e = edgeIt.second;
     241        20624 :         if (!e->myCurrentIsRoad) {
     242          270 :             continue;
     243              :         }
     244        20624 :         if (e->myCurrentNodes.size() < 2) {
     245          270 :             WRITE_WARNINGF(TL("Discarding way '%' because it has only % node(s)"), e->id, e->myCurrentNodes.size());
     246          270 :             continue;
     247              :         }
     248        20354 :         extendRailwayDistances(e, nb.getTypeCont());
     249              :         // build nodes;
     250              :         //  - the from- and to-nodes must be built in any case
     251              :         //  - the in-between nodes are only built if more than one edge references them
     252        20354 :         NBNode* first = insertNodeChecking(e->myCurrentNodes.front(), nc, tlsc);
     253        20354 :         NBNode* last = insertNodeChecking(e->myCurrentNodes.back(), nc, tlsc);
     254              :         NBNode* currentFrom = first;
     255              :         int running = 0;
     256              :         std::vector<long long int> passed;
     257       134945 :         for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
     258       114591 :             passed.push_back(*j);
     259       114591 :             if (nodeUsage[*j] > 1 && j != e->myCurrentNodes.end() - 1 && j != e->myCurrentNodes.begin()) {
     260        18048 :                 NBNode* currentTo = insertNodeChecking(*j, nc, tlsc);
     261        18048 :                 running = insertEdge(e, running, currentFrom, currentTo, passed, nb, first, last);
     262              :                 currentFrom = currentTo;
     263              :                 passed.clear();
     264        18048 :                 passed.push_back(*j);
     265              :             }
     266              :         }
     267        20354 :         if (running == 0) {
     268              :             running = -1;
     269              :         }
     270        20354 :         insertEdge(e, running, currentFrom, last, passed, nb, first, last);
     271        20354 :     }
     272              : 
     273              :     /* Collect edges which explicitly are part of a roundabout and store the edges of each
     274              :      * detected roundabout */
     275          188 :     nb.getEdgeCont().extractRoundabouts();
     276              : 
     277          188 :     if (myImportCrossings) {
     278              :         /* After edges are instantiated
     279              :          * nodes are parsed again to add pedestrian crossings to them
     280              :          * This is only executed if crossings are imported and not guessed */
     281            2 :         const double crossingWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
     282              : 
     283           14 :         for (auto item : nodeUsage) {
     284           13 :             NIOSMNode* osmNode = myOSMNodes.find(item.first)->second;
     285           13 :             if (osmNode->pedestrianCrossing) {
     286            5 :                 NBNode* n = osmNode->node;
     287            5 :                 EdgeVector incomingEdges = n->getIncomingEdges();
     288            5 :                 EdgeVector outgoingEdges = n->getOutgoingEdges();
     289              :                 size_t incomingEdgesNo = incomingEdges.size();
     290              :                 size_t outgoingEdgesNo = outgoingEdges.size();
     291              : 
     292           21 :                 for (size_t i = 0; i < incomingEdgesNo; i++) {
     293              :                     /* Check if incoming edge has driving lanes(and sidewalks)
     294              :                      * if not, ignore
     295              :                      * if yes, check if there is a corresponding outgoing edge for the opposite direction
     296              :                      *   -> if yes, check if it has driving lanes
     297              :                      *          --> if yes, do the crossing
     298              :                      *          --> if no, only do the crossing with the incoming edge (usually one lane roads with two sidewalks)
     299              :                      *   -> if not, do nothing as we don't have a sidewalk in the opposite direction */
     300           16 :                     auto const iEdge = incomingEdges[i];
     301              : 
     302           16 :                     if (iEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1
     303           16 :                             && iEdge->getSpecialLane(SVC_PEDESTRIAN) > -1) {
     304           12 :                         std::string const& iEdgeId = iEdge->getID();
     305              :                         std::size_t const m = iEdgeId.find_first_of("#");
     306           12 :                         std::string const& iWayId = iEdgeId.substr(0, m);
     307           53 :                         for (size_t j = 0; j < outgoingEdgesNo; j++) {
     308           41 :                             auto const oEdge = outgoingEdges[j];
     309              :                             // Searching for a corresponding outgoing edge (based on OSM way identifier)
     310              :                             // with at least a pedestrian lane, going in the opposite direction
     311           41 :                             if (oEdge->getID().find(iWayId) != std::string::npos
     312           19 :                                     && oEdge->getSpecialLane(SVC_PEDESTRIAN) > -1
     313           79 :                                     && oEdge->getID().rfind(iWayId, 0) != 0) {
     314            9 :                                 EdgeVector edgeVector = EdgeVector{ iEdge };
     315            9 :                                 if (oEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1) {
     316            3 :                                     edgeVector.push_back(oEdge);
     317              :                                 }
     318              : 
     319            9 :                                 if (!n->checkCrossingDuplicated(edgeVector)) {
     320            9 :                                     n->addCrossing(edgeVector, crossingWidth, false);
     321              :                                 }
     322            9 :                             }
     323              :                         }
     324              :                     }
     325              :                 }
     326           21 :                 for (size_t i = 0; i < outgoingEdgesNo; i++) {
     327              :                     // Same checks as above for loop, but for outgoing edges
     328           16 :                     auto const oEdge = outgoingEdges[i];
     329              : 
     330           16 :                     if (oEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1
     331           16 :                             && oEdge->getSpecialLane(SVC_PEDESTRIAN) > -1) {
     332           10 :                         std::string const& oEdgeId = oEdge->getID();
     333              :                         std::size_t const m = oEdgeId.find_first_of("#");
     334           10 :                         std::string const& iWayId = oEdgeId.substr(0, m);
     335           45 :                         for (size_t j = 0; j < incomingEdgesNo; j++) {
     336           35 :                             auto const iEdge = incomingEdges[j];
     337           35 :                             if (iEdge->getID().find(iWayId) != std::string::npos
     338           17 :                                     && iEdge->getSpecialLane(SVC_PEDESTRIAN) > -1
     339           69 :                                     && iEdge->getID().rfind(iWayId, 0) != 0) {
     340            7 :                                 EdgeVector edgeVector = EdgeVector{ oEdge };
     341            7 :                                 if (iEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1) {
     342            3 :                                     edgeVector.push_back(iEdge);
     343              :                                 }
     344              : 
     345            7 :                                 if (!n->checkCrossingDuplicated(edgeVector)) {
     346            7 :                                     n->addCrossing(edgeVector, crossingWidth, false);
     347              :                                 }
     348            7 :                             }
     349              :                         }
     350              :                     }
     351              :                 }
     352            5 :             }
     353              :         }
     354              :     }
     355              : 
     356          188 :     const double layerElevation = oc.getFloat("osm.layer-elevation");
     357          188 :     if (layerElevation > 0) {
     358            0 :         reconstructLayerElevation(layerElevation, nb);
     359              :     }
     360              : 
     361              :     // revise pt stops; remove stops on deleted edges
     362          188 :     nb.getPTStopCont().cleanupDeleted(nb.getEdgeCont());
     363              : 
     364              :     // load relations (after edges are built since we want to apply
     365              :     // turn-restrictions directly to NBEdges)
     366              :     RelationHandler relationHandler(myOSMNodes, myEdges, &(nb.getPTStopCont()), myPlatformShapes,
     367          188 :                                     &nb.getPTLineCont(), oc);
     368              :     idx = 0;
     369          380 :     for (const std::string& file : files) {
     370          192 :         if (readers[idx] != nullptr) {
     371          122 :             relationHandler.setFileName(file);
     372          122 :             readers[idx]->setHandler(relationHandler);
     373          366 :             const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing relations from osm-file '" + file + "'");
     374          122 :             readers[idx]->parseSection(SUMO_TAG_RELATION);
     375          122 :             PROGRESS_TIME_MESSAGE(before);
     376          122 :             delete readers[idx];
     377              :         }
     378          192 :         idx++;
     379              :     }
     380              : 
     381              :     // declare additional stops that are not anchored to a (road)-way or route relation
     382              :     std::set<std::string> stopNames;
     383         1749 :     for (const auto& item : nb.getPTStopCont().getStops()) {
     384         3122 :         stopNames.insert(item.second->getName());
     385              :     }
     386       323227 :     for (const auto& item : myOSMNodes) {
     387       323039 :         const NIOSMNode* n = item.second;
     388       323039 :         if (n->ptStopPosition && stopNames.count(n->name) == 0) {
     389          139 :             Position ptPos(n->lon, n->lat, n->ele);
     390          139 :             if (!NBNetBuilder::transformCoordinate(ptPos)) {
     391            0 :                 WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);
     392              :             }
     393          139 :             SumoXMLTag element = isRailway(n->permissions) ? SUMO_TAG_TRAIN_STOP : SUMO_TAG_BUS_STOP;
     394          278 :             std::shared_ptr<NBPTStop> ptStop = std::make_shared<NBPTStop>(element, toString(n->id), ptPos, "", "", n->ptStopLength, n->name, n->permissions);
     395          278 :             nb.getPTStopCont().insert(ptStop, true);
     396              :         }
     397              :     }
     398          378 : }
     399              : 
     400              : // ---------------------------------------------------------------------------
     401              : // definitions of NIImporter_OpenStreetMap-methods
     402              : // ---------------------------------------------------------------------------
     403              : 
     404              : NBNode*
     405        58865 : NIImporter_OpenStreetMap::insertNodeChecking(long long int id, NBNodeCont& nc, NBTrafficLightLogicCont& tlsc) {
     406        58865 :     NBNode* node = nc.retrieve(toString(id));
     407        58865 :     if (node == nullptr) {
     408        31092 :         NIOSMNode* n = myOSMNodes.find(id)->second;
     409        31092 :         Position pos(n->lon, n->lat, n->ele);
     410        31092 :         if (!NBNetBuilder::transformCoordinate(pos, true)) {
     411            0 :             WRITE_ERRORF("Unable to project coordinates for junction '%'.", id);
     412            0 :             return nullptr;
     413              :         }
     414        31092 :         node = new NBNode(toString(id), pos);
     415        31092 :         if (!nc.insert(node)) {
     416            0 :             WRITE_ERRORF(TL("Could not insert junction '%'."), toString(id));
     417            0 :             delete node;
     418            0 :             return nullptr;
     419              :         }
     420        31092 :         n->node = node;
     421        31092 :         if (n->railwayCrossing) {
     422         1592 :             if (n->getParameter("crossing:barrier") != "no") {
     423          661 :                 node->reinit(pos, SumoXMLNodeType::RAIL_CROSSING);
     424          270 :             } else if (n->getParameter("crossing.light") == "yes") {
     425            0 :                 node->reinit(pos, SumoXMLNodeType::TRAFFIC_LIGHT);
     426              :             }
     427        30296 :         } else if (n->railwaySignal) {
     428          863 :             node->reinit(pos, SumoXMLNodeType::RAIL_SIGNAL);
     429        29433 :         } else if (n->tlsControlled) {
     430              :             // ok, this node is a traffic light node where no other nodes
     431              :             //  participate
     432              :             // @note: The OSM-community has not settled on a schema for differentiating between fixed and actuated lights
     433         2456 :             TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(
     434         2456 :                                         OptionsCont::getOptions().getString("tls.default-type"));
     435         2456 :             NBOwnTLDef* tlDef = new NBOwnTLDef(toString(id), node, 0, type);
     436         2456 :             if (!tlsc.insert(tlDef)) {
     437              :                 // actually, nothing should fail here
     438            0 :                 delete tlDef;
     439            0 :                 throw ProcessError(TLF("Could not allocate tls '%'.", toString(id)));
     440              :             }
     441              :         }
     442        31092 :         if (n->railwayBufferStop) {
     443          124 :             node->setParameter("buffer_stop", "true");
     444              :             node->setFringeType(FringeType::INNER);
     445              :         }
     446        31092 :         if (n->railwaySignal) {
     447          863 :             if (n->myRailDirection == WAY_FORWARD) {
     448         1308 :                 node->setParameter(NBTrafficLightDefinition::OSM_SIGNAL_DIRECTION, "forward");
     449          209 :             } else if (n->myRailDirection == WAY_BACKWARD) {
     450          404 :                 node->setParameter(NBTrafficLightDefinition::OSM_SIGNAL_DIRECTION, "backward");
     451              :             }
     452              :         }
     453        31092 :         node->updateParameters(n->getParametersMap());
     454              :     }
     455              :     return node;
     456              : }
     457              : 
     458              : 
     459              : int
     460        38620 : NIImporter_OpenStreetMap::insertEdge(Edge* e, int index, NBNode* from, NBNode* to,
     461              :                                      const std::vector<long long int>& passed, NBNetBuilder& nb,
     462              :                                      const NBNode* first, const NBNode* last) {
     463              :     NBNodeCont& nc = nb.getNodeCont();
     464              :     NBEdgeCont& ec = nb.getEdgeCont();
     465              :     NBTypeCont& tc = nb.getTypeCont();
     466              :     NBPTStopCont& sc = nb.getPTStopCont();
     467              : 
     468              :     NBTrafficLightLogicCont& tlsc = nb.getTLLogicCont();
     469              :     // patch the id
     470        38620 :     std::string id = toString(e->id);
     471        38620 :     if (from == nullptr || to == nullptr) {
     472            0 :         WRITE_ERRORF("Discarding edge '%' because the nodes could not be built.", id);
     473            0 :         return index;
     474              :     }
     475        38620 :     if (index >= 0) {
     476        53508 :         id = id + "#" + toString(index);
     477              :     } else {
     478        11866 :         index = 0;
     479              :     }
     480        38620 :     if (from == to) {
     481              :         assert(passed.size() >= 2);
     482          109 :         if (passed.size() == 2) {
     483            0 :             WRITE_WARNINGF(TL("Discarding edge '%' which connects two identical nodes without geometry."), id);
     484            0 :             return index;
     485              :         }
     486              :         // in the special case of a looped way split again using passed
     487          109 :         int intermediateIndex = (int) passed.size() / 2;
     488          109 :         NBNode* intermediate = insertNodeChecking(passed[intermediateIndex], nc, tlsc);
     489          109 :         std::vector<long long int> part1(passed.begin(), passed.begin() + intermediateIndex + 1);
     490          109 :         std::vector<long long int> part2(passed.begin() + intermediateIndex, passed.end());
     491          109 :         index = insertEdge(e, index, from, intermediate, part1, nb, first, last);
     492          109 :         return insertEdge(e, index, intermediate, to, part2, nb, first, last);
     493          109 :     }
     494        38511 :     const int newIndex = index + 1;
     495        38511 :     const std::string type = usableType(e->myHighWayType, id, tc);
     496        38511 :     if (type == "") {  // we do not want to import it
     497              :         return newIndex;
     498              :     }
     499        37429 :     std::string routingType = "";
     500        37429 :     int numLanesForward = tc.getEdgeTypeNumLanes(type);
     501        37429 :     int numLanesBackward = tc.getEdgeTypeNumLanes(type);
     502        37429 :     double speed = tc.getEdgeTypeSpeed(type);
     503        37429 :     bool defaultsToOneWay = tc.getEdgeTypeIsOneWay(type);
     504        37429 :     const SVCPermissions defaultPermissions = tc.getEdgeTypePermissions(type);
     505        37429 :     SVCPermissions extra = myImportBikeAccess ? e->myExtraAllowed : (e->myExtraAllowed & ~SVC_BICYCLE);
     506        37429 :     const SVCPermissions extraDis = myImportBikeAccess ? e->myExtraDisallowed : (e->myExtraDisallowed & ~SVC_BICYCLE);
     507              :     std::vector<SumoXMLAttr> defaults;
     508              :     // extra permissions are more specific than extra prohibitions except for buses (which come from the less specific psv tag)
     509        37429 :     if ((extraDis & SVC_BUS) && (extra & SVC_BUS)) {
     510            1 :         extra = extra & ~SVC_BUS;
     511              :     }
     512        37429 :     SVCPermissions permissions = (defaultPermissions & ~extraDis) | extra;
     513        37429 :     if (defaultPermissions == SVC_SHIP) {
     514              :         // extra permission apply to the ships operating on the route rather than the waterway
     515              :         permissions = defaultPermissions;
     516              :     }
     517        37429 :     if (defaultsToOneWay && defaultPermissions == SVC_PEDESTRIAN && (permissions & (~SVC_PEDESTRIAN)) != 0) {
     518              :         defaultsToOneWay = false;
     519              :     }
     520        39699 :     if ((permissions & SVC_RAIL) != 0 && e->myExtraTags.count("electrified") != 0) {
     521          543 :         permissions |= (SVC_RAIL_ELECTRIC | SVC_RAIL_FAST);
     522              :     }
     523              : 
     524              :     // convert the shape
     525        37429 :     PositionVector shape;
     526        37429 :     double distanceStart = myOSMNodes[passed.front()]->positionMeters;
     527        37429 :     double distanceEnd = myOSMNodes[passed.back()]->positionMeters;
     528        37429 :     const bool useDistance = distanceStart != std::numeric_limits<double>::max() && distanceEnd != std::numeric_limits<double>::max();
     529        37429 :     if (useDistance) {
     530              :         // negative sign denotes counting in the other direction
     531          486 :         if (distanceStart < distanceEnd) {
     532          396 :             distanceStart *= -1;
     533              :         } else {
     534           90 :             distanceEnd *= -1;
     535              :         }
     536              :     } else {
     537              :         distanceStart = 0;
     538              :         distanceEnd = 0;
     539              :     }
     540              :     // get additional direction information
     541        74858 :     int nodeDirection = myOSMNodes.find(StringUtils::toLong(from->getID()))->second->myRailDirection |
     542        74858 :                         myOSMNodes.find(StringUtils::toLong(to->getID()))->second->myRailDirection;
     543              : 
     544              :     std::vector<std::shared_ptr<NBPTStop> > ptStops;
     545       161616 :     for (long long i : passed) {
     546       124187 :         NIOSMNode* n = myOSMNodes.find(i)->second;
     547              :         // recheck permissions, maybe they got assigned to a strange edge, see #11656
     548       124187 :         if (n->ptStopPosition && (n->permissions == 0 || (permissions & n->permissions) != 0)) {
     549         3372 :             std::shared_ptr<NBPTStop> existingPtStop = sc.get(toString(n->id));
     550         1686 :             if (existingPtStop != nullptr) {
     551          396 :                 existingPtStop->registerAdditionalEdge(toString(e->id), id);
     552              :             } else {
     553         1488 :                 Position ptPos(n->lon, n->lat, n->ele);
     554         1488 :                 if (!NBNetBuilder::transformCoordinate(ptPos)) {
     555            0 :                     WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);
     556              :                 }
     557         1488 :                 SumoXMLTag element = isRailway(n->permissions) ? SUMO_TAG_TRAIN_STOP : SUMO_TAG_BUS_STOP;
     558         2976 :                 ptStops.push_back(std::make_shared<NBPTStop>(element, toString(n->id), ptPos, id, toString(e->id), n->ptStopLength, n->name, n->permissions));
     559         2976 :                 sc.insert(ptStops.back());
     560              :             }
     561              :         }
     562       124187 :         if (n->railwaySignal) {
     563         1723 :             nodeDirection |= n->myRailDirection;
     564              :         }
     565       124187 :         Position pos(n->lon, n->lat, n->ele);
     566       124187 :         shape.push_back(pos);
     567              :     }
     568              : #ifdef DEBUG_LAYER_ELEVATION
     569              :     if (e->id == "DEBUGID") {
     570              :         std::cout
     571              :                 << " id=" << id << " from=" << from->getID() << " fromRailDirection=" << myOSMNodes.find(StringUtils::toLong(from->getID()))->second->myRailDirection
     572              :                 << " to=" << to->getID() << " toRailDirection=" << myOSMNodes.find(StringUtils::toLong(to->getID()))->second->myRailDirection
     573              :                 << " origRailDirection=" << e->myRailDirection
     574              :                 << " nodeDirection=" << nodeDirection
     575              :                 << "\n";
     576              :     }
     577              : #endif
     578        37429 :     if (e->myRailDirection == WAY_UNKNOWN && nodeDirection != WAY_UNKNOWN && nodeDirection != WAY_FORWARD
     579          990 :             && nodeDirection != (WAY_FORWARD | WAY_UNKNOWN)) {
     580              :         //std::cout << "way " << e->id << " nodeDirection=" << nodeDirection << " origDirection=" << e->myRailDirection << "\n";
     581              :         // heuristic: assume that the mapped way direction indicates
     582              :         // potential driving direction
     583          223 :         e->myRailDirection = WAY_BOTH;
     584              :     }
     585        37429 :     if (!NBNetBuilder::transformCoordinates(shape)) {
     586            0 :         WRITE_ERRORF("Unable to project coordinates for edge '%'.", id);
     587              :     }
     588              : 
     589              :     SVCPermissions forwardPermissions = permissions;
     590              :     SVCPermissions backwardPermissions = permissions;
     591        37429 :     const std::string streetName = isRailway(permissions) && e->ref != "" ? e->ref : e->streetName;
     592        37429 :     if (streetName == e->ref) {
     593        49062 :         e->unsetParameter("ref"); // avoid superfluous param for railways
     594              :     }
     595        37429 :     double forwardWidth = tc.getEdgeTypeWidth(type);
     596        37429 :     double backwardWidth = tc.getEdgeTypeWidth(type);
     597        37429 :     double sidewalkWidth = tc.getEdgeTypeSidewalkWidth(type);
     598        37429 :     bool addSidewalk = sidewalkWidth != NBEdge::UNSPECIFIED_WIDTH;
     599        37429 :     if (myImportSidewalks) {
     600         5281 :         if (addSidewalk) {
     601              :             // only use sidewalk width from typemap but don't add sidewalks
     602              :             // unless OSM specifies them
     603              :             addSidewalk = false;
     604              :         } else {
     605         8258 :             sidewalkWidth = OptionsCont::getOptions().getFloat("default.sidewalk-width");
     606              :         }
     607              :     }
     608        37429 :     double bikeLaneWidth = tc.getEdgeTypeBikeLaneWidth(type);
     609        37429 :     const std::string& onewayBike = e->myExtraTags["oneway:bicycle"];
     610        37429 :     if (onewayBike == "false" || onewayBike == "no" || onewayBike == "0") {
     611          606 :         e->myCyclewayType = e->myCyclewayType == WAY_UNKNOWN ? WAY_BACKWARD : (WayType)(e->myCyclewayType | WAY_BACKWARD);
     612              :     }
     613              : 
     614        37429 :     const bool addBikeLane = bikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH ||
     615        41649 :                              (myImportBikeAccess && (((e->myCyclewayType & WAY_BOTH) != 0 || e->myExtraTags.count("segregated") != 0) &&
     616           68 :                                      !(e->myCyclewayType == WAY_BACKWARD && (e->myBuswayType & WAY_BOTH) != 0)));
     617        37429 :     if (addBikeLane && bikeLaneWidth == NBEdge::UNSPECIFIED_WIDTH) {
     618          136 :         bikeLaneWidth = OptionsCont::getOptions().getFloat("default.bikelane-width");
     619              :     }
     620              :     // check directions
     621              :     bool addForward = true;
     622              :     bool addBackward = true;
     623        37429 :     const bool explicitTwoWay = e->myIsOneWay == "no";
     624        36531 :     if ((e->myIsOneWay == "true" || e->myIsOneWay == "yes" || e->myIsOneWay == "1"
     625        29154 :             || (defaultsToOneWay && e->myIsOneWay != "no" && e->myIsOneWay != "false" && e->myIsOneWay != "0"))
     626        63122 :             && e->myRailDirection != WAY_BOTH) {
     627              :         addBackward = false;
     628              :     }
     629        37429 :     if (e->myIsOneWay == "-1" || e->myIsOneWay == "reverse" || e->myRailDirection == WAY_BACKWARD) {
     630              :         // one-way in reversed direction of way
     631              :         addForward = false;
     632              :         addBackward = true;
     633              :     }
     634         8802 :     if (!e->myIsOneWay.empty() && e->myIsOneWay != "false" && e->myIsOneWay != "no" && e->myIsOneWay != "true"
     635        44810 :             && e->myIsOneWay != "yes" && e->myIsOneWay != "-1" && e->myIsOneWay != "1" && e->myIsOneWay != "reverse") {
     636            0 :         WRITE_WARNINGF(TL("New value for oneway found: %"), e->myIsOneWay);
     637              :     }
     638        37429 :     if ((permissions == SVC_BICYCLE || permissions == (SVC_BICYCLE | SVC_PEDESTRIAN) || permissions == SVC_PEDESTRIAN)) {
     639        16064 :         if (addBackward && (onewayBike == "true" || onewayBike == "yes" || onewayBike == "1")) {
     640              :             addBackward = false;
     641              :         }
     642        16064 :         if (addForward && (onewayBike == "reverse" || onewayBike == "-1")) {
     643              :             addForward = false;
     644              :         }
     645        16064 :         if (!addBackward && (onewayBike == "false" || onewayBike == "no" || onewayBike == "0")) {
     646              :             addBackward = true;
     647              :         }
     648              :     }
     649              :     bool ok = true;
     650              :     // if we had been able to extract the number of lanes, override the highway type default
     651        37429 :     if (e->myNoLanes > 0) {
     652         6987 :         if (addForward && !addBackward) {
     653         4694 :             numLanesForward = e->myNoLanesForward > 0 ? e->myNoLanesForward : e->myNoLanes;
     654         2293 :         } else if (!addForward && addBackward) {
     655            2 :             numLanesBackward = e->myNoLanesForward < 0 ? -e->myNoLanesForward : e->myNoLanes;
     656              :         } else {
     657         2291 :             if (e->myNoLanesForward > 0) {
     658              :                 numLanesForward = e->myNoLanesForward;
     659         1628 :             } else if (e->myNoLanesForward < 0) {
     660           26 :                 numLanesForward = e->myNoLanes + e->myNoLanesForward;
     661              :             } else {
     662         1602 :                 numLanesForward = (int) std::ceil(e->myNoLanes / 2.0);
     663              :             }
     664         2291 :             numLanesBackward = e->myNoLanes - numLanesForward;
     665              :             // sometimes ways are tagged according to their physical width of a single
     666              :             // lane but they are intended for traffic in both directions
     667              :             numLanesForward = MAX2(1, numLanesForward);
     668              :             numLanesBackward = MAX2(1, numLanesBackward);
     669              :         }
     670        30442 :     } else if (e->myNoLanes == 0) {
     671            0 :         WRITE_WARNINGF(TL("Skipping edge '%' because it has zero lanes."), id);
     672              :         ok = false;
     673              :     } else {
     674              :         // the total number of lanes is not known but at least one direction
     675        30442 :         if (e->myNoLanesForward > 0) {
     676              :             numLanesForward = e->myNoLanesForward;
     677        30442 :         } else if ((e->myBuswayType & WAY_FORWARD) != 0 && (extraDis & SVC_PASSENGER) == 0) {
     678              :             // if we have a busway lane, yet cars may drive this implies at least two lanes
     679              :             numLanesForward = MAX2(numLanesForward, 2);
     680              :         }
     681        30442 :         if (e->myNoLanesForward < 0) {
     682            0 :             numLanesBackward = -e->myNoLanesForward;
     683        30442 :         } else if ((e->myBuswayType & WAY_BACKWARD) != 0 && (extraDis & SVC_PASSENGER) == 0) {
     684              :             // if we have a busway lane, yet cars may drive this implies at least two lanes
     685              :             numLanesBackward = MAX2(numLanesForward, 2);
     686              :         }
     687        30442 :         if (myAnnotateDefaults && e->myNoLanesForward == 0) {
     688          119 :             defaults.push_back(SUMO_ATTR_NUMLANES);
     689              :         }
     690              :     }
     691              :     // deal with busways that run in the opposite direction of a one-way street
     692        37429 :     if (!addForward && (e->myBuswayType & WAY_FORWARD) != 0) {
     693              :         addForward = true;
     694              :         forwardPermissions = SVC_BUS;
     695              :         numLanesForward = 1;
     696              :     }
     697        37429 :     if (!addBackward && (e->myBuswayType & WAY_BACKWARD) != 0) {
     698              :         addBackward = true;
     699              :         backwardPermissions = SVC_BUS;
     700              :         numLanesBackward = 1;
     701              :     }
     702              :     // width is meant for raw lane count before adding sidewalks or cycleways
     703        37438 :     const int taggedLanes = (addForward ? numLanesForward : 0) + (addBackward ? numLanesBackward : 0);
     704         1432 :     if (e->myWidth > 0 && e->myWidthLanesForward.size() == 0 && e->myWidthLanesBackward.size() == 0 && taggedLanes != 0
     705        40475 :             && !OptionsCont::getOptions().getBool("ignore-widths")) {
     706              :         // width is tagged excluding sidewalks and cycleways
     707         1250 :         forwardWidth = e->myWidth / taggedLanes;
     708              :         backwardWidth = forwardWidth;
     709              :     }
     710              : 
     711              :     // if we had been able to extract the maximum speed, override the type's default
     712        37429 :     if (e->myMaxSpeed != MAXSPEED_UNGIVEN) {
     713              :         speed = e->myMaxSpeed;
     714        25147 :     } else if (myAnnotateDefaults) {
     715          124 :         defaults.push_back(SUMO_ATTR_SPEED);
     716              :     }
     717              :     double speedBackward = speed;
     718        37429 :     if (e->myMaxSpeedBackward != MAXSPEED_UNGIVEN) {
     719              :         speedBackward = e->myMaxSpeedBackward;
     720              :     }
     721        37429 :     if (speed <= 0 || speedBackward <= 0) {
     722            0 :         WRITE_WARNINGF(TL("Skipping edge '%' because it has speed %."), id, speed);
     723              :         ok = false;
     724              :     }
     725        37429 :     if (e->myNoLanes == 1 && addForward && addBackward) {
     726              :         // narrow road which now receives a total of 2 lanes but has less capacity than implied
     727          189 :         if (e->myWidth < 0 && e->myWidthLanesForward.size() == 0 && e->myWidthLanesBackward.size() == 0) {
     728          140 :             if (forwardWidth == NBEdge::UNSPECIFIED_WIDTH) {
     729              :                 forwardWidth = SUMO_const_laneWidth;
     730              :             }
     731          140 :             if (backwardWidth == NBEdge::UNSPECIFIED_WIDTH) {
     732              :                 backwardWidth = SUMO_const_laneWidth;
     733              :             }
     734          140 :             forwardWidth /= 2;
     735          140 :             backwardWidth /= 2;
     736              :         }
     737          189 :         if (e->myWidth < 5) {
     738              :             routingType = "narrow";
     739              :         }
     740              :     }
     741              :     // deal with cycleways that run in the opposite direction of a one-way street
     742        37429 :     WayType cyclewayType = e->myCyclewayType; // make a copy because we do some temporary modifications
     743        37429 :     if (addBikeLane) {
     744          464 :         if (!addForward && (cyclewayType & WAY_FORWARD) != 0) {
     745              :             addForward = true;
     746              :             forwardPermissions = SVC_BICYCLE;
     747              :             forwardWidth = bikeLaneWidth;
     748              :             numLanesForward = 1;
     749              :             // do not add an additional cycle lane
     750              :             cyclewayType = (WayType)(cyclewayType & ~WAY_FORWARD);
     751              :         }
     752          464 :         if (!addBackward && (cyclewayType & WAY_BACKWARD) != 0) {
     753              :             addBackward = true;
     754              :             backwardPermissions = SVC_BICYCLE;
     755              :             backwardWidth = bikeLaneWidth;
     756              :             numLanesBackward = 1;
     757              :             // do not add an additional cycle lane
     758              :             cyclewayType = (WayType)(cyclewayType & ~WAY_BACKWARD);
     759              :         }
     760              :     }
     761              :     // deal with sidewalks that run in the opposite direction of a one-way street
     762        37429 :     WayType sidewalkType = e->mySidewalkType; // make a copy because we do some temporary modifications
     763        37429 :     if (sidewalkType == WAY_UNKNOWN && (e->myExtraAllowed & SVC_PEDESTRIAN) != 0 && (permissions & SVC_PASSENGER) != 0) {
     764              :         // do not assume shared space unless sidewalk is actively disabled
     765           41 :         if (myOnewayDualSidewalk) {
     766              :             sidewalkType = WAY_BOTH;
     767              :         }
     768              :     }
     769        37429 :     if (addSidewalk || (myImportSidewalks && (permissions & SVC_ROAD_CLASSES) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {
     770         3253 :         if (!addForward && (sidewalkType & WAY_FORWARD) != 0) {
     771              :             addForward = true;
     772              :             forwardPermissions = SVC_PEDESTRIAN;
     773            0 :             forwardWidth = tc.getEdgeTypeSidewalkWidth(type);
     774              :             numLanesForward = 1;
     775              :             // do not add an additional sidewalk
     776            0 :             sidewalkType = (WayType)(sidewalkType & ~WAY_FORWARD);  //clang tidy thinks "!WAY_FORWARD" is always false
     777         3253 :         } else if (addSidewalk && addForward && (sidewalkType & WAY_BOTH) == 0
     778          232 :                    && numLanesForward == 1 && numLanesBackward <= 1
     779          184 :                    && (e->myExtraDisallowed & SVC_PEDESTRIAN) == 0) {
     780              :             // our typemap says pedestrians should walk here but the data says
     781              :             // there is no sidewalk at all. If the road is small, pedestrians can just walk
     782              :             // on the road
     783          154 :             forwardPermissions |= SVC_PEDESTRIAN;
     784              :         }
     785         3253 :         if (!addBackward && (sidewalkType & WAY_BACKWARD) != 0) {
     786              :             addBackward = true;
     787              :             backwardPermissions = SVC_PEDESTRIAN;
     788          101 :             backwardWidth = tc.getEdgeTypeSidewalkWidth(type);
     789              :             numLanesBackward = 1;
     790              :             // do not add an additional cycle lane
     791          101 :             sidewalkType = (WayType)(sidewalkType & ~WAY_BACKWARD); //clang tidy thinks "!WAY_BACKWARD" is always false
     792         3152 :         } else if (addSidewalk && addBackward && (sidewalkType & WAY_BOTH) == 0
     793          128 :                    && numLanesBackward == 1 && numLanesForward <= 1
     794          110 :                    && (e->myExtraDisallowed & SVC_PEDESTRIAN) == 0) {
     795              :             // our typemap says pedestrians should walk here but the data says
     796              :             // there is no sidewalk at all. If the road is small, pedestrians can just walk
     797              :             // on the road
     798          103 :             backwardPermissions |= SVC_PEDESTRIAN;
     799              :         }
     800              :     }
     801              : 
     802        37429 :     const std::string origID = OptionsCont::getOptions().getBool("output.original-names") ? toString(e->id) : "";
     803        37429 :     if (ok) {
     804        37429 :         const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
     805        37429 :         const int offsetFactor = lefthand ? -1 : 1;
     806        37429 :         LaneSpreadFunction lsf = (addBackward || OptionsCont::getOptions().getBool("osm.oneway-spread-right")) &&
     807        37429 :                                  (e->myRailDirection == WAY_UNKNOWN || explicitTwoWay)  ? LaneSpreadFunction::RIGHT : LaneSpreadFunction::CENTER;
     808        70372 :         if (addBackward && lsf == LaneSpreadFunction::RIGHT && OptionsCont::getOptions().getString("default.spreadtype") == toString(LaneSpreadFunction::ROADCENTER)) {
     809              :             lsf = LaneSpreadFunction::ROADCENTER;
     810              :         }
     811        37429 :         if (tc.getEdgeTypeSpreadType(type) != LaneSpreadFunction::RIGHT) {
     812              :             // user defined value overrides defaults
     813           31 :             lsf = tc.getEdgeTypeSpreadType(type);
     814              :         }
     815        37429 :         if (defaults.size() > 0) {
     816          248 :             e->setParameter("osmDefaults", joinToString(defaults, " "));
     817              :         }
     818              : 
     819        37429 :         id = StringUtils::escapeXML(id);
     820        37429 :         const std::string reverseID = "-" + id;
     821        37429 :         const bool markOSMDirection =  from->getType() == SumoXMLNodeType::RAIL_SIGNAL || to->getType() == SumoXMLNodeType::RAIL_SIGNAL;
     822        37429 :         if (addForward) {
     823              :             assert(numLanesForward > 0);
     824              :             NBEdge* nbe = new NBEdge(id, from, to, type, speed, NBEdge::UNSPECIFIED_FRICTION, numLanesForward, tc.getEdgeTypePriority(type),
     825              :                                      forwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape, lsf,
     826       149680 :                                      StringUtils::escapeXML(streetName), origID, true);
     827        37420 :             if (markOSMDirection) {
     828         2806 :                 nbe->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "forward");
     829              :             }
     830        37420 :             nbe->setPermissions(forwardPermissions, -1);
     831        37420 :             if ((e->myBuswayType & WAY_FORWARD) != 0) {
     832           18 :                 nbe->setPermissions(SVC_BUS, 0);
     833              :             }
     834        37420 :             applyChangeProhibition(nbe, e->myChangeForward);
     835        37420 :             applyLaneUse(nbe, e, true);
     836        37420 :             applyTurnSigns(nbe, e->myTurnSignsForward);
     837              :             nbe->setTurnSignTarget(last->getID());
     838        37420 :             if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_FORWARD) != 0)) {
     839          427 :                 nbe->addBikeLane(bikeLaneWidth * offsetFactor);
     840        36993 :             } else if (nbe->getPermissions(0) == SVC_BUS) {
     841              :                 // bikes drive on buslanes if no separate cycle lane is available
     842           69 :                 nbe->setPermissions(SVC_BUS | SVC_BICYCLE, 0);
     843              :             }
     844        37420 :             if ((addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_FORWARD) != 0))
     845        36554 :                     || (myImportSidewalks && (sidewalkType & WAY_FORWARD) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {
     846         2021 :                 nbe->addSidewalk(sidewalkWidth * offsetFactor);
     847              :             }
     848        37420 :             if (!addBackward && (e->myExtraAllowed & SVC_PEDESTRIAN) != 0 && (nbe->getPermissions(0) & SVC_PEDESTRIAN) == 0) {
     849              :                 // Pedestrians are explicitly allowed (maybe through foot="yes") but did not get a sidewalk (maybe through sidewalk="no").
     850              :                 // Since we do not have a backward edge, we need to make sure they can at least walk somewhere, see #14124
     851            3 :                 nbe->setPermissions(nbe->getPermissions(0) | SVC_PEDESTRIAN, 0);
     852              :             }
     853        37420 :             nbe->updateParameters(e->getParametersMap());
     854              :             nbe->setDistance(distanceStart);
     855        37420 :             if (e->myAmInRoundabout) {
     856              :                 // ensure roundabout edges have the precedence
     857           59 :                 nbe->setJunctionPriority(to, NBEdge::JunctionPriority::ROUNDABOUT);
     858           59 :                 nbe->setJunctionPriority(from, NBEdge::JunctionPriority::ROUNDABOUT);
     859              :             }
     860              : 
     861              :             // process forward lanes width
     862        37420 :             const int numForwardLanesFromWidthKey = (int)e->myWidthLanesForward.size();
     863        37430 :             if (numForwardLanesFromWidthKey > 0 && !OptionsCont::getOptions().getBool("ignore-widths")) {
     864           10 :                 if ((int)nbe->getLanes().size() != numForwardLanesFromWidthKey) {
     865            0 :                     WRITE_WARNINGF(TL("Forward lanes count for edge '%' ('%') is not matching the number of lanes defined in width:lanes:forward key ('%'). Using default width values."),
     866              :                                    id, nbe->getLanes().size(), numForwardLanesFromWidthKey);
     867              :                 } else {
     868           32 :                     for (int i = 0; i < numForwardLanesFromWidthKey; i++) {
     869           22 :                         const double actualWidth = e->myWidthLanesForward[i] <= 0 ? forwardWidth : e->myWidthLanesForward[i];
     870           22 :                         const int laneIndex = lefthand ? i : numForwardLanesFromWidthKey - i - 1;
     871           22 :                         nbe->setLaneWidth(laneIndex, actualWidth);
     872              :                     }
     873              :                 }
     874              :             }
     875              :             nbe->setRoutingType(routingType);
     876              : 
     877        37420 :             if (!ec.insert(nbe)) {
     878            0 :                 delete nbe;
     879            0 :                 throw ProcessError(TLF("Could not add edge '%'.", id));
     880              :             }
     881              :         }
     882        37429 :         if (addBackward) {
     883              :             assert(numLanesBackward > 0);
     884              :             NBEdge* nbe = new NBEdge(reverseID, to, from, type, speedBackward, NBEdge::UNSPECIFIED_FRICTION, numLanesBackward, tc.getEdgeTypePriority(type),
     885        23068 :                                      backwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape.reverse(), lsf,
     886        46136 :                                      StringUtils::escapeXML(streetName), origID, true);
     887        11534 :             if (markOSMDirection) {
     888          974 :                 nbe->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
     889              :             }
     890        11534 :             nbe->setPermissions(backwardPermissions);
     891        11534 :             if ((e->myBuswayType & WAY_BACKWARD) != 0) {
     892           69 :                 nbe->setPermissions(SVC_BUS, 0);
     893              :             }
     894        11534 :             applyChangeProhibition(nbe, e->myChangeBackward);
     895        11534 :             applyLaneUse(nbe, e, false);
     896        11534 :             applyTurnSigns(nbe, e->myTurnSignsBackward);
     897              :             nbe->setTurnSignTarget(first->getID());
     898        11534 :             if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_BACKWARD) != 0)) {
     899          157 :                 nbe->addBikeLane(bikeLaneWidth * offsetFactor);
     900        11377 :             } else if (nbe->getPermissions(0) == SVC_BUS) {
     901              :                 // bikes drive on buslanes if no separate cycle lane is available
     902           63 :                 nbe->setPermissions(SVC_BUS | SVC_BICYCLE, 0);
     903              :             }
     904        11534 :             if ((addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_BACKWARD) != 0))
     905        11033 :                     || (myImportSidewalks && (sidewalkType & WAY_BACKWARD) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {
     906         1095 :                 nbe->addSidewalk(sidewalkWidth * offsetFactor);
     907              :             }
     908        11534 :             nbe->updateParameters(e->getParametersMap());
     909              :             nbe->setDistance(distanceEnd);
     910        11534 :             if (e->myAmInRoundabout) {
     911              :                 // ensure roundabout edges have the precedence
     912            0 :                 nbe->setJunctionPriority(from, NBEdge::JunctionPriority::ROUNDABOUT);
     913            0 :                 nbe->setJunctionPriority(to, NBEdge::JunctionPriority::ROUNDABOUT);
     914              :             }
     915              :             // process backward lanes width
     916        11534 :             const int numBackwardLanesFromWidthKey = (int)e->myWidthLanesBackward.size();
     917        11536 :             if (numBackwardLanesFromWidthKey > 0 && !OptionsCont::getOptions().getBool("ignore-widths")) {
     918            2 :                 if ((int)nbe->getLanes().size() != numBackwardLanesFromWidthKey) {
     919            0 :                     WRITE_WARNINGF(TL("Backward lanes count for edge '%' ('%') is not matching the number of lanes defined in width:lanes:backward key ('%'). Using default width values."),
     920              :                                    id, nbe->getLanes().size(), numBackwardLanesFromWidthKey);
     921              :                 } else {
     922            8 :                     for (int i = 0; i < numBackwardLanesFromWidthKey; i++) {
     923            6 :                         const double actualWidth = e->myWidthLanesBackward[i] <= 0 ? backwardWidth : e->myWidthLanesBackward[i];
     924            6 :                         const int laneIndex = lefthand ? i : numBackwardLanesFromWidthKey - i - 1;
     925            6 :                         nbe->setLaneWidth(laneIndex, actualWidth);
     926              :                     }
     927              :                 }
     928              :             }
     929              :             nbe->setRoutingType(routingType);
     930              : 
     931        11534 :             if (!ec.insert(nbe)) {
     932            0 :                 delete nbe;
     933            0 :                 throw ProcessError(TLF("Could not add edge '-%'.", id));
     934              :             }
     935              :         }
     936        38244 :         if ((e->myParkingType & PARKING_BOTH) != 0 && OptionsCont::getOptions().isSet("parking-output")) {
     937          216 :             if ((e->myParkingType & PARKING_RIGHT) != 0) {
     938          216 :                 if (addForward) {
     939          648 :                     nb.getParkingCont().push_back(NBParking(id, id));
     940              :                 } else {
     941              :                     /// XXX parking area should be added on the left side of a reverse one-way street
     942            0 :                     if ((e->myParkingType & PARKING_LEFT) == 0 && !addBackward) {
     943              :                         /// put it on the wrong side (better than nothing)
     944            0 :                         nb.getParkingCont().push_back(NBParking(reverseID, reverseID));
     945              :                     }
     946              :                 }
     947              :             }
     948          216 :             if ((e->myParkingType & PARKING_LEFT) != 0) {
     949           78 :                 if (addBackward) {
     950          234 :                     nb.getParkingCont().push_back(NBParking(reverseID, reverseID));
     951              :                 } else {
     952              :                     /// XXX parking area should be added on the left side of an one-way street
     953            0 :                     if ((e->myParkingType & PARKING_RIGHT) == 0 && !addForward) {
     954              :                         /// put it on the wrong side (better than nothing)
     955            0 :                         nb.getParkingCont().push_back(NBParking(id, id));
     956              :                     }
     957              :                 }
     958              :             }
     959              :         }
     960              :     }
     961              :     return newIndex;
     962        37429 : }
     963              : 
     964              : 
     965              : void
     966            0 : NIImporter_OpenStreetMap::reconstructLayerElevation(const double layerElevation, NBNetBuilder& nb) {
     967              :     NBNodeCont& nc = nb.getNodeCont();
     968              :     NBEdgeCont& ec = nb.getEdgeCont();
     969              :     // reconstruct elevation from layer info
     970              :     // build a map of raising and lowering forces (attractor and distance)
     971              :     // for all nodes unknownElevation
     972              :     std::map<NBNode*, std::vector<std::pair<double, double> > > layerForces;
     973              : 
     974              :     // collect all nodes that belong to a way with layer information
     975              :     std::set<NBNode*> knownElevation;
     976            0 :     for (auto& myEdge : myEdges) {
     977            0 :         Edge* e = myEdge.second;
     978            0 :         if (e->myLayer != 0) {
     979            0 :             for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
     980            0 :                 NBNode* node = nc.retrieve(toString(*j));
     981            0 :                 if (node != nullptr) {
     982              :                     knownElevation.insert(node);
     983            0 :                     layerForces[node].emplace_back(e->myLayer * layerElevation, POSITION_EPS);
     984              :                 }
     985              :             }
     986              :         }
     987              :     }
     988              : #ifdef DEBUG_LAYER_ELEVATION
     989              :     std::cout << "known elevations:\n";
     990              :     for (std::set<NBNode*>::iterator it = knownElevation.begin(); it != knownElevation.end(); ++it) {
     991              :         const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];
     992              :         std::cout << "  node=" << (*it)->getID() << " ele=";
     993              :         for (std::vector<std::pair<double, double> >::const_iterator it_ele = primaryLayers.begin(); it_ele != primaryLayers.end(); ++it_ele) {
     994              :             std::cout << it_ele->first << " ";
     995              :         }
     996              :         std::cout << "\n";
     997              :     }
     998              : #endif
     999              :     // layer data only provides a lower bound on elevation since it is used to
    1000              :     // resolve the relation among overlapping ways.
    1001              :     // Perform a sanity check for steep inclines and raise the knownElevation if necessary
    1002              :     std::map<NBNode*, double> knownEleMax;
    1003            0 :     for (auto it : knownElevation) {
    1004              :         double eleMax = -std::numeric_limits<double>::max();
    1005            0 :         const std::vector<std::pair<double, double> >& primaryLayers = layerForces[it];
    1006            0 :         for (const auto& primaryLayer : primaryLayers) {
    1007            0 :             eleMax = MAX2(eleMax, primaryLayer.first);
    1008              :         }
    1009            0 :         knownEleMax[it] = eleMax;
    1010              :     }
    1011            0 :     const double gradeThreshold = OptionsCont::getOptions().getFloat("osm.layer-elevation.max-grade") / 100;
    1012              :     bool changed = true;
    1013            0 :     while (changed) {
    1014              :         changed = false;
    1015            0 :         for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {
    1016              :             std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it,
    1017            0 :                     knownEleMax[*it]
    1018            0 :                     / gradeThreshold * 3,
    1019            0 :                     knownElevation);
    1020            0 :             for (auto& neighbor : neighbors) {
    1021              :                 if (knownElevation.count(neighbor.first) != 0) {
    1022            0 :                     const double grade = fabs(knownEleMax[*it] - knownEleMax[neighbor.first])
    1023            0 :                                          / MAX2(POSITION_EPS, neighbor.second.first);
    1024              : #ifdef DEBUG_LAYER_ELEVATION
    1025              :                     std::cout << "   grade at node=" << (*it)->getID() << " ele=" << knownEleMax[*it] << " neigh=" << it_neigh->first->getID() << " neighEle=" << knownEleMax[it_neigh->first] << " grade=" << grade << " dist=" << it_neigh->second.first << " speed=" << it_neigh->second.second << "\n";
    1026              : #endif
    1027            0 :                     if (grade > gradeThreshold * 50 / 3.6 / neighbor.second.second) {
    1028              :                         // raise the lower node to the higher level
    1029            0 :                         const double eleMax = MAX2(knownEleMax[*it], knownEleMax[neighbor.first]);
    1030            0 :                         if (knownEleMax[*it] < eleMax) {
    1031            0 :                             knownEleMax[*it] = eleMax;
    1032              :                         } else {
    1033            0 :                             knownEleMax[neighbor.first] = eleMax;
    1034              :                         }
    1035              :                         changed = true;
    1036              :                     }
    1037              :                 }
    1038              :             }
    1039              :         }
    1040              :     }
    1041              : 
    1042              :     // collect all nodes within a grade-dependent range around knownElevation-nodes and apply knowElevation forces
    1043              :     std::set<NBNode*> unknownElevation;
    1044            0 :     for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {
    1045            0 :         const double eleMax = knownEleMax[*it];
    1046            0 :         const double maxDist = fabs(eleMax) * 100 / layerElevation;
    1047            0 :         std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);
    1048            0 :         for (auto& neighbor : neighbors) {
    1049              :             if (knownElevation.count(neighbor.first) == 0) {
    1050            0 :                 unknownElevation.insert(neighbor.first);
    1051            0 :                 layerForces[neighbor.first].emplace_back(eleMax, neighbor.second.first);
    1052              :             }
    1053              :         }
    1054              :     }
    1055              : 
    1056              :     // apply forces to ground-level nodes (neither in knownElevation nor unknownElevation)
    1057            0 :     for (auto it = unknownElevation.begin(); it != unknownElevation.end(); ++it) {
    1058              :         double eleMax = -std::numeric_limits<double>::max();
    1059            0 :         const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];
    1060            0 :         for (const auto& primaryLayer : primaryLayers) {
    1061            0 :             eleMax = MAX2(eleMax, primaryLayer.first);
    1062              :         }
    1063            0 :         const double maxDist = fabs(eleMax) * 100 / layerElevation;
    1064            0 :         std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);
    1065            0 :         for (auto& neighbor : neighbors) {
    1066              :             if (knownElevation.count(neighbor.first) == 0 && unknownElevation.count(neighbor.first) == 0) {
    1067            0 :                 layerForces[*it].emplace_back(0, neighbor.second.first);
    1068              :             }
    1069              :         }
    1070              :     }
    1071              :     // compute the elevation for each node as the weighted average of all forces
    1072              : #ifdef DEBUG_LAYER_ELEVATION
    1073              :     std::cout << "summation of forces\n";
    1074              : #endif
    1075              :     std::map<NBNode*, double> nodeElevation;
    1076            0 :     for (auto& layerForce : layerForces) {
    1077              :         const std::vector<std::pair<double, double> >& forces = layerForce.second;
    1078              :         if (knownElevation.count(layerForce.first) != 0) {
    1079              :             // use the maximum value
    1080              :             /*
    1081              :             double eleMax = -std::numeric_limits<double>::max();
    1082              :             for (std::vector<std::pair<double, double> >::const_iterator it_force = forces.begin(); it_force != forces.end(); ++it_force) {
    1083              :                 eleMax = MAX2(eleMax, it_force->first);
    1084              :             }
    1085              :             */
    1086              : #ifdef DEBUG_LAYER_ELEVATION
    1087              :             std::cout << "   node=" << it->first->getID() << " knownElevation=" << knownEleMax[it->first] << "\n";
    1088              : #endif
    1089            0 :             nodeElevation[layerForce.first] = knownEleMax[layerForce.first];
    1090            0 :         } else if (forces.size() == 1) {
    1091            0 :             nodeElevation[layerForce.first] = forces.front().first;
    1092              :         } else {
    1093              :             // use the weighted sum
    1094              :             double distSum = 0;
    1095            0 :             for (const auto& force : forces) {
    1096            0 :                 distSum += force.second;
    1097              :             }
    1098              :             double weightSum = 0;
    1099              :             double elevation = 0;
    1100              : #ifdef DEBUG_LAYER_ELEVATION
    1101              :             std::cout << "   node=" << it->first->getID() << "  distSum=" << distSum << "\n";
    1102              : #endif
    1103            0 :             for (const auto& force : forces) {
    1104            0 :                 const double weight = (distSum - force.second) / distSum;
    1105            0 :                 weightSum += weight;
    1106            0 :                 elevation += force.first * weight;
    1107              : 
    1108              : #ifdef DEBUG_LAYER_ELEVATION
    1109              :                 std::cout << "       force=" << it_force->first << " dist=" << it_force->second << "  weight=" << weight << " ele=" << elevation << "\n";
    1110              : #endif
    1111              :             }
    1112            0 :             nodeElevation[layerForce.first] = elevation / weightSum;
    1113              :         }
    1114              :     }
    1115              : #ifdef DEBUG_LAYER_ELEVATION
    1116              :     std::cout << "final elevations:\n";
    1117              :     for (std::map<NBNode*, double>::iterator it = nodeElevation.begin(); it != nodeElevation.end(); ++it) {
    1118              :         std::cout << "  node=" << (it->first)->getID() << " ele=" << it->second << "\n";
    1119              :     }
    1120              : #endif
    1121              :     // apply node elevations
    1122            0 :     for (auto& it : nodeElevation) {
    1123            0 :         NBNode* n = it.first;
    1124            0 :         n->reinit(n->getPosition() + Position(0, 0, it.second), n->getType());
    1125              :     }
    1126              : 
    1127              :     // apply way elevation to all edges that had layer information
    1128            0 :     for (const auto& it : ec) {
    1129            0 :         NBEdge* edge = it.second;
    1130              :         const PositionVector& geom = edge->getGeometry();
    1131            0 :         const double length = geom.length2D();
    1132            0 :         const double zFrom = nodeElevation[edge->getFromNode()];
    1133            0 :         const double zTo = nodeElevation[edge->getToNode()];
    1134              :         // XXX if the from- or to-node was part of multiple ways with
    1135              :         // different layers, reconstruct the layer value from origID
    1136              :         double dist = 0;
    1137            0 :         PositionVector newGeom;
    1138            0 :         for (auto it_pos = geom.begin(); it_pos != geom.end(); ++it_pos) {
    1139            0 :             if (it_pos != geom.begin()) {
    1140            0 :                 dist += (*it_pos).distanceTo2D(*(it_pos - 1));
    1141              :             }
    1142            0 :             newGeom.push_back((*it_pos) + Position(0, 0, zFrom + (zTo - zFrom) * dist / length));
    1143              :         }
    1144            0 :         edge->setGeometry(newGeom);
    1145            0 :     }
    1146            0 : }
    1147              : 
    1148              : std::map<NBNode*, std::pair<double, double> >
    1149            0 : NIImporter_OpenStreetMap::getNeighboringNodes(NBNode* node, double maxDist, const std::set<NBNode*>& knownElevation) {
    1150              :     std::map<NBNode*, std::pair<double, double> > result;
    1151              :     std::set<NBNode*> visited;
    1152              :     std::vector<NBNode*> open;
    1153            0 :     open.push_back(node);
    1154            0 :     result[node] = std::make_pair(0, 0);
    1155            0 :     while (!open.empty()) {
    1156            0 :         NBNode* n = open.back();
    1157              :         open.pop_back();
    1158            0 :         if (visited.count(n) != 0) {
    1159            0 :             continue;
    1160              :         }
    1161              :         visited.insert(n);
    1162            0 :         const EdgeVector& edges = n->getEdges();
    1163            0 :         for (auto e : edges) {
    1164            0 :             NBNode* s = nullptr;
    1165            0 :             if (n->hasIncoming(e)) {
    1166            0 :                 s = e->getFromNode();
    1167              :             } else {
    1168            0 :                 s = e->getToNode();
    1169              :             }
    1170            0 :             const double dist = result[n].first + e->getGeometry().length2D();
    1171            0 :             const double speed = MAX2(e->getSpeed(), result[n].second);
    1172              :             if (result.count(s) == 0) {
    1173            0 :                 result[s] = std::make_pair(dist, speed);
    1174              :             } else {
    1175            0 :                 result[s] = std::make_pair(MIN2(dist, result[s].first), MAX2(speed, result[s].second));
    1176              :             }
    1177            0 :             if (dist < maxDist && knownElevation.count(s) == 0) {
    1178            0 :                 open.push_back(s);
    1179              :             }
    1180              :         }
    1181              :     }
    1182              :     result.erase(node);
    1183            0 :     return result;
    1184            0 : }
    1185              : 
    1186              : 
    1187              : std::string
    1188        58865 : NIImporter_OpenStreetMap::usableType(const std::string& type, const std::string& id, NBTypeCont& tc) {
    1189        58865 :     if (tc.knows(type)) {
    1190              :         return type;
    1191              :     }
    1192              :     if (myUnusableTypes.count(type) > 0) {
    1193         1495 :         return "";
    1194              :     }
    1195              :     if (myKnownCompoundTypes.count(type) > 0) {
    1196        10603 :         return myKnownCompoundTypes[type];
    1197              :     }
    1198              :     // this edge has a type which does not yet exist in the TypeContainer
    1199         1412 :     StringTokenizer tok = StringTokenizer(type, compoundTypeSeparator);
    1200              :     std::vector<std::string> types;
    1201         2035 :     while (tok.hasNext()) {
    1202         1329 :         std::string t = tok.next();
    1203         1329 :         if (tc.knows(t)) {
    1204          581 :             if (std::find(types.begin(), types.end(), t) == types.end()) {
    1205          578 :                 types.push_back(t);
    1206              :             }
    1207          748 :         } else if (tok.size() > 1) {
    1208         1196 :             if (!StringUtils::startsWith(t, "service.")) {
    1209         1155 :                 WRITE_WARNINGF(TL("Discarding unknown compound '%' in type '%' (first occurrence for edge '%')."), t, type, id);
    1210              :             }
    1211              :         }
    1212              :     }
    1213          706 :     if (types.empty()) {
    1214          348 :         if (!StringUtils::startsWith(type, "service.")) {
    1215          501 :             WRITE_WARNINGF(TL("Discarding unusable type '%' (first occurrence for edge '%')."), type, id);
    1216              :         }
    1217              :         myUnusableTypes.insert(type);
    1218          174 :         return "";
    1219              :     }
    1220          532 :     const std::string newType = joinToString(types, "|");
    1221          532 :     if (tc.knows(newType)) {
    1222          488 :         myKnownCompoundTypes[type] = newType;
    1223              :         return newType;
    1224              :     } else if (myKnownCompoundTypes.count(newType) > 0) {
    1225            0 :         return myKnownCompoundTypes[newType];
    1226              :     } else {
    1227              :         // build a new type by merging all values
    1228              :         int numLanes = 0;
    1229              :         double maxSpeed = 0;
    1230              :         int prio = 0;
    1231           44 :         double width = NBEdge::UNSPECIFIED_WIDTH;
    1232              :         double sidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
    1233              :         double bikelaneWidth = NBEdge::UNSPECIFIED_WIDTH;
    1234              :         bool defaultIsOneWay = true;
    1235              :         SVCPermissions permissions = 0;
    1236              :         LaneSpreadFunction spreadType = LaneSpreadFunction::RIGHT;
    1237              :         bool discard = true;
    1238              :         bool hadDiscard = false;
    1239          134 :         for (auto& type2 : types) {
    1240           90 :             if (!tc.getEdgeTypeShallBeDiscarded(type2)) {
    1241           85 :                 numLanes = MAX2(numLanes, tc.getEdgeTypeNumLanes(type2));
    1242           85 :                 maxSpeed = MAX2(maxSpeed, tc.getEdgeTypeSpeed(type2));
    1243           85 :                 prio = MAX2(prio, tc.getEdgeTypePriority(type2));
    1244           85 :                 defaultIsOneWay &= tc.getEdgeTypeIsOneWay(type2);
    1245              :                 //std::cout << "merging component " << type2 << " into type " << newType << " allows=" << getVehicleClassNames(tc.getPermissions(type2)) << " oneway=" << defaultIsOneWay << "\n";
    1246           85 :                 permissions |= tc.getEdgeTypePermissions(type2);
    1247           85 :                 spreadType = tc.getEdgeTypeSpreadType(type2);
    1248           85 :                 width = MAX2(width, tc.getEdgeTypeWidth(type2));
    1249           85 :                 sidewalkWidth = MAX2(sidewalkWidth, tc.getEdgeTypeSidewalkWidth(type2));
    1250           85 :                 bikelaneWidth = MAX2(bikelaneWidth, tc.getEdgeTypeBikeLaneWidth(type2));
    1251              :                 discard = false;
    1252              :             } else {
    1253              :                 hadDiscard = true;
    1254              :             }
    1255              :         }
    1256           44 :         if (hadDiscard && permissions == 0) {
    1257              :             discard = true;
    1258              :         }
    1259           41 :         if (discard) {
    1260            9 :             WRITE_WARNINGF(TL("Discarding compound type '%' (first occurrence for edge '%')."), newType, id);
    1261              :             myUnusableTypes.insert(newType);
    1262            3 :             return "";
    1263              :         }
    1264           41 :         if (width != NBEdge::UNSPECIFIED_WIDTH) {
    1265              :             width = MAX2(width, SUMO_const_laneWidth);
    1266              :         }
    1267              :         // ensure pedestrians don't run into trains
    1268           41 :         if (sidewalkWidth == NBEdge::UNSPECIFIED_WIDTH
    1269           29 :                 && (permissions & SVC_PEDESTRIAN) != 0
    1270           23 :                 && (permissions & SVC_RAIL_CLASSES) != 0) {
    1271              :             //std::cout << "patching sidewalk for type '" << newType << "' which allows=" << getVehicleClassNames(permissions) << "\n";
    1272           40 :             sidewalkWidth = OptionsCont::getOptions().getFloat("default.sidewalk-width");
    1273              :         }
    1274              : 
    1275          123 :         WRITE_MESSAGEF(TL("Adding new type '%' (first occurrence for edge '%')."), type, id);
    1276           41 :         tc.insertEdgeType(newType, numLanes, maxSpeed, prio, permissions, spreadType, width,
    1277              :                           defaultIsOneWay, sidewalkWidth, bikelaneWidth, 0, 0, 0);
    1278          125 :         for (auto& type3 : types) {
    1279           84 :             if (!tc.getEdgeTypeShallBeDiscarded(type3)) {
    1280           84 :                 tc.copyEdgeTypeRestrictionsAndAttrs(type3, newType);
    1281              :             }
    1282              :         }
    1283           41 :         myKnownCompoundTypes[type] = newType;
    1284              :         return newType;
    1285              :     }
    1286          706 : }
    1287              : 
    1288              : void
    1289        20354 : NIImporter_OpenStreetMap::extendRailwayDistances(Edge* e, NBTypeCont& tc) {
    1290        20354 :     const std::string id = toString(e->id);
    1291        20354 :     std::string type = usableType(e->myHighWayType, id, tc);
    1292        20354 :     if (type != "" && isRailway(tc.getEdgeTypePermissions(type))) {
    1293              :         std::vector<NIOSMNode*> nodes;
    1294              :         std::vector<double> usablePositions;
    1295              :         std::vector<int> usableIndex;
    1296        29766 :         for (long long int n : e->myCurrentNodes) {
    1297        26880 :             NIOSMNode* node = myOSMNodes[n];
    1298        26880 :             node->positionMeters = interpretDistance(node);
    1299        26880 :             if (node->positionMeters != std::numeric_limits<double>::max()) {
    1300          343 :                 usablePositions.push_back(node->positionMeters);
    1301          343 :                 usableIndex.push_back((int)nodes.size());
    1302              :             }
    1303        26880 :             nodes.push_back(node);
    1304              :         }
    1305         2886 :         if (usablePositions.size() == 0) {
    1306              :             return;
    1307              :         } else {
    1308              :             bool forward = true;
    1309          233 :             if (usablePositions.size() == 1) {
    1310          525 :                 WRITE_WARNINGF(TL("Ambiguous railway kilometrage direction for way '%' (assuming forward)"), id);
    1311              :             } else {
    1312           58 :                 forward = usablePositions.front() < usablePositions.back();
    1313              :             }
    1314              :             // check for consistency
    1315          343 :             for (int i = 1; i < (int)usablePositions.size(); i++) {
    1316          110 :                 if ((usablePositions[i - 1] < usablePositions[i]) != forward) {
    1317            0 :                     WRITE_WARNINGF(TL("Inconsistent railway kilometrage direction for way '%': % (skipping)"), id, toString(usablePositions));
    1318            0 :                     return;
    1319              :                 }
    1320              :             }
    1321          233 :             if (nodes.size() > usablePositions.size()) {
    1322              :                 // complete missing values
    1323          233 :                 PositionVector shape;
    1324         3240 :                 for (NIOSMNode* node : nodes) {
    1325         3007 :                     shape.push_back(Position(node->lon, node->lat, 0));
    1326              :                 }
    1327          233 :                 if (!NBNetBuilder::transformCoordinates(shape)) {
    1328              :                     return; // error will be given later
    1329              :                 }
    1330          233 :                 double sign = forward ? 1 : -1;
    1331              :                 // extend backward before first usable value
    1332         1485 :                 for (int i = usableIndex.front() - 1; i >= 0; i--) {
    1333         1252 :                     nodes[i]->positionMeters = nodes[i + 1]->positionMeters - sign * shape[i].distanceTo2D(shape[i + 1]);
    1334              :                 }
    1335              :                 // extend forward
    1336         1755 :                 for (int i = usableIndex.front() + 1; i < (int)nodes.size(); i++) {
    1337         1522 :                     if (nodes[i]->positionMeters == std::numeric_limits<double>::max()) {
    1338         1412 :                         nodes[i]->positionMeters = nodes[i - 1]->positionMeters + sign * shape[i].distanceTo2D(shape[i - 1]);
    1339              :                     }
    1340              :                 }
    1341              :                 //std::cout << " way=" << id << " usable=" << toString(usablePositions) << "\n indices=" << toString(usableIndex)
    1342              :                 //    << " final:\n";
    1343              :                 //for (auto n : nodes) {
    1344              :                 //    std::cout << "    " << n->id << " " << n->positionMeters << " " << n->position<< "\n";
    1345              :                 //}
    1346          233 :             }
    1347              :         }
    1348         2886 :     }
    1349              : }
    1350              : 
    1351              : 
    1352              : double
    1353        26880 : NIImporter_OpenStreetMap::interpretDistance(NIOSMNode* node) {
    1354        26880 :     if (node->position.size() > 0) {
    1355              :         try {
    1356          690 :             if (StringUtils::startsWith(node->position, "mi:")) {
    1357            0 :                 return StringUtils::toDouble(node->position.substr(3)) * 1609.344; // meters per mile
    1358              :             } else {
    1359          345 :                 return StringUtils::toDouble(node->position) * 1000;
    1360              :             }
    1361            2 :         } catch (...) {
    1362            6 :             WRITE_WARNINGF(TL("Value of railway:position is not numeric ('%') in node '%'."), node->position, toString(node->id));
    1363            2 :         }
    1364              :     }
    1365              :     return std::numeric_limits<double>::max();
    1366              : }
    1367              : 
    1368              : SUMOVehicleClass
    1369         8053 : NIImporter_OpenStreetMap::interpretTransportType(const std::string& type, NIOSMNode* toSet) {
    1370              :     SUMOVehicleClass result = SVC_IGNORING;
    1371         8053 :     if (type == "train") {
    1372              :         result = SVC_RAIL;
    1373         7572 :     } else if (type == "subway") {
    1374              :         result = SVC_SUBWAY;
    1375         7352 :     } else if (type == "aerialway") {
    1376              :         result = SVC_CABLE_CAR;
    1377         7350 :     } else if (type == "light_rail" || type == "monorail") {
    1378              :         result = SVC_RAIL_URBAN;
    1379         6983 :     } else if (type == "share_taxi") {
    1380              :         result = SVC_TAXI;
    1381         6981 :     } else if (type == "minibus") {
    1382              :         result = SVC_BUS;
    1383         6979 :     } else if (type == "trolleybus") {
    1384              :         result = SVC_BUS;
    1385              :     } else if (SumoVehicleClassStrings.hasString(type)) {
    1386         2583 :         result = SumoVehicleClassStrings.get(type);
    1387              :     }
    1388         8053 :     std::string stop = "";
    1389         8053 :     if (result == SVC_TRAM) {
    1390              :         stop = ".tram";
    1391         7349 :     } else if (result == SVC_BUS) {
    1392              :         stop = ".bus";
    1393         5458 :     } else if (isRailway(result)) {
    1394              :         stop = ".train";
    1395              :     }
    1396         8053 :     if (toSet != nullptr && result != SVC_IGNORING) {
    1397         2311 :         toSet->permissions |= result;
    1398         2311 :         toSet->ptStopLength = OptionsCont::getOptions().getFloat("osm.stop-output.length" + stop);
    1399              :     }
    1400         8053 :     return result;
    1401              : }
    1402              : 
    1403              : 
    1404              : void
    1405        48954 : NIImporter_OpenStreetMap::applyChangeProhibition(NBEdge* e, int changeProhibition) {
    1406              :     bool multiLane = changeProhibition > 3;
    1407              :     //std::cout << "applyChangeProhibition e=" << e->getID() << " changeProhibition=" << std::bitset<32>(changeProhibition) << " val=" << changeProhibition << "\n";
    1408        49235 :     for (int lane = 0; changeProhibition > 0 && lane < e->getNumLanes(); lane++) {
    1409          281 :         int code = changeProhibition % 4; // only look at the last 2 bits
    1410          281 :         SVCPermissions changeLeft = (code & CHANGE_NO_LEFT) == 0 ? SVCAll : (SVCPermissions)SVC_AUTHORITY;
    1411          281 :         SVCPermissions changeRight = (code & CHANGE_NO_RIGHT) == 0 ? SVCAll : (SVCPermissions)SVC_AUTHORITY;
    1412          281 :         e->setPermittedChanging(lane, changeLeft, changeRight);
    1413          281 :         if (multiLane) {
    1414          208 :             changeProhibition = changeProhibition >> 2;
    1415              :         }
    1416              :     }
    1417        48954 : }
    1418              : 
    1419              : 
    1420              : void
    1421        48954 : NIImporter_OpenStreetMap::applyLaneUse(NBEdge* e, NIImporter_OpenStreetMap::Edge* nie, const bool forward) {
    1422        48954 :     if (myImportLaneAccess) {
    1423              :         const int numLanes = e->getNumLanes();
    1424         2455 :         const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
    1425         2455 :         const std::vector<bool>& designated = forward ? nie->myDesignatedLaneForward : nie->myDesignatedLaneBackward;
    1426         2455 :         const std::vector<SVCPermissions>& allowed = forward ? nie->myAllowedLaneForward : nie->myAllowedLaneBackward;
    1427         2455 :         const std::vector<SVCPermissions>& disallowed = forward ? nie->myDisallowedLaneForward : nie->myDisallowedLaneBackward;
    1428         5057 :         for (int lane = 0; lane < numLanes; lane++) {
    1429              :             // laneUse stores from left to right
    1430         2602 :             const int i = lefthand ? lane : numLanes - 1 - lane;
    1431              :             // Extra allowed SVCs for this lane or none if no info was present for the lane
    1432         2602 :             const SVCPermissions extraAllowed = i < (int)allowed.size() ? allowed[i] : (SVCPermissions)SVC_IGNORING;
    1433              :             // Extra disallowed SVCs for this lane or none if no info was present for the lane
    1434         2602 :             const SVCPermissions extraDisallowed = i < (int)disallowed.size() ? disallowed[i] : (SVCPermissions)SVC_IGNORING;
    1435         2774 :             if (i < (int)designated.size() && designated[i]) {
    1436              :                 // if designated, delete all permissions
    1437           68 :                 e->setPermissions(SVC_IGNORING, lane);
    1438           68 :                 e->preferVehicleClass(lane, extraAllowed);
    1439              :             }
    1440         2602 :             e->setPermissions((e->getPermissions(lane) | extraAllowed) & (~extraDisallowed), lane);
    1441              :         }
    1442              :     }
    1443        48954 : }
    1444              : 
    1445              : void
    1446          941 : NIImporter_OpenStreetMap::mergeTurnSigns(std::vector<int>& signs, std::vector<int> signs2) {
    1447          941 :     if (signs.empty()) {
    1448          937 :         signs.insert(signs.begin(), signs2.begin(), signs2.end());
    1449              :     } else {
    1450           18 :         for (int i = 0; i < (int)MIN2(signs.size(), signs2.size()); i++) {
    1451           14 :             signs[i] |= signs2[i];
    1452              :         }
    1453              :     }
    1454          941 : }
    1455              : 
    1456              : 
    1457              : void
    1458        48954 : NIImporter_OpenStreetMap::applyTurnSigns(NBEdge* e, const std::vector<int>& turnSigns) {
    1459        48954 :     if (myImportTurnSigns && turnSigns.size() > 0) {
    1460              :         // no sidewalks and bike lanes have been added yet
    1461           89 :         if ((int)turnSigns.size() == e->getNumLanes()) {
    1462              :             //std::cout << "apply turnSigns for " << e->getID() << " turnSigns=" << toString(turnSigns) << "\n";
    1463          279 :             for (int i = 0; i < (int)turnSigns.size(); i++) {
    1464              :                 // laneUse stores from left to right
    1465          196 :                 const int laneIndex = e->getNumLanes() - 1 - i;
    1466              :                 NBEdge::Lane& lane = e->getLaneStruct(laneIndex);
    1467          196 :                 lane.turnSigns = turnSigns[i];
    1468              :             }
    1469              :         } else {
    1470           18 :             WRITE_WARNINGF(TL("Ignoring turn sign information for % lanes on edge % with % driving lanes"), turnSigns.size(), e->getID(), e->getNumLanes());
    1471              :         }
    1472              :     }
    1473        48954 : }
    1474              : 
    1475              : 
    1476              : // ---------------------------------------------------------------------------
    1477              : // definitions of NIImporter_OpenStreetMap::NodesHandler-methods
    1478              : // ---------------------------------------------------------------------------
    1479          190 : NIImporter_OpenStreetMap::NodesHandler::NodesHandler(std::map<long long int, NIOSMNode*>& toFill,
    1480          190 :         std::set<NIOSMNode*, CompareNodes>& uniqueNodes, const OptionsCont& oc) :
    1481              :     SUMOSAXHandler("osm - file"),
    1482          190 :     myToFill(toFill),
    1483          190 :     myCurrentNode(nullptr),
    1484          190 :     myIsStation(false),
    1485          190 :     myHierarchyLevel(0),
    1486          190 :     myUniqueNodes(uniqueNodes),
    1487          190 :     myImportElevation(oc.getBool("osm.elevation")),
    1488          190 :     myDuplicateNodes(0),
    1489          380 :     myOptionsCont(oc) {
    1490              :     // init rail signal rules
    1491          572 :     for (std::string kv : oc.getStringVector("osm.railsignals")) {
    1492          192 :         if (kv == "DEFAULT") {
    1493          188 :             myRailSignalRules.push_back("railway:signal:main=");
    1494          376 :             myRailSignalRules.push_back("railway:signal:combined=");
    1495            4 :         } else if (kv == "ALL") {
    1496            2 :             myRailSignalRules.push_back("railway=signal");
    1497              :         } else {
    1498            6 :             myRailSignalRules.push_back("railway:signal:" + kv);
    1499              :         }
    1500              :     }
    1501          190 : }
    1502              : 
    1503              : 
    1504          190 : NIImporter_OpenStreetMap::NodesHandler::~NodesHandler() = default;
    1505              : 
    1506              : void
    1507       562823 : NIImporter_OpenStreetMap::NodesHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
    1508       562823 :     ++myHierarchyLevel;
    1509       562823 :     if (element == SUMO_TAG_NODE) {
    1510       339346 :         bool ok = true;
    1511       339346 :         myLastNodeID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1512       339346 :         if (myHierarchyLevel != 2) {
    1513            0 :             WRITE_ERROR("Node element on wrong XML hierarchy level (id='" + myLastNodeID +
    1514              :                         "', level='" + toString(myHierarchyLevel) + "').");
    1515        14667 :             return;
    1516              :         }
    1517       339346 :         const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, myLastNodeID.c_str(), ok);
    1518       339346 :         if (action == "delete" || !ok) {
    1519              :             return;
    1520              :         }
    1521              :         try {
    1522              :             // we do not use attrs.get here to save some time on parsing
    1523       324682 :             const long long int id = StringUtils::toLong(myLastNodeID);
    1524       324681 :             myCurrentNode = nullptr;
    1525       324681 :             const auto insertionIt = myToFill.lower_bound(id);
    1526       324681 :             if (insertionIt == myToFill.end() || insertionIt->first != id) {
    1527              :                 // assume we are loading multiple files, so we won't report duplicate nodes
    1528       323158 :                 const double tlon = attrs.get<double>(SUMO_ATTR_LON, myLastNodeID.c_str(), ok);
    1529       323158 :                 const double tlat = attrs.get<double>(SUMO_ATTR_LAT, myLastNodeID.c_str(), ok);
    1530       323158 :                 if (!ok) {
    1531              :                     return;
    1532              :                 }
    1533       323156 :                 myCurrentNode = new NIOSMNode(id, tlon, tlat);
    1534       323156 :                 auto similarNode = myUniqueNodes.find(myCurrentNode);
    1535       323156 :                 if (similarNode == myUniqueNodes.end()) {
    1536              :                     myUniqueNodes.insert(myCurrentNode);
    1537              :                 } else {
    1538           36 :                     delete myCurrentNode;
    1539           36 :                     myCurrentNode = *similarNode;
    1540           36 :                     myDuplicateNodes++;
    1541              :                 }
    1542       323156 :                 myToFill.emplace_hint(insertionIt, id, myCurrentNode);
    1543              :             }
    1544            1 :         } catch (FormatException&) {
    1545            1 :             WRITE_ERROR(TL("Attribute 'id' in the definition of a node is not of type long long int."));
    1546              :             return;
    1547            1 :         }
    1548              :     }
    1549       548156 :     if (element == SUMO_TAG_TAG && myCurrentNode != nullptr) {
    1550       215362 :         if (myHierarchyLevel != 3) {
    1551            1 :             WRITE_ERROR(TL("Tag element on wrong XML hierarchy level."));
    1552            1 :             return;
    1553              :         }
    1554       215361 :         bool ok = true;
    1555       215361 :         const std::string& key = attrs.get<std::string>(SUMO_ATTR_K, myLastNodeID.c_str(), ok, false);
    1556              :         // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
    1557       210772 :         if (key == "highway" || key == "ele" || key == "crossing" || key == "railway" || key == "public_transport"
    1558       199043 :                 || key == "name" || key == "train" || key == "bus" || key == "tram" || key == "light_rail" || key == "subway" || key == "station" || key == "noexit"
    1559       186822 :                 || key == "crossing:barrier"
    1560       186455 :                 || key == "crossing:light"
    1561       186316 :                 || key == "railway:ref"
    1562       401608 :                 || StringUtils::startsWith(key, "railway:signal")
    1563       600906 :                 || StringUtils::startsWith(key, "railway:position")
    1564              :            ) {
    1565        40957 :             const std::string& value = attrs.get<std::string>(SUMO_ATTR_V, myLastNodeID.c_str(), ok, false);
    1566        45546 :             if (key == "highway" && value.find("traffic_signal") != std::string::npos) {
    1567         1382 :                 myCurrentNode->tlsControlled = true;
    1568        42162 :             } else if (key == "crossing" && value.find("traffic_signals") != std::string::npos) {
    1569         1676 :                 myCurrentNode->tlsControlled = true;
    1570        41106 :             } else if (key == "highway" && value.find("crossing") != std::string::npos) {
    1571         1928 :                 myCurrentNode->pedestrianCrossing = true;
    1572           57 :             } else if ((key == "noexit" && value == "yes")
    1573        35971 :                        || (key == "railway" && value == "buffer_stop")) {
    1574          120 :                 myCurrentNode->railwayBufferStop = true;
    1575        41124 :             } else if (key == "railway" && value.find("crossing") != std::string::npos) {
    1576         1604 :                 myCurrentNode->railwayCrossing = true;
    1577        34247 :             } else if (key == "crossing:barrier") {
    1578          734 :                 myCurrentNode->setParameter("crossing:barrier", value);
    1579        33880 :             } else if (key == "crossing:light") {
    1580          278 :                 myCurrentNode->setParameter("crossing:light", value);
    1581        33741 :             } else if (key == "railway:signal:direction") {
    1582         1356 :                 if (value == "both") {
    1583           35 :                     myCurrentNode->myRailDirection = WAY_BOTH;
    1584         1321 :                 } else if (value == "backward") {
    1585          402 :                     myCurrentNode->myRailDirection = WAY_BACKWARD;
    1586          919 :                 } else if (value == "forward") {
    1587          919 :                     myCurrentNode->myRailDirection = WAY_FORWARD;
    1588              :                 }
    1589        64770 :             } else if (StringUtils::startsWith(key, "railway:signal") || (key == "railway" && value == "signal")) {
    1590        11521 :                 std::string kv = key + "=" + value;
    1591        11521 :                 std::string kglob = key + "=";
    1592        11521 :                 if ((std::find(myRailSignalRules.begin(), myRailSignalRules.end(), kv) != myRailSignalRules.end())
    1593        11521 :                         || (std::find(myRailSignalRules.begin(), myRailSignalRules.end(), kglob) != myRailSignalRules.end())) {
    1594          883 :                     myCurrentNode->railwaySignal = true;
    1595              :                 }
    1596        41728 :             } else if (StringUtils::startsWith(key, "railway:position") && value.size() > myCurrentNode->position.size()) {
    1597              :                 // use the entry with the highest precision (more digits)
    1598          367 :                 myCurrentNode->position = value;
    1599        20497 :             } else if ((key == "public_transport" && value == "stop_position") ||
    1600        18919 :                        (key == "highway" && value == "bus_stop")) {
    1601         2047 :                 myCurrentNode->ptStopPosition = true;
    1602         2047 :                 if (myCurrentNode->ptStopLength == 0) {
    1603              :                     // default length
    1604          891 :                     myCurrentNode->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length");
    1605              :                 }
    1606        18450 :             } else if (key == "name") {
    1607        10156 :                 myCurrentNode->name = value;
    1608         8294 :             } else if (myImportElevation && key == "ele") {
    1609              :                 try {
    1610         1546 :                     const double elevation = StringUtils::parseDist(value);
    1611         1545 :                     if (std::isnan(elevation)) {
    1612            0 :                         WRITE_WARNINGF(TL("Value of key '%' is invalid ('%') in node '%'."), key, value, myLastNodeID);
    1613              :                     } else {
    1614         1545 :                         myCurrentNode->ele = elevation;
    1615              :                     }
    1616            1 :                 } catch (...) {
    1617            3 :                     WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in node '%'."), key, value, myLastNodeID);
    1618            1 :                 }
    1619         6748 :             } else if (key == "station") {
    1620           84 :                 interpretTransportType(value, myCurrentNode);
    1621           84 :                 myIsStation = true;
    1622         6664 :             } else if (key == "railway:ref") {
    1623           69 :                 myRailwayRef = value;
    1624              :             } else {
    1625              :                 // v="yes"
    1626         6595 :                 interpretTransportType(key, myCurrentNode);
    1627              :             }
    1628              :         }
    1629       218163 :         if (myAllAttributes && (myExtraAttributes.count(key) != 0 || myExtraAttributes.size() == 0)) {
    1630          428 :             const std::string info = "node=" + toString(myCurrentNode->id) + ", k=" + key;
    1631          856 :             myCurrentNode->setParameter(key, attrs.get<std::string>(SUMO_ATTR_V, info.c_str(), ok, false));
    1632              :         }
    1633              :     }
    1634              : }
    1635              : 
    1636              : 
    1637              : void
    1638       562631 : NIImporter_OpenStreetMap::NodesHandler::myEndElement(int element) {
    1639       562631 :     if (element == SUMO_TAG_NODE && myHierarchyLevel == 2) {
    1640       339346 :         if (myIsStation && myRailwayRef != "") {
    1641           44 :             myCurrentNode->setParameter("railway:ref", myRailwayRef);
    1642              :         }
    1643       339346 :         myCurrentNode = nullptr;
    1644       339346 :         myIsStation = false;
    1645       339346 :         myRailwayRef = "";
    1646              :     }
    1647       562631 :     --myHierarchyLevel;
    1648       562631 : }
    1649              : 
    1650              : 
    1651              : // ---------------------------------------------------------------------------
    1652              : // definitions of NIImporter_OpenStreetMap::EdgesHandler-methods
    1653              : // ---------------------------------------------------------------------------
    1654          188 : NIImporter_OpenStreetMap::EdgesHandler::EdgesHandler(
    1655              :     const std::map<long long int, NIOSMNode*>& osmNodes,
    1656              :     std::map<long long int, Edge*>& toFill, std::map<long long int, Edge*>& platformShapes,
    1657          188 :     const NBTypeCont& tc):
    1658              :     SUMOSAXHandler("osm - file"),
    1659          188 :     myOSMNodes(osmNodes),
    1660          188 :     myEdgeMap(toFill),
    1661          188 :     myPlatformShapesMap(platformShapes),
    1662          376 :     myTypeCont(tc) {
    1663              : 
    1664          188 :     const double unlimitedSpeed = OptionsCont::getOptions().getFloat("osm.speedlimit-none");
    1665              : 
    1666          188 :     mySpeedMap["nan"] = MAXSPEED_UNGIVEN;
    1667          188 :     mySpeedMap["sign"] = MAXSPEED_UNGIVEN;
    1668          188 :     mySpeedMap["signals"] = MAXSPEED_UNGIVEN;
    1669          188 :     mySpeedMap["none"] = unlimitedSpeed;
    1670          188 :     mySpeedMap["no"] = unlimitedSpeed;
    1671          188 :     mySpeedMap["walk"] = 5. / 3.6;
    1672              :     // https://wiki.openstreetmap.org/wiki/Key:source:maxspeed#Commonly_used_values
    1673          188 :     mySpeedMap["AT:urban"] = 50. / 3.6;
    1674          188 :     mySpeedMap["AT:rural"] = 100. / 3.6;
    1675          188 :     mySpeedMap["AT:trunk"] = 100. / 3.6;
    1676          188 :     mySpeedMap["AT:motorway"] = 130. / 3.6;
    1677          188 :     mySpeedMap["AU:urban"] = 50. / 3.6;
    1678          188 :     mySpeedMap["BE:urban"] = 50. / 3.6;
    1679          188 :     mySpeedMap["BE:zone"] = 30. / 3.6;
    1680          188 :     mySpeedMap["BE:motorway"] = 120. / 3.6;
    1681          188 :     mySpeedMap["BE:zone30"] = 30. / 3.6;
    1682          188 :     mySpeedMap["BE-VLG:rural"] = 70. / 3.6;
    1683          188 :     mySpeedMap["BE-WAL:rural"] = 90. / 3.6;
    1684          188 :     mySpeedMap["BE:school"] = 30. / 3.6;
    1685          188 :     mySpeedMap["CZ:motorway"] = 130. / 3.6;
    1686          188 :     mySpeedMap["CZ:trunk"] = 110. / 3.6;
    1687          188 :     mySpeedMap["CZ:rural"] = 90. / 3.6;
    1688          188 :     mySpeedMap["CZ:urban_motorway"] = 80. / 3.6;
    1689          188 :     mySpeedMap["CZ:urban_trunk"] = 80. / 3.6;
    1690          188 :     mySpeedMap["CZ:urban"] = 50. / 3.6;
    1691          188 :     mySpeedMap["DE:motorway"] = unlimitedSpeed;
    1692          188 :     mySpeedMap["DE:rural"] = 100. / 3.6;
    1693          188 :     mySpeedMap["DE:urban"] = 50. / 3.6;
    1694          188 :     mySpeedMap["DE:bicycle_road"] = 30. / 3.6;
    1695          188 :     mySpeedMap["DK:motorway"] = 130. / 3.6;
    1696          188 :     mySpeedMap["DK:rural"] = 80. / 3.6;
    1697          188 :     mySpeedMap["DK:urban"] = 50. / 3.6;
    1698          188 :     mySpeedMap["EE:urban"] = 50. / 3.6;
    1699          188 :     mySpeedMap["EE:rural"] = 90. / 3.6;
    1700          188 :     mySpeedMap["ES:urban"] = 50. / 3.6;
    1701          188 :     mySpeedMap["ES:zone30"] = 30. / 3.6;
    1702          188 :     mySpeedMap["FR:motorway"] = 130. / 3.6; // 110 (raining)
    1703          188 :     mySpeedMap["FR:rural"] = 80. / 3.6;
    1704          188 :     mySpeedMap["FR:urban"] = 50. / 3.6;
    1705          188 :     mySpeedMap["FR:zone30"] = 30. / 3.6;
    1706          188 :     mySpeedMap["HU:living_street"] = 20. / 3.6;
    1707          188 :     mySpeedMap["HU:motorway"] = 130. / 3.6;
    1708          188 :     mySpeedMap["HU:rural"] = 90. / 3.6;
    1709          188 :     mySpeedMap["HU:trunk"] = 110. / 3.6;
    1710          188 :     mySpeedMap["HU:urban"] = 50. / 3.6;
    1711          188 :     mySpeedMap["IT:rural"] = 90. / 3.6;
    1712          188 :     mySpeedMap["IT:motorway"] = 130. / 3.6;
    1713          188 :     mySpeedMap["IT:urban"] = 50. / 3.6;
    1714          188 :     mySpeedMap["JP:nsl"] = 60. / 3.6;
    1715          188 :     mySpeedMap["JP:express"] = 100. / 3.6;
    1716          188 :     mySpeedMap["LT:rural"] = 90. / 3.6;
    1717          188 :     mySpeedMap["LT:urban"] = 50. / 3.6;
    1718          188 :     mySpeedMap["NO:rural"] = 80. / 3.6;
    1719          188 :     mySpeedMap["NO:urban"] = 50. / 3.6;
    1720          188 :     mySpeedMap["ON:urban"] = 50. / 3.6;
    1721          188 :     mySpeedMap["ON:rural"] = 80. / 3.6;
    1722          188 :     mySpeedMap["PT:motorway"] = 120. / 3.6;
    1723          188 :     mySpeedMap["PT:rural"] = 90. / 3.6;
    1724          188 :     mySpeedMap["PT:trunk"] = 100. / 3.6;
    1725          188 :     mySpeedMap["PT:urban"] = 50. / 3.6;
    1726          188 :     mySpeedMap["RO:motorway"] = 130. / 3.6;
    1727          188 :     mySpeedMap["RO:rural"] = 90. / 3.6;
    1728          188 :     mySpeedMap["RO:trunk"] = 100. / 3.6;
    1729          188 :     mySpeedMap["RO:urban"] = 50. / 3.6;
    1730          188 :     mySpeedMap["RS:living_street"] = 30. / 3.6;
    1731          188 :     mySpeedMap["RS:motorway"] = 130. / 3.6;
    1732          188 :     mySpeedMap["RS:rural"] = 80. / 3.6;
    1733          188 :     mySpeedMap["RS:trunk"] = 100. / 3.6;
    1734          188 :     mySpeedMap["RS:urban"] = 50. / 3.6;
    1735          188 :     mySpeedMap["RU:living_street"] = 20. / 3.6;
    1736          188 :     mySpeedMap["RU:urban"] = 60. / 3.6;
    1737          188 :     mySpeedMap["RU:rural"] = 90. / 3.6;
    1738          188 :     mySpeedMap["RU:motorway"] = 110. / 3.6;
    1739          188 :     const double seventy = StringUtils::parseSpeed("70mph");
    1740          188 :     const double sixty = StringUtils::parseSpeed("60mph");
    1741          188 :     mySpeedMap["GB:motorway"] = seventy;
    1742          188 :     mySpeedMap["GB:nsl_dual"] = seventy;
    1743          188 :     mySpeedMap["GB:nsl_single"] = sixty;
    1744          188 :     mySpeedMap["UK:motorway"] = seventy;
    1745          188 :     mySpeedMap["UK:nsl_dual"] = seventy;
    1746          188 :     mySpeedMap["UK:nsl_single"] = sixty;
    1747          188 :     mySpeedMap["UZ:living_street"] = 30. / 3.6;
    1748          188 :     mySpeedMap["UZ:urban"] = 70. / 3.6;
    1749          188 :     mySpeedMap["UZ:rural"] = 100. / 3.6;
    1750          188 :     mySpeedMap["UZ:motorway"] = 110. / 3.6;
    1751          188 : }
    1752              : 
    1753          188 : NIImporter_OpenStreetMap::EdgesHandler::~EdgesHandler() = default;
    1754              : 
    1755              : void
    1756       682335 : NIImporter_OpenStreetMap::EdgesHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
    1757       682335 :     if (element == SUMO_TAG_WAY) {
    1758        46386 :         bool ok = true;
    1759        46386 :         const long long int id = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);
    1760        46386 :         const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, nullptr, ok);
    1761        46386 :         if (action == "delete" || !ok) {
    1762         2878 :             myCurrentEdge = nullptr;
    1763              :             return;
    1764              :         }
    1765        43508 :         myCurrentEdge = new Edge(id);
    1766              :     }
    1767              :     // parse "nd" (node) elements
    1768       679457 :     if (element == SUMO_TAG_ND && myCurrentEdge != nullptr) {
    1769       394258 :         bool ok = true;
    1770       394258 :         long long int ref = attrs.get<long long int>(SUMO_ATTR_REF, nullptr, ok);
    1771       394258 :         if (ok) {
    1772       394258 :             auto node = myOSMNodes.find(ref);
    1773       394258 :             if (node == myOSMNodes.end()) {
    1774        19512 :                 WRITE_WARNINGF(TL("The referenced geometry information (ref='%') is not known"), toString(ref));
    1775              :                 return;
    1776              :             }
    1777              : 
    1778       384502 :             ref = node->second->id; // node may have been substituted
    1779       384502 :             if (myCurrentEdge->myCurrentNodes.empty() ||
    1780       342099 :                     myCurrentEdge->myCurrentNodes.back() != ref) { // avoid consecutive duplicates
    1781       384487 :                 myCurrentEdge->myCurrentNodes.push_back(ref);
    1782              :             }
    1783              : 
    1784              :         }
    1785              :     }
    1786       669701 :     if (element == SUMO_TAG_TAG && myCurrentEdge != nullptr) {
    1787       217866 :         bool ok = true;
    1788       435732 :         std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentEdge->id).c_str(), ok, false);
    1789       372688 :         if (key.size() > 6 && StringUtils::startsWith(key, "busway:")) {
    1790              :             // handle special busway keys
    1791           17 :             const std::string buswaySpec = key.substr(7);
    1792              :             key = "busway";
    1793           17 :             if (buswaySpec == "right") {
    1794           13 :                 myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_FORWARD);
    1795            4 :             } else if (buswaySpec == "left") {
    1796            4 :                 myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BACKWARD);
    1797            0 :             } else if (buswaySpec == "both") {
    1798            0 :                 myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BOTH);
    1799              :             } else {
    1800              :                 key = "ignore";
    1801              :             }
    1802              :         }
    1803       221991 :         if (myAllAttributes && (myExtraAttributes.count(key) != 0 || myExtraAttributes.size() == 0)) {
    1804          998 :             const std::string info = "way=" + toString(myCurrentEdge->id) + ", k=" + key;
    1805         1996 :             myCurrentEdge->setParameter(key, attrs.get<std::string>(SUMO_ATTR_V, info.c_str(), ok, false));
    1806              :         }
    1807              :         // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
    1808       435732 :         if (!StringUtils::endsWith(key, "way")
    1809       407476 :                 && !StringUtils::startsWith(key, "lanes")
    1810       185080 :                 && key != "maxspeed" && key != "maxspeed:type"
    1811       178183 :                 && key != "zone:maxspeed"
    1812       178141 :                 && key != "maxspeed:forward" && key != "maxspeed:backward"
    1813       178110 :                 && key != "junction" && key != "name" && key != "tracks" && key != "layer"
    1814       163566 :                 && key != "route"
    1815       381426 :                 && !StringUtils::startsWith(key, "cycleway")
    1816       377826 :                 && !StringUtils::startsWith(key, "sidewalk")
    1817       155715 :                 && key != "ref"
    1818       152639 :                 && key != "highspeed"
    1819       370495 :                 && !StringUtils::startsWith(key, "parking")
    1820       367433 :                 && !StringUtils::startsWith(key, "change")
    1821       367349 :                 && !StringUtils::startsWith(key, "vehicle:lanes")
    1822       149422 :                 && key != "postal_code"
    1823       144775 :                 && key != "railway:preferred_direction"
    1824       144582 :                 && key != "railway:bidirectional"
    1825       144578 :                 && key != "railway:track_ref"
    1826       144458 :                 && key != "usage"
    1827       142863 :                 && key != "access"
    1828       141323 :                 && key != "emergency"
    1829       141283 :                 && key != "service"
    1830       139788 :                 && key != "electrified"
    1831       136613 :                 && key != "segregated"
    1832       136252 :                 && key != "bus"
    1833       135706 :                 && key != "psv"
    1834       135553 :                 && key != "foot"
    1835       133154 :                 && key != "bicycle"
    1836       131153 :                 && key != "oneway:bicycle"
    1837       130932 :                 && key != "oneway:bus"
    1838       130917 :                 && key != "oneway:psv"
    1839       130906 :                 && key != "bus:lanes"
    1840       130868 :                 && key != "bus:lanes:forward"
    1841       130828 :                 && key != "bus:lanes:backward"
    1842       130798 :                 && key != "psv:lanes"
    1843       130790 :                 && key != "psv:lanes:forward"
    1844       130787 :                 && key != "psv:lanes:backward"
    1845       130784 :                 && key != "bicycle:lanes"
    1846       130779 :                 && key != "bicycle:lanes:forward"
    1847       130766 :                 && key != "bicycle:lanes:backward"
    1848       348626 :                 && !StringUtils::startsWith(key, "width")
    1849       479041 :                 && !(StringUtils::startsWith(key, "turn:") && key.find(":lanes") != std::string::npos)
    1850       347042 :                 && key != "public_transport") {
    1851              :             return;
    1852              :         }
    1853        89554 :         const std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentEdge->id).c_str(), ok, false);
    1854              : 
    1855       161294 :         if (key == "highway" || key == "railway" || key == "waterway" || StringUtils::startsWith(key, "cycleway")
    1856       212325 :                 || key == "busway" || key == "route" || StringUtils::startsWith(key, "sidewalk") || key == "highspeed"
    1857       148812 :                 || key == "aeroway" || key == "aerialway" || key == "usage" || key == "service") {
    1858              :             // build type id
    1859        86829 :             if (key != "highway" || myTypeCont.knows(key + "." + value)) {
    1860        33268 :                 myCurrentEdge->myCurrentIsRoad = true;
    1861              :             }
    1862              :             // special cycleway stuff https://wiki.openstreetmap.org/wiki/Key:cycleway
    1863        33387 :             if (key == "cycleway") {
    1864          748 :                 if (value == "no" || value == "none" || value == "separate") {
    1865           41 :                     myCurrentEdge->myCyclewayType = WAY_NONE;
    1866          707 :                 } else if (value == "both") {
    1867            0 :                     myCurrentEdge->myCyclewayType = WAY_BOTH;
    1868          707 :                 } else if (value == "right") {
    1869            0 :                     myCurrentEdge->myCyclewayType = WAY_FORWARD;
    1870          707 :                 } else if (value == "left") {
    1871            0 :                     myCurrentEdge->myCyclewayType = WAY_BACKWARD;
    1872          707 :                 } else if (value == "opposite_track") {
    1873            6 :                     myCurrentEdge->myCyclewayType = WAY_BACKWARD;
    1874          701 :                 } else if (value == "opposite_lane") {
    1875            5 :                     myCurrentEdge->myCyclewayType = WAY_BACKWARD;
    1876          696 :                 } else if (value == "opposite") {
    1877              :                     // according to the wiki ref above, this should rather be a bidi lane, see #13438
    1878          110 :                     myCurrentEdge->myCyclewayType = WAY_BACKWARD;
    1879              :                 }
    1880              :             }
    1881        33387 :             if (key == "cycleway:left") {
    1882          475 :                 if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {
    1883          475 :                     myCurrentEdge->myCyclewayType = WAY_NONE;
    1884              :                 }
    1885          475 :                 if (value == "yes" || value == "lane" || value == "track") {
    1886           73 :                     myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_BACKWARD);
    1887              :                 }
    1888              :                 key = "cycleway"; // for type adaption
    1889              :             }
    1890        33387 :             if (key == "cycleway:right") {
    1891         1256 :                 if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {
    1892          827 :                     myCurrentEdge->myCyclewayType = WAY_NONE;
    1893              :                 }
    1894         1256 :                 if (value == "yes" || value == "lane" || value == "track") {
    1895          824 :                     myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_FORWARD);
    1896              :                 }
    1897              :                 key = "cycleway"; // for type adaption
    1898              :             }
    1899        33387 :             if (key == "cycleway:both") {
    1900          481 :                 if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {
    1901          480 :                     if (value == "no" || value == "none" || value == "separate") {
    1902          396 :                         myCurrentEdge->myCyclewayType = WAY_NONE;
    1903              :                     }
    1904          480 :                     if (value == "yes" || value == "lane" || value == "track") {
    1905           77 :                         myCurrentEdge->myCyclewayType = WAY_BOTH;
    1906              :                     }
    1907              :                 }
    1908              :                 key = "cycleway"; // for type adaption
    1909              :             }
    1910        33387 :             if (key == "cycleway" && value != "lane" && value != "track" && value != "opposite_track" && value != "opposite_lane") {
    1911              :                 // typemap covers only the lane and track cases
    1912         7275 :                 return;
    1913              :             }
    1914        63560 :             if (StringUtils::startsWith(key, "cycleway:")) {
    1915              :                 // no need to extend the type id for other cycleway sub tags
    1916              :                 return;
    1917              :             }
    1918              :             // special sidewalk stuff
    1919        30387 :             if (key == "sidewalk") {
    1920         2703 :                 if (value == "no" || value == "none" || value == "separate") {
    1921          283 :                     myCurrentEdge->mySidewalkType = WAY_NONE;
    1922         2420 :                 } else if (value == "both") {
    1923         1392 :                     myCurrentEdge->mySidewalkType = WAY_BOTH;
    1924         1028 :                 } else if (value == "right") {
    1925          966 :                     myCurrentEdge->mySidewalkType = WAY_FORWARD;
    1926           62 :                 } else if (value == "left") {
    1927           62 :                     myCurrentEdge->mySidewalkType = WAY_BACKWARD;
    1928              :                 }
    1929              :             }
    1930        30387 :             if (key == "sidewalk:left") {
    1931          495 :                 if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {
    1932          495 :                     myCurrentEdge->mySidewalkType = WAY_NONE;
    1933              :                 }
    1934          495 :                 if (value == "yes") {
    1935            2 :                     myCurrentEdge->mySidewalkType = (WayType)(myCurrentEdge->mySidewalkType | WAY_BACKWARD);
    1936              :                 }
    1937              :             }
    1938        30387 :             if (key == "sidewalk:right") {
    1939          500 :                 if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {
    1940            6 :                     myCurrentEdge->mySidewalkType = WAY_NONE;
    1941              :                 }
    1942          500 :                 if (value == "yes") {
    1943           19 :                     myCurrentEdge->mySidewalkType = (WayType)(myCurrentEdge->mySidewalkType | WAY_FORWARD);
    1944              :                 }
    1945              :             }
    1946        30387 :             if (key == "sidewalk:both") {
    1947          231 :                 if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {
    1948          231 :                     if (value == "no" || value == "none" || value == "separate") {
    1949          231 :                         myCurrentEdge->mySidewalkType = WAY_NONE;
    1950              :                     }
    1951          231 :                     if (value == "yes") {
    1952            0 :                         myCurrentEdge->mySidewalkType = WAY_BOTH;
    1953              :                     }
    1954              :                 }
    1955              :             }
    1956        60774 :             if (StringUtils::startsWith(key, "sidewalk")) {
    1957              :                 // no need to extend the type id
    1958              :                 return;
    1959              :             }
    1960              :             // special busway stuff
    1961        26142 :             if (key == "busway") {
    1962           24 :                 if (value == "no") {
    1963              :                     return;
    1964              :                 }
    1965           24 :                 if (value == "opposite_track") {
    1966            0 :                     myCurrentEdge->myBuswayType = WAY_BACKWARD;
    1967           24 :                 } else if (value == "opposite_lane") {
    1968            9 :                     myCurrentEdge->myBuswayType = WAY_BACKWARD;
    1969              :                 }
    1970              :                 // no need to extend the type id
    1971           24 :                 return;
    1972              :             }
    1973        26118 :             std::string singleTypeID = key + "." + value;
    1974        26118 :             if (key == "highspeed") {
    1975           10 :                 if (value == "no") {
    1976              :                     return;
    1977              :                 }
    1978              :                 singleTypeID = "railway.highspeed";
    1979              :             }
    1980        26112 :             addType(singleTypeID);
    1981              : 
    1982        56167 :         } else if (key == "bus" || key == "psv") {
    1983              :             // 'psv' includes taxi in the UK but not in germany
    1984              :             try {
    1985          699 :                 if (StringUtils::toBool(value)) {
    1986          654 :                     myCurrentEdge->myExtraAllowed |= SVC_BUS;
    1987          654 :                     addType(key);
    1988              :                 } else {
    1989            6 :                     myCurrentEdge->myExtraDisallowed |= SVC_BUS;
    1990              :                 }
    1991           39 :             } catch (const BoolFormatException&) {
    1992           39 :                 myCurrentEdge->myExtraAllowed |= SVC_BUS;
    1993           39 :                 addType(key);
    1994           39 :             }
    1995        55468 :         } else if (key == "emergency") {
    1996              :             try {
    1997           40 :                 if (StringUtils::toBool(value)) {
    1998           31 :                     myCurrentEdge->myExtraAllowed |= SVC_AUTHORITY | SVC_EMERGENCY;
    1999              :                 }
    2000            9 :             } catch (const BoolFormatException&) {
    2001            9 :                 myCurrentEdge->myExtraAllowed |= SVC_AUTHORITY | SVC_EMERGENCY;
    2002            9 :             }
    2003        55428 :         } else if (key == "access") {
    2004         1540 :             if (value == "no") {
    2005          194 :                 myCurrentEdge->myExtraDisallowed |= ~(SVC_PUBLIC_CLASSES | SVC_EMERGENCY | SVC_AUTHORITY);
    2006              :             }
    2007       107776 :         } else if (StringUtils::startsWith(key, "width:lanes")) {
    2008              :             try {
    2009           15 :                 const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
    2010              :                 std::vector<double> widthLanes;
    2011           19 :                 for (std::string width : values) {
    2012           14 :                     const double parsedWidth = width == "" ? -1 : StringUtils::parseDist(width);
    2013           14 :                     widthLanes.push_back(parsedWidth);
    2014              :                 }
    2015              : 
    2016            5 :                 if (key == "width:lanes" || key == "width:lanes:forward") {
    2017            3 :                     myCurrentEdge->myWidthLanesForward = widthLanes;
    2018            2 :                 } else if (key == "width:lanes:backward") {
    2019            2 :                     myCurrentEdge->myWidthLanesBackward = widthLanes;
    2020              :                 } else {
    2021            0 :                     WRITE_WARNINGF(TL("Using default lane width for edge '%' as key '%' could not be parsed."), toString(myCurrentEdge->id), key);
    2022              :                 }
    2023            5 :             } catch (const NumberFormatException&) {
    2024            0 :                 WRITE_WARNINGF(TL("Using default lane width for edge '%' as value '%' could not be parsed."), toString(myCurrentEdge->id), value);
    2025            0 :             }
    2026        53883 :         } else if (key == "width") {
    2027              :             try {
    2028          638 :                 myCurrentEdge->myWidth = StringUtils::parseDist(value);
    2029            0 :             } catch (const NumberFormatException&) {
    2030            0 :                 WRITE_WARNINGF(TL("Using default width for edge '%' as value '%' could not be parsed."), toString(myCurrentEdge->id), value);
    2031            0 :             }
    2032        53245 :         } else if (key == "foot") {
    2033         2399 :             if (value == "use_sidepath" || value == "no") {
    2034         1370 :                 myCurrentEdge->myExtraDisallowed |= SVC_PEDESTRIAN;
    2035         1029 :             } else if (value == "yes" || value == "designated" || value == "permissive") {
    2036          973 :                 myCurrentEdge->myExtraAllowed |= SVC_PEDESTRIAN;
    2037              :             }
    2038        50846 :         } else if (key == "bicycle") {
    2039         2001 :             if (value == "use_sidepath" || value == "no") {
    2040          726 :                 myCurrentEdge->myExtraDisallowed |= SVC_BICYCLE;
    2041         1275 :             } else if (value == "yes" || value == "designated" || value == "permissive") {
    2042         1163 :                 myCurrentEdge->myExtraAllowed |= SVC_BICYCLE;
    2043              :             }
    2044        48845 :         } else if (key == "oneway:bicycle") {
    2045          442 :             myCurrentEdge->myExtraTags["oneway:bicycle"] = value;
    2046        48624 :         } else if (key == "oneway:bus" || key == "oneway:psv") {
    2047           26 :             if (value == "no") {
    2048              :                 // need to add a bus way in reversed direction of way
    2049           26 :                 myCurrentEdge->myBuswayType = WAY_BACKWARD;
    2050              :             }
    2051        48598 :         } else if (key == "lanes") {
    2052              :             try {
    2053         3654 :                 myCurrentEdge->myNoLanes = StringUtils::toInt(value);
    2054            1 :             } catch (NumberFormatException&) {
    2055              :                 // might be a list of values
    2056            3 :                 StringTokenizer st(value, ";", true);
    2057            1 :                 std::vector<std::string> list = st.getVector();
    2058            1 :                 if (list.size() >= 2) {
    2059              :                     int minLanes = std::numeric_limits<int>::max();
    2060              :                     try {
    2061            4 :                         for (auto& i : list) {
    2062            6 :                             const int numLanes = StringUtils::toInt(StringUtils::prune(i));
    2063              :                             minLanes = MIN2(minLanes, numLanes);
    2064              :                         }
    2065            1 :                         myCurrentEdge->myNoLanes = minLanes;
    2066            3 :                         WRITE_WARNINGF(TL("Using minimum lane number from list (%) for edge '%'."), value, toString(myCurrentEdge->id));
    2067            0 :                     } catch (NumberFormatException&) {
    2068            0 :                         WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
    2069            0 :                     }
    2070              :                 }
    2071            1 :             } catch (EmptyData&) {
    2072            0 :                 WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
    2073            0 :             }
    2074        44944 :         } else if (key == "lanes:forward") {
    2075              :             try {
    2076          392 :                 const int numLanes = StringUtils::toInt(value);
    2077          392 :                 if (myCurrentEdge->myNoLanesForward < 0 && myCurrentEdge->myNoLanes < 0) {
    2078              :                     // fix lane count in case only lanes:forward and lanes:backward are set
    2079           31 :                     myCurrentEdge->myNoLanes = numLanes - myCurrentEdge->myNoLanesForward;
    2080              :                 }
    2081          392 :                 myCurrentEdge->myNoLanesForward = numLanes;
    2082            0 :             } catch (...) {
    2083            0 :                 WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
    2084            0 :             }
    2085        44552 :         } else if (key == "lanes:backward") {
    2086              :             try {
    2087          375 :                 const int numLanes = StringUtils::toInt(value);
    2088          375 :                 if (myCurrentEdge->myNoLanesForward > 0 && myCurrentEdge->myNoLanes < 0) {
    2089              :                     // fix lane count in case only lanes:forward and lanes:backward are set
    2090            2 :                     myCurrentEdge->myNoLanes = numLanes + myCurrentEdge->myNoLanesForward;
    2091              :                 }
    2092              :                 // denote backwards count with a negative sign
    2093          375 :                 myCurrentEdge->myNoLanesForward = -numLanes;
    2094            0 :             } catch (...) {
    2095            0 :                 WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
    2096            0 :             }
    2097        44177 :         } else if (myCurrentEdge->myMaxSpeed == MAXSPEED_UNGIVEN &&
    2098        26730 :                    (key == "maxspeed" || key == "maxspeed:type" || key == "maxspeed:forward" || key == "zone:maxspeed")) {
    2099              :             // both 'maxspeed' and 'maxspeed:type' may be given so we must take care not to overwrite an already seen value
    2100         6769 :             myCurrentEdge->myMaxSpeed = interpretSpeed(key, value);
    2101        37408 :         } else if (key == "maxspeed:backward" && myCurrentEdge->myMaxSpeedBackward == MAXSPEED_UNGIVEN) {
    2102           13 :             myCurrentEdge->myMaxSpeedBackward = interpretSpeed(key, value);
    2103        37395 :         } else if (key == "junction") {
    2104           55 :             if ((value == "roundabout" || value == "circular") && myCurrentEdge->myIsOneWay.empty()) {
    2105           36 :                 myCurrentEdge->myIsOneWay = "yes";
    2106              :             }
    2107           55 :             if (value == "roundabout") {
    2108           28 :                 myCurrentEdge->myAmInRoundabout = true;
    2109              :             }
    2110        37340 :         } else if (key == "oneway") {
    2111         4200 :             myCurrentEdge->myIsOneWay = value;
    2112        33140 :         } else if (key == "name") {
    2113        10274 :             myCurrentEdge->streetName = value;
    2114        22866 :         } else if (key == "ref") {
    2115         3076 :             myCurrentEdge->ref = value;
    2116         6152 :             myCurrentEdge->setParameter("ref", value);
    2117        19790 :         } else if (key == "layer") {
    2118              :             try {
    2119         3812 :                 myCurrentEdge->myLayer = StringUtils::toInt(value);
    2120            0 :             } catch (...) {
    2121            0 :                 WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
    2122            0 :             }
    2123        15978 :         } else if (key == "tracks") {
    2124              :             try {
    2125          403 :                 if (StringUtils::toInt(value) == 1) {
    2126          373 :                     myCurrentEdge->myIsOneWay = "true";
    2127              :                 } else {
    2128           87 :                     WRITE_WARNINGF(TL("Ignoring track count % for edge '%'."), value, myCurrentEdge->id);
    2129              :                 }
    2130            1 :             } catch (...) {
    2131            3 :                 WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
    2132            1 :             }
    2133        15575 :         } else if (key == "railway:preferred_direction") {
    2134          193 :             if (value == "both") {
    2135           41 :                 myCurrentEdge->myRailDirection = WAY_BOTH;
    2136          152 :             } else if (myCurrentEdge->myRailDirection == WAY_UNKNOWN) {
    2137          150 :                 if (value == "backward") {
    2138            4 :                     myCurrentEdge->myRailDirection = WAY_BACKWARD;
    2139          146 :                 } else if (value == "forward") {
    2140          146 :                     myCurrentEdge->myRailDirection = WAY_FORWARD;
    2141              :                 }
    2142              :             }
    2143        15382 :         } else if (key == "railway:bidirectional") {
    2144            4 :             if (value == "regular") {
    2145            4 :                 myCurrentEdge->myRailDirection = WAY_BOTH;
    2146              :             }
    2147        15378 :         } else if (key == "electrified" || key == "segregated") {
    2148         3536 :             if (value != "no") {
    2149         3276 :                 myCurrentEdge->myExtraTags[key] = value;
    2150              :             }
    2151        11842 :         } else if (key == "railway:track_ref") {
    2152          120 :             myCurrentEdge->setParameter(key, value);
    2153        11722 :         } else if (key == "public_transport" && value == "platform") {
    2154         1700 :             myCurrentEdge->myExtraTags["platform"] = "yes";
    2155        12119 :         } else if ((key == "parking:both" || key == "parking:lane:both") && !StringUtils::startsWith(value, "no")) {
    2156          121 :             myCurrentEdge->myParkingType |= PARKING_BOTH;
    2157        11165 :         } else if ((key == "parking:left" || key == "parking:lane:left") && !StringUtils::startsWith(value, "no")) {
    2158           84 :             myCurrentEdge->myParkingType |= PARKING_LEFT;
    2159        11137 :         } else if ((key == "parking:right" || key == "parking:lane:right") && !StringUtils::startsWith(value, "no")) {
    2160          220 :             myCurrentEdge->myParkingType |= PARKING_RIGHT;
    2161        10447 :         } else if (key == "change" || key == "change:lanes") {
    2162           14 :             myCurrentEdge->myChangeForward = myCurrentEdge->myChangeBackward = interpretChangeType(value);
    2163        10433 :         } else if (key == "change:forward" || key == "change:lanes:forward") {
    2164           35 :             myCurrentEdge->myChangeForward = interpretChangeType(value);
    2165        10398 :         } else if (key == "change:backward" || key == "change:lanes:backward") {
    2166           35 :             myCurrentEdge->myChangeBackward = interpretChangeType(value);
    2167        10363 :         } else if (key == "vehicle:lanes" || key == "vehicle:lanes:forward") {
    2168           44 :             interpretLaneUse(value, SVC_PASSENGER, true);
    2169           44 :             interpretLaneUse(value, SVC_PRIVATE, true);
    2170        10319 :         } else if (key == "vehicle:lanes:backward") {
    2171           16 :             interpretLaneUse(value, SVC_PASSENGER, false);
    2172           16 :             interpretLaneUse(value, SVC_PRIVATE, false);
    2173        10303 :         } else if (key == "bus:lanes" || key == "bus:lanes:forward") {
    2174           78 :             interpretLaneUse(value, SVC_BUS, true);
    2175        10225 :         } else if (key == "bus:lanes:backward") {
    2176           30 :             interpretLaneUse(value, SVC_BUS, false);
    2177        10195 :         } else if (key == "psv:lanes" || key == "psv:lanes:forward") {
    2178           11 :             interpretLaneUse(value, SVC_BUS, true);
    2179           11 :             interpretLaneUse(value, SVC_TAXI, true);
    2180        10184 :         } else if (key == "psv:lanes:backward") {
    2181            3 :             interpretLaneUse(value, SVC_BUS, false);
    2182            3 :             interpretLaneUse(value, SVC_TAXI, false);
    2183        10181 :         } else if (key == "bicycle:lanes" || key == "bicycle:lanes:forward") {
    2184           18 :             interpretLaneUse(value, SVC_BICYCLE, true);
    2185        10163 :         } else if (key == "bicycle:lanes:backward") {
    2186            6 :             interpretLaneUse(value, SVC_BICYCLE, false);
    2187        21255 :         } else if (StringUtils::startsWith(key, "turn:") && key.find(":lanes") != std::string::npos) {
    2188              :             int shift = 0;
    2189              :             // use the first 8 bit to encode permitted directions for all classes
    2190              :             // and the successive 8 bit blocks for selected classes
    2191         1878 :             if (StringUtils::startsWith(key, "turn:bus") || StringUtils::startsWith(key, "turn:psv:")) {
    2192              :                 shift = NBEdge::TURN_SIGN_SHIFT_BUS;
    2193         1874 :             } else if (StringUtils::startsWith(key, "turn:taxi")) {
    2194              :                 shift = NBEdge::TURN_SIGN_SHIFT_TAXI;
    2195         1874 :             } else if (StringUtils::startsWith(key, "turn:bicycle")) {
    2196              :                 shift = NBEdge::TURN_SIGN_SHIFT_BICYCLE;
    2197              :             }
    2198         2823 :             const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
    2199              :             std::vector<int> turnCodes;
    2200         3478 :             for (std::string codeList : values) {
    2201         7611 :                 const std::vector<std::string> codes = StringTokenizer(codeList, ";").getVector();
    2202         2537 :                 int turnCode = 0;
    2203         2537 :                 if (codes.size() == 0) {
    2204           24 :                     turnCode = (int)LinkDirection::STRAIGHT;
    2205              :                 }
    2206         5443 :                 for (std::string code : codes) {
    2207         2906 :                     if (code == "" || code == "none" || code == "through") {
    2208         1451 :                         turnCode |= (int)LinkDirection::STRAIGHT << shift ;
    2209         1455 :                     } else if (code == "left" || code == "sharp_left") {
    2210          707 :                         turnCode |= (int)LinkDirection::LEFT << shift;
    2211          748 :                     } else if (code == "right" || code == "sharp_right") {
    2212          592 :                         turnCode |= (int)LinkDirection::RIGHT << shift;
    2213          156 :                     } else if (code == "slight_left") {
    2214           62 :                         turnCode |= (int)LinkDirection::PARTLEFT << shift;
    2215           94 :                     } else if (code == "slight_right") {
    2216           53 :                         turnCode |= (int)LinkDirection::PARTRIGHT << shift;
    2217           41 :                     } else if (code == "reverse") {
    2218            0 :                         turnCode |= (int)LinkDirection::TURN << shift;
    2219           41 :                     } else if (code == "merge_to_left" || code == "merge_to_right") {
    2220           37 :                         turnCode |= (int)LinkDirection::NODIR << shift;
    2221              :                     }
    2222              :                 }
    2223         2537 :                 turnCodes.push_back(turnCode);
    2224         2537 :             }
    2225         1224 :             if (StringUtils::endsWith(key, "lanes") || StringUtils::endsWith(key, "lanes:forward")) {
    2226          828 :                 mergeTurnSigns(myCurrentEdge->myTurnSignsForward, turnCodes);
    2227          226 :             } else if (StringUtils::endsWith(key, "lanes:backward")) {
    2228          113 :                 mergeTurnSigns(myCurrentEdge->myTurnSignsBackward, turnCodes);
    2229            0 :             } else if (StringUtils::endsWith(key, "lanes:both_ways")) {
    2230            0 :                 mergeTurnSigns(myCurrentEdge->myTurnSignsForward, turnCodes);
    2231            0 :                 mergeTurnSigns(myCurrentEdge->myTurnSignsBackward, turnCodes);
    2232              :             }
    2233          941 :         }
    2234              :     }
    2235              : }
    2236              : 
    2237              : 
    2238              : void
    2239        26805 : NIImporter_OpenStreetMap::EdgesHandler::addType(const std::string& singleTypeID) {
    2240              :     // special case: never build compound type for highspeed rail
    2241        26805 :     if (!myCurrentEdge->myHighWayType.empty() && singleTypeID != "railway.highspeed") {
    2242         4783 :         if (myCurrentEdge->myHighWayType == "railway.highspeed") {
    2243            8 :             return;
    2244              :         }
    2245              :         // osm-ways may be used by more than one mode (eg railway.tram + highway.residential. this is relevant for multimodal traffic)
    2246              :         // we create a new type for this kind of situation which must then be resolved in insertEdge()
    2247        19100 :         std::vector<std::string> types = StringTokenizer(myCurrentEdge->myHighWayType,
    2248         4775 :                                          compoundTypeSeparator).getVector();
    2249         4775 :         types.push_back(singleTypeID);
    2250         4775 :         myCurrentEdge->myHighWayType = joinToStringSorting(types, compoundTypeSeparator);
    2251         4775 :     } else {
    2252        22022 :         myCurrentEdge->myHighWayType = singleTypeID;
    2253              :     }
    2254              : }
    2255              : 
    2256              : 
    2257              : double
    2258         6782 : NIImporter_OpenStreetMap::EdgesHandler::interpretSpeed(const std::string& key, std::string value) {
    2259         6782 :     if (mySpeedMap.find(value) != mySpeedMap.end()) {
    2260           32 :         return mySpeedMap[value];
    2261              :     } else {
    2262              :         // handle symbolic names of the form DE:30 / DE:zone30
    2263         6750 :         if (value.size() > 3 && value[2] == ':') {
    2264            6 :             if (value.substr(3, 4) == "zone") {
    2265            2 :                 value = value.substr(7);
    2266              :             } else {
    2267           10 :                 value = value.substr(3);
    2268              :             }
    2269              :         }
    2270              :         try {
    2271         6750 :             return StringUtils::parseSpeed(value);
    2272            4 :         } catch (...) {
    2273           16 :             WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
    2274              :                           toString(myCurrentEdge->id) + "'.");
    2275              :             return MAXSPEED_UNGIVEN;
    2276            4 :         }
    2277              :     }
    2278              : }
    2279              : 
    2280              : 
    2281              : int
    2282           84 : NIImporter_OpenStreetMap::EdgesHandler::interpretChangeType(const std::string& value) const {
    2283              :     int result = 0;
    2284          252 :     const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
    2285          233 :     for (const std::string& val : values) {
    2286          149 :         if (val == "no") {
    2287           35 :             result += CHANGE_NO;
    2288          114 :         } else if (val == "not_left") {
    2289           64 :             result += CHANGE_NO_LEFT;
    2290           50 :         } else if (val == "not_right") {
    2291           11 :             result += CHANGE_NO_RIGHT;
    2292              :         }
    2293          149 :         result = result << 2;
    2294              :     }
    2295              :     // last shift was superfluous
    2296           84 :     result = result >> 2;
    2297              : 
    2298           84 :     if (values.size() > 1) {
    2299           49 :         result += 2 << 29; // mark multi-value input
    2300              :     }
    2301              :     //std::cout << " way=" << myCurrentEdge->id << " value=" << value << " result=" << std::bitset<32>(result) << "\n";
    2302           84 :     return result;
    2303           84 : }
    2304              : 
    2305              : 
    2306              : void
    2307          280 : NIImporter_OpenStreetMap::EdgesHandler::interpretLaneUse(const std::string& value, SUMOVehicleClass svc, const bool forward) const {
    2308          840 :     const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
    2309          280 :     std::vector<bool>& designated = forward ? myCurrentEdge->myDesignatedLaneForward : myCurrentEdge->myDesignatedLaneBackward;
    2310          280 :     std::vector<SVCPermissions>& allowed = forward ? myCurrentEdge->myAllowedLaneForward : myCurrentEdge->myAllowedLaneBackward;
    2311          280 :     std::vector<SVCPermissions>& disallowed = forward ? myCurrentEdge->myDisallowedLaneForward : myCurrentEdge->myDisallowedLaneBackward;
    2312          280 :     designated.resize(MAX2(designated.size(), values.size()), false);
    2313          280 :     allowed.resize(MAX2(allowed.size(), values.size()), SVC_IGNORING);
    2314          280 :     disallowed.resize(MAX2(disallowed.size(), values.size()), SVC_IGNORING);
    2315              :     int i = 0;
    2316          997 :     for (const std::string& val : values) {
    2317          717 :         if (val == "yes" || val == "permissive") {
    2318          286 :             allowed[i] |= svc;
    2319          431 :         } else if (val == "lane" || val == "designated") {
    2320          152 :             allowed[i] |= svc;
    2321              :             designated[i] = true;
    2322          279 :         } else if (val == "no") {
    2323          233 :             disallowed[i] |= svc;
    2324              :         } else {
    2325          138 :             WRITE_WARNINGF(TL("Unknown lane use specifier '%' ignored for way '%'"), val, myCurrentEdge->id);
    2326              :         }
    2327          717 :         i++;
    2328              :     }
    2329          280 : }
    2330              : 
    2331              : 
    2332              : void
    2333       682406 : NIImporter_OpenStreetMap::EdgesHandler::myEndElement(int element) {
    2334       682406 :     if (element == SUMO_TAG_WAY && myCurrentEdge != nullptr) {
    2335        43508 :         if (myCurrentEdge->myCurrentIsRoad) {
    2336        21511 :             const auto insertionIt = myEdgeMap.lower_bound(myCurrentEdge->id);
    2337        21511 :             if (insertionIt == myEdgeMap.end() || insertionIt->first != myCurrentEdge->id) {
    2338              :                 // assume we are loading multiple files, so we won't report duplicate edges
    2339        21437 :                 myEdgeMap.emplace_hint(insertionIt, myCurrentEdge->id, myCurrentEdge);
    2340              :             } else {
    2341           74 :                 delete myCurrentEdge;
    2342              :             }
    2343        43994 :         } else if (myCurrentEdge->myExtraTags.count("platform") != 0) {
    2344          475 :             const auto insertionIt = myPlatformShapesMap.lower_bound(myCurrentEdge->id);
    2345          475 :             if (insertionIt == myPlatformShapesMap.end() || insertionIt->first != myCurrentEdge->id) {
    2346              :                 // assume we are loading multiple files, so we won't report duplicate platforms
    2347          469 :                 myPlatformShapesMap.emplace_hint(insertionIt, myCurrentEdge->id, myCurrentEdge);
    2348              :             } else {
    2349            6 :                 delete myCurrentEdge;
    2350              :             }
    2351              :         } else {
    2352        21522 :             delete myCurrentEdge;
    2353              :         }
    2354        43508 :         myCurrentEdge = nullptr;
    2355              :     }
    2356       682406 : }
    2357              : 
    2358              : 
    2359              : // ---------------------------------------------------------------------------
    2360              : // definitions of NIImporter_OpenStreetMap::RelationHandler-methods
    2361              : // ---------------------------------------------------------------------------
    2362          188 : NIImporter_OpenStreetMap::RelationHandler::RelationHandler(
    2363              :     const std::map<long long int, NIOSMNode*>& osmNodes,
    2364              :     const std::map<long long int, Edge*>& osmEdges, NBPTStopCont* nbptStopCont,
    2365              :     const std::map<long long int, Edge*>& platformShapes,
    2366              :     NBPTLineCont* nbptLineCont,
    2367          188 :     const OptionsCont& oc) :
    2368              :     SUMOSAXHandler("osm - file"),
    2369          188 :     myOSMNodes(osmNodes),
    2370          188 :     myOSMEdges(osmEdges),
    2371          188 :     myPlatformShapes(platformShapes),
    2372          188 :     myNBPTStopCont(nbptStopCont),
    2373          188 :     myNBPTLineCont(nbptLineCont),
    2374          376 :     myOptionsCont(oc) {
    2375          188 :     resetValues();
    2376          188 : }
    2377              : 
    2378              : 
    2379          376 : NIImporter_OpenStreetMap::RelationHandler::~RelationHandler() = default;
    2380              : 
    2381              : 
    2382              : void
    2383         4887 : NIImporter_OpenStreetMap::RelationHandler::resetValues() {
    2384         4887 :     myCurrentRelation = INVALID_ID;
    2385         4887 :     myIsRestriction = false;
    2386         4887 :     myRestrictionException = SVC_IGNORING;
    2387         4887 :     myFromWay = INVALID_ID;
    2388         4887 :     myToWay = INVALID_ID;
    2389         4887 :     myViaNode = INVALID_ID;
    2390         4887 :     myViaWay = INVALID_ID;
    2391         4887 :     myStation = INVALID_ID;
    2392         4887 :     myRestrictionType = RestrictionType::UNKNOWN;
    2393              :     myPlatforms.clear();
    2394              :     myStops.clear();
    2395              :     myPlatformStops.clear();
    2396              :     myWays.clear();
    2397         4887 :     myIsStopArea = false;
    2398         4887 :     myIsRoute = false;
    2399         4887 :     myPTRouteType = "";
    2400         4887 :     myRouteColor.setValid(false);
    2401         4887 : }
    2402              : 
    2403              : 
    2404              : void
    2405       469902 : NIImporter_OpenStreetMap::RelationHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
    2406       469902 :     if (element == SUMO_TAG_RELATION) {
    2407         4699 :         bool ok = true;
    2408         4699 :         myCurrentRelation = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);
    2409         4699 :         const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, nullptr, ok);
    2410         4699 :         if (action == "delete" || !ok) {
    2411            0 :             myCurrentRelation = INVALID_ID;
    2412              :         }
    2413         4699 :         myName = "";
    2414         4699 :         myRef = "";
    2415         4699 :         myInterval = -1;
    2416         4699 :         myNightService = "";
    2417              :         return;
    2418              :     }
    2419       465203 :     if (myCurrentRelation == INVALID_ID) {
    2420              :         return;
    2421              :     }
    2422       465202 :     if (element == SUMO_TAG_MEMBER) {
    2423       425246 :         bool ok = true;
    2424      1275738 :         std::string role = attrs.hasAttribute("role") ? attrs.getStringSecure("role", "") : "";
    2425       425246 :         const long long int ref = attrs.get<long long int>(SUMO_ATTR_REF, nullptr, ok);
    2426       425246 :         if (role == "via") {
    2427              :             // u-turns for divided ways may be given with 2 via-nodes or 1 via-way
    2428          408 :             std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
    2429          408 :             if (memberType == "way" && checkEdgeRef(ref)) {
    2430           11 :                 myViaWay = ref;
    2431          397 :             } else if (memberType == "node") {
    2432          786 :                 if (myOSMNodes.find(ref) != myOSMNodes.end()) {
    2433          388 :                     myViaNode = ref;
    2434              :                 } else {
    2435           10 :                     WRITE_WARNINGF(TL("No node found for reference '%' in relation '%'."), toString(ref), toString(myCurrentRelation));
    2436              :                 }
    2437              :             }
    2438       424838 :         } else if (role == "from" && checkEdgeRef(ref)) {
    2439          374 :             myFromWay = ref;
    2440       424464 :         } else if (role == "to" && checkEdgeRef(ref)) {
    2441          334 :             myToWay = ref;
    2442       848260 :         } else if (StringUtils::startsWith(role, "stop")) {
    2443              :             // permit _entry_only and _exit_only variants
    2444        31545 :             myStops.push_back(ref);
    2445       785170 :         } else if (StringUtils::startsWith(role, "platform")) {
    2446              :             // permit _entry_only and _exit_only variants
    2447        30161 :             std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
    2448        30161 :             if (memberType == "way") {
    2449        22093 :                 const std::map<long long int, NIImporter_OpenStreetMap::Edge*>::const_iterator& wayIt = myPlatformShapes.find(ref);
    2450        22093 :                 if (wayIt != myPlatformShapes.end()) {
    2451              :                     NIIPTPlatform platform;
    2452         1200 :                     platform.isWay = true;
    2453         1200 :                     platform.ref = ref;
    2454         1200 :                     myPlatforms.push_back(platform);
    2455              :                 }
    2456         8068 :             } else if (memberType == "node") {
    2457              :                 // myIsStopArea may not be set yet
    2458         5638 :                 myStops.push_back(ref);
    2459              :                 myPlatformStops.insert(ref);
    2460              :                 NIIPTPlatform platform;
    2461         5638 :                 platform.isWay = false;
    2462         5638 :                 platform.ref = ref;
    2463         5638 :                 myPlatforms.push_back(platform);
    2464              :             }
    2465              : 
    2466       362424 :         } else if (role == "station") {
    2467            5 :             myStation = ref;
    2468       362419 :         } else if (role.empty()) {
    2469       315414 :             std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
    2470       315414 :             if (memberType == "way") {
    2471       309030 :                 myWays.push_back(ref);
    2472         6384 :             } else if (memberType == "node") {
    2473         4962 :                 auto it = myOSMNodes.find(ref);
    2474         5926 :                 if (it != myOSMNodes.end() && it->second->hasParameter("railway:ref")) {
    2475           20 :                     myStation = ref;
    2476              :                 } else {
    2477         4942 :                     myStops.push_back(ref);
    2478              :                 }
    2479              :             }
    2480              :         }
    2481              :         return;
    2482              :     }
    2483              :     // parse values
    2484        39956 :     if (element == SUMO_TAG_TAG) {
    2485        39956 :         bool ok = true;
    2486        39956 :         std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentRelation).c_str(), ok, false);
    2487              :         // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
    2488        39956 :         if (key == "type" || key == "restriction") {
    2489         5082 :             std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2490         5082 :             if (key == "type" && value == "restriction") {
    2491          391 :                 myIsRestriction = true;
    2492          391 :                 return;
    2493              :             }
    2494         4691 :             if (key == "type" && value == "route") {
    2495         1638 :                 myIsRoute = true;
    2496         1638 :                 return;
    2497              :             }
    2498         3053 :             if (key == "restriction") {
    2499              :                 // @note: the 'right/left/straight' part is ignored since the information is
    2500              :                 // redundantly encoded in the 'from', 'to' and 'via' members
    2501          390 :                 if (value.substr(0, 5) == "only_") {
    2502          217 :                     myRestrictionType = RestrictionType::ONLY;
    2503          173 :                 } else if (value.substr(0, 3) == "no_") {
    2504          173 :                     myRestrictionType = RestrictionType::NO;
    2505              :                 } else {
    2506            0 :                     WRITE_WARNINGF(TL("Found unknown restriction type '%' in relation '%'"), value, toString(myCurrentRelation));
    2507              :                 }
    2508          390 :                 return;
    2509              :             }
    2510        34874 :         } else if (key == "except") {
    2511           27 :             std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2512          117 :             for (const std::string& v : StringTokenizer(value, ";").getVector()) {
    2513           36 :                 if (v == "psv") {
    2514           16 :                     myRestrictionException |= SVC_BUS;
    2515           20 :                 } else if (v == "bicycle") {
    2516           12 :                     myRestrictionException |= SVC_BICYCLE;
    2517            8 :                 } else if (v == "hgv") {
    2518            0 :                     myRestrictionException |= SVC_TRUCK | SVC_TRAILER;
    2519            8 :                 } else if (v == "motorcar") {
    2520            2 :                     myRestrictionException |= SVC_PASSENGER | SVC_TAXI;
    2521            6 :                 } else if (v == "emergency") {
    2522            1 :                     myRestrictionException |= SVC_EMERGENCY;
    2523              :                 }
    2524           27 :             }
    2525        34847 :         } else if (key == "public_transport") {
    2526          469 :             std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2527          469 :             if (value == "stop_area") {
    2528          375 :                 myIsStopArea = true;
    2529              :             }
    2530        34378 :         } else if (key == "route") {
    2531         1644 :             std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2532         1424 :             if (value == "train" || value == "subway" || value == "light_rail" || value == "monorail" || value == "tram" || value == "bus"
    2533         1963 :                     || value == "trolleybus" || value == "aerialway" || value == "ferry" || value == "share_taxi" || value == "minibus") {
    2534         1358 :                 myPTRouteType = value;
    2535              :             }
    2536              : 
    2537        32734 :         } else if (key == "name") {
    2538         5672 :             myName = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2539        29898 :         } else if (key == "colour") {
    2540         1822 :             std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2541              :             try {
    2542         1821 :                 myRouteColor = RGBColor::parseColor(value);
    2543            1 :             } catch (...) {
    2544            3 :                 WRITE_WARNINGF(TL("Invalid color value '%' in relation %"), value, myCurrentRelation);
    2545            1 :             }
    2546        28987 :         } else if (key == "ref") {
    2547         4004 :             myRef = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2548        26985 :         } else if (key == "interval" || key == "headway") {
    2549          510 :             myInterval = attrs.get<int>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2550        26475 :         } else if (key == "by_night") {
    2551          170 :             myNightService = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
    2552              :         }
    2553              :     }
    2554              : }
    2555              : 
    2556              : 
    2557              : bool
    2558          979 : NIImporter_OpenStreetMap::RelationHandler::checkEdgeRef(long long int ref) const {
    2559         1958 :     if (myOSMEdges.find(ref) != myOSMEdges.end()) {
    2560              :         return true;
    2561              :     }
    2562          520 :     WRITE_WARNINGF(TL("No way found for reference '%' in relation '%'"), toString(ref), toString(myCurrentRelation));
    2563          260 :     return false;
    2564              : }
    2565              : 
    2566              : 
    2567              : void
    2568       470023 : NIImporter_OpenStreetMap::RelationHandler::myEndElement(int element) {
    2569       470023 :     if (element == SUMO_TAG_RELATION) {
    2570         4699 :         if (myIsRestriction) {
    2571              :             assert(myCurrentRelation != INVALID_ID);
    2572              :             bool ok = true;
    2573          391 :             if (myRestrictionType == RestrictionType::UNKNOWN) {
    2574            2 :                 WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown type."), toString(myCurrentRelation));
    2575              :                 ok = false;
    2576              :             }
    2577          391 :             if (myFromWay == INVALID_ID) {
    2578          146 :                 WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown from-way."), toString(myCurrentRelation));
    2579              :                 ok = false;
    2580              :             }
    2581          391 :             if (myToWay == INVALID_ID) {
    2582          162 :                 WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown to-way."), toString(myCurrentRelation));
    2583              :                 ok = false;
    2584              :             }
    2585          391 :             if (myViaNode == INVALID_ID && myViaWay == INVALID_ID) {
    2586           32 :                 WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown via."), toString(myCurrentRelation));
    2587              :                 ok = false;
    2588              :             }
    2589          375 :             if (ok && !applyRestriction()) {
    2590           34 :                 WRITE_WARNINGF(TL("Ignoring restriction relation '%'."), toString(myCurrentRelation));
    2591              :             }
    2592         4308 :         } else if (myIsStopArea) {
    2593         1740 :             for (long long ref : myStops) {
    2594         1365 :                 myStopAreas[ref] = myCurrentRelation;
    2595         2730 :                 if (myOSMNodes.find(ref) == myOSMNodes.end()) {
    2596              :                     //WRITE_WARNING(
    2597              :                     //    "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
    2598              :                     //    + "' does not exist. Probably OSM file is incomplete.");
    2599          461 :                     continue;
    2600              :                 }
    2601              : 
    2602         1128 :                 NIOSMNode* n = myOSMNodes.find(ref)->second;
    2603         2256 :                 std::shared_ptr<NBPTStop> ptStop = myNBPTStopCont->get(toString(n->id));
    2604         1128 :                 if (ptStop == nullptr) {
    2605              :                     //WRITE_WARNING(
    2606              :                     //    "Relation '" + toString(myCurrentRelation) + "' refers to a non existing pt stop at node: '"
    2607              :                     //    + toString(n->id) + "'. Probably OSM file is incomplete.");
    2608              :                     continue;
    2609              :                 }
    2610         2142 :                 for (NIIPTPlatform& myPlatform : myPlatforms) {
    2611         1238 :                     if (myPlatform.isWay) {
    2612              :                         assert(myPlatformShapes.find(myPlatform.ref) != myPlatformShapes.end()); //already tested earlier
    2613         1902 :                         Edge* edge = (*myPlatformShapes.find(myPlatform.ref)).second;
    2614          951 :                         if (edge->myCurrentNodes.size() > 1 && edge->myCurrentNodes[0] == *(edge->myCurrentNodes.end() - 1)) {
    2615           88 :                             WRITE_WARNINGF(TL("Platform '%' in relation: '%' is given as polygon, which currently is not supported."), myPlatform.ref, myCurrentRelation);
    2616           93 :                             continue;
    2617              : 
    2618              :                         }
    2619          863 :                         PositionVector p;
    2620         2933 :                         for (auto nodeRef : edge->myCurrentNodes) {
    2621         4140 :                             if (myOSMNodes.find(nodeRef) == myOSMNodes.end()) {
    2622              :                                 //WRITE_WARNING(
    2623              :                                 //    "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
    2624              :                                 //    + "' does not exist. Probably OSM file is incomplete.");
    2625            0 :                                 continue;
    2626              :                             }
    2627         2070 :                             NIOSMNode* pNode = myOSMNodes.find(nodeRef)->second;
    2628         2070 :                             Position pNodePos(pNode->lon, pNode->lat, pNode->ele);
    2629         2070 :                             if (!NBNetBuilder::transformCoordinate(pNodePos)) {
    2630            0 :                                 WRITE_ERRORF("Unable to project coordinates for node '%'.", pNode->id);
    2631            0 :                                 continue;
    2632              :                             }
    2633         2070 :                             p.push_back(pNodePos);
    2634              :                         }
    2635          863 :                         if (p.size() == 0) {
    2636           10 :                             WRITE_WARNINGF(TL("Referenced platform: '%' in relation: '%' is corrupt. Probably OSM file is incomplete."),
    2637              :                                            toString(myPlatform.ref), toString(myCurrentRelation));
    2638              :                             continue;
    2639              :                         }
    2640          858 :                         NBPTPlatform platform(p[(int)p.size() / 2], p.length());
    2641          858 :                         ptStop->addPlatformCand(platform);
    2642          863 :                     } else {
    2643          574 :                         if (myOSMNodes.find(myPlatform.ref) == myOSMNodes.end()) {
    2644              :                             //WRITE_WARNING(
    2645              :                             //    "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
    2646              :                             //    + "' does not exist. Probably OSM file is incomplete.");
    2647           83 :                             continue;
    2648              :                         }
    2649          204 :                         NIOSMNode* pNode = myOSMNodes.find(myPlatform.ref)->second;
    2650          204 :                         Position platformPos(pNode->lon, pNode->lat, pNode->ele);
    2651          204 :                         if (!NBNetBuilder::transformCoordinate(platformPos)) {
    2652            0 :                             WRITE_ERRORF("Unable to project coordinates for node '%'.", pNode->id);
    2653              :                         }
    2654          408 :                         NBPTPlatform platform(platformPos, myOptionsCont.getFloat("osm.stop-output.length"));
    2655          204 :                         ptStop->addPlatformCand(platform);
    2656              : 
    2657              :                     }
    2658              :                 }
    2659          904 :                 ptStop->setIsMultipleStopPositions(myStops.size() > 1, myCurrentRelation);
    2660          904 :                 if (myStation != INVALID_ID) {
    2661           48 :                     const auto& nodeIt = myOSMNodes.find(myStation);
    2662           48 :                     if (nodeIt != myOSMNodes.end()) {
    2663           44 :                         NIOSMNode* station = nodeIt->second;
    2664           44 :                         if (station != nullptr) {
    2665           88 :                             if (station->hasParameter("railway:ref")) {
    2666          126 :                                 ptStop->setParameter("stationRef", station->getParameter("railway:ref"));
    2667              :                             }
    2668              :                         }
    2669              :                     }
    2670              :                 }
    2671              :             }
    2672         3933 :         } else if (myPTRouteType != "" && myIsRoute) {
    2673         1354 :             NBPTLine* ptLine = new NBPTLine(toString(myCurrentRelation), myName, myPTRouteType, myRef, myInterval, myNightService,
    2674         1354 :                                             interpretTransportType(myPTRouteType), myRouteColor);
    2675              :             int consecutiveGap = false;
    2676              :             int missingBefore = 0;
    2677              :             int missingAfter = 0;
    2678        37270 :             for (long long ref : myStops) {
    2679        35938 :                 const auto& nodeIt = myOSMNodes.find(ref);
    2680        35938 :                 if (nodeIt == myOSMNodes.end()) {
    2681        32710 :                     if (ptLine->getStops().empty()) {
    2682        17288 :                         missingBefore++;
    2683              :                     } else {
    2684        15422 :                         missingAfter++;
    2685        15422 :                         consecutiveGap++;
    2686              :                     }
    2687        32710 :                     continue;
    2688              :                 }
    2689              :                 // give some slack for single missing stops
    2690         3228 :                 if (consecutiveGap > 1) {
    2691           44 :                     WRITE_WARNINGF(TL("PT line '%' in relation % has a gap of % stops, only keeping first part."), myName, myCurrentRelation, consecutiveGap);
    2692           22 :                     missingAfter = (int)myStops.size() - missingBefore - (int)ptLine->getStops().size();
    2693           22 :                     break;
    2694              :                 }
    2695              :                 // reset gap
    2696              :                 consecutiveGap = 0;
    2697              : 
    2698         3206 :                 const NIOSMNode* const n = nodeIt->second;
    2699         6412 :                 std::shared_ptr<NBPTStop> ptStop = myNBPTStopCont->get(toString(n->id));
    2700         3206 :                 if (ptStop == nullptr) {
    2701              :                     // loose stop, which must later be mapped onto a line way
    2702          146 :                     Position ptPos(n->lon, n->lat, n->ele);
    2703          146 :                     if (!NBNetBuilder::transformCoordinate(ptPos)) {
    2704            0 :                         WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);
    2705              :                     }
    2706          146 :                     const SumoXMLTag stopElement = isRailway(n->permissions) ? SUMO_TAG_TRAIN_STOP : SUMO_TAG_BUS_STOP;
    2707          146 :                     ptStop = std::make_shared<NBPTStop>(stopElement, toString(n->id), ptPos, "", "", n->ptStopLength, n->name, n->permissions);
    2708          292 :                     myNBPTStopCont->insert(ptStop);
    2709              :                     if (myStopAreas.count(n->id)) {
    2710           12 :                         ptStop->setIsMultipleStopPositions(false, myStopAreas[n->id]);
    2711              :                     }
    2712              :                     if (myPlatformStops.count(n->id) > 0) {
    2713              :                         ptStop->setIsPlatform();
    2714              :                     }
    2715              :                 }
    2716         6412 :                 ptLine->addPTStop(ptStop);
    2717              :             }
    2718       209040 :             for (long long& myWay : myWays) {
    2719       207686 :                 auto entr = myOSMEdges.find(myWay);
    2720       207686 :                 if (entr != myOSMEdges.end()) {
    2721        14116 :                     Edge* edge = entr->second;
    2722       104504 :                     for (long long& myCurrentNode : edge->myCurrentNodes) {
    2723        90388 :                         ptLine->addWayNode(myWay, myCurrentNode);
    2724              :                     }
    2725              :                 }
    2726              :             }
    2727         1354 :             ptLine->setNumOfStops((int)myStops.size(), missingBefore, missingAfter);
    2728         1354 :             if (ptLine->getStops().empty()) {
    2729          219 :                 WRITE_WARNINGF(TL("PT line in relation % with no stops ignored. Probably OSM file is incomplete."), myCurrentRelation);
    2730          219 :                 delete ptLine;
    2731          219 :                 resetValues();
    2732          219 :                 return;
    2733              :             }
    2734         1135 :             if (!myNBPTLineCont->insert(ptLine)) {
    2735            6 :                 WRITE_WARNINGF(TL("Ignoring duplicate PT line '%'."), myCurrentRelation);
    2736            6 :                 delete ptLine;
    2737              :             }
    2738              :         }
    2739              :         // other relations might use similar subelements so reset in any case
    2740         4480 :         resetValues();
    2741              :     }
    2742              : }
    2743              : 
    2744              : bool
    2745          284 : NIImporter_OpenStreetMap::RelationHandler::applyRestriction() const {
    2746              :     // since OSM ways are bidirectional we need the via to figure out which direction was meant
    2747          284 :     if (myViaNode != INVALID_ID) {
    2748          274 :         NBNode* viaNode = myOSMNodes.find(myViaNode)->second->node;
    2749          274 :         if (viaNode == nullptr) {
    2750            0 :             WRITE_WARNINGF(TL("Via-node '%' was not instantiated"), toString(myViaNode));
    2751            0 :             return false;
    2752              :         }
    2753          274 :         NBEdge* from = findEdgeRef(myFromWay, viaNode->getIncomingEdges());
    2754          274 :         NBEdge* to = findEdgeRef(myToWay, viaNode->getOutgoingEdges());
    2755          274 :         if (from == nullptr) {
    2756            6 :             WRITE_WARNINGF(TL("from-edge '%' of restriction relation could not be determined"), toString(myFromWay));
    2757            3 :             return false;
    2758              :         }
    2759          271 :         if (to == nullptr) {
    2760            8 :             WRITE_WARNINGF(TL("to-edge '%' of restriction relation could not be determined"), toString(myToWay));
    2761            4 :             return false;
    2762              :         }
    2763          267 :         if (myRestrictionType == RestrictionType::ONLY) {
    2764          156 :             from->addEdge2EdgeConnection(to, true);
    2765              :             // make sure that these connections remain disabled even if network
    2766              :             // modifications (ramps.guess) reset existing connections
    2767          528 :             for (NBEdge* cand : from->getToNode()->getOutgoingEdges()) {
    2768          372 :                 if (!from->isConnectedTo(cand)) {
    2769          214 :                     if (myRestrictionException == SVC_IGNORING) {
    2770          201 :                         from->removeFromConnections(cand, -1, -1, true);
    2771              :                     } else {
    2772           13 :                         from->addEdge2EdgeConnection(cand, true, myRestrictionException);
    2773              :                     }
    2774              :                 }
    2775              :             }
    2776              :         } else {
    2777          111 :             if (myRestrictionException == SVC_IGNORING) {
    2778          107 :                 from->removeFromConnections(to, -1, -1, true);
    2779              :             } else {
    2780            4 :                 from->addEdge2EdgeConnection(to, true, myRestrictionException);
    2781           18 :                 for (NBEdge* cand : from->getToNode()->getOutgoingEdges()) {
    2782           14 :                     if (!from->isConnectedTo(cand)) {
    2783           10 :                         from->addEdge2EdgeConnection(cand, true);
    2784              :                     }
    2785              :                 }
    2786              :             }
    2787              :         }
    2788              :     } else {
    2789              :         // XXX interpreting via-ways or via-node lists not yet implemented
    2790           10 :         WRITE_WARNINGF(TL("direction of restriction relation could not be determined%"), "");
    2791           10 :         return false;
    2792              :     }
    2793              :     return true;
    2794              : }
    2795              : 
    2796              : NBEdge*
    2797          548 : NIImporter_OpenStreetMap::RelationHandler::findEdgeRef(long long int wayRef,
    2798              :         const std::vector<NBEdge*>& candidates) const {
    2799          548 :     const std::string prefix = toString(wayRef);
    2800          548 :     const std::string backPrefix = "-" + prefix;
    2801              :     NBEdge* result = nullptr;
    2802              :     int found = 0;
    2803         1979 :     for (auto candidate : candidates) {
    2804         2862 :         if ((candidate->getID().substr(0, prefix.size()) == prefix) ||
    2805         2430 :                 (candidate->getID().substr(0, backPrefix.size()) == backPrefix)) {
    2806              :             result = candidate;
    2807          540 :             found++;
    2808              :         }
    2809              :     }
    2810          548 :     if (found > 1) {
    2811            0 :         WRITE_WARNINGF(TL("Ambiguous way reference '%' in restriction relation"), prefix);
    2812              :         result = nullptr;
    2813              :     }
    2814          548 :     return result;
    2815              : }
    2816              : 
    2817              : 
    2818              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1