LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_OpenStreetMap.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.8 % 1547 1374
Test Date: 2024-12-21 15:45:41 Functions: 87.2 % 39 34

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

Generated by: LCOV version 2.0-1