LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_OpenStreetMap.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.2 % 1690 1491
Test Date: 2026-03-26 16:31:35 Functions: 87.8 % 41 36

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

Generated by: LCOV version 2.0-1