LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_OpenStreetMap.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1374 1549 88.7 %
Date: 2024-05-02 15:31:40 Functions: 34 39 87.2 %

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

Generated by: LCOV version 1.14