Path: blob/main/src/netimport/NIImporter_OpenStreetMap.cpp
169666 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file NIImporter_OpenStreetMap.cpp14/// @author Daniel Krajzewicz15/// @author Jakob Erdmann16/// @author Michael Behrisch17/// @author Walter Bamberger18/// @author Gregor Laemmel19/// @author Mirko Barthauer20/// @date Mon, 14.04.200821///22// Importer for networks stored in OpenStreetMap format23/****************************************************************************/24#include <config.h>25#include <algorithm>26#include <set>27#include <functional>28#include <sstream>29#include <limits>30#include <utils/common/UtilExceptions.h>31#include <utils/common/StringUtils.h>32#include <utils/common/ToString.h>33#include <utils/common/MsgHandler.h>34#include <utils/common/StringUtils.h>35#include <utils/common/StringTokenizer.h>36#include <utils/common/FileHelpers.h>37#include <utils/geom/GeoConvHelper.h>38#include <utils/geom/GeomConvHelper.h>39#include <utils/options/OptionsCont.h>40#include <utils/xml/SUMOSAXHandler.h>41#include <utils/xml/SUMOSAXReader.h>42#include <utils/xml/SUMOXMLDefinitions.h>43#include <utils/xml/XMLSubSys.h>44#include <netbuild/NBEdge.h>45#include <netbuild/NBEdgeCont.h>46#include <netbuild/NBNode.h>47#include <netbuild/NBNodeCont.h>48#include <netbuild/NBNetBuilder.h>49#include <netbuild/NBOwnTLDef.h>50#include <netbuild/NBPTLine.h>51#include <netbuild/NBPTLineCont.h>52#include <netbuild/NBPTPlatform.h>53#include <netbuild/NBPTStop.h>54#include "NILoader.h"55#include "NIImporter_OpenStreetMap.h"5657//#define DEBUG_LAYER_ELEVATION58//#define DEBUG_RAIL_DIRECTION5960// ---------------------------------------------------------------------------61// static members62// ---------------------------------------------------------------------------63const double NIImporter_OpenStreetMap::MAXSPEED_UNGIVEN = -1;6465const long long int NIImporter_OpenStreetMap::INVALID_ID = std::numeric_limits<long long int>::max();66bool NIImporter_OpenStreetMap::myAllAttributes(false);67std::set<std::string> NIImporter_OpenStreetMap::myExtraAttributes;6869// ===========================================================================70// Private classes71// ===========================================================================7273/** @brief Functor which compares two Edges74*/75class NIImporter_OpenStreetMap::CompareEdges {76public:77bool operator()(const Edge* e1, const Edge* e2) const {78if (e1->myHighWayType != e2->myHighWayType) {79return e1->myHighWayType > e2->myHighWayType;80}81if (e1->myNoLanes != e2->myNoLanes) {82return e1->myNoLanes > e2->myNoLanes;83}84if (e1->myNoLanesForward != e2->myNoLanesForward) {85return e1->myNoLanesForward > e2->myNoLanesForward;86}87if (e1->myMaxSpeed != e2->myMaxSpeed) {88return e1->myMaxSpeed > e2->myMaxSpeed;89}90if (e1->myIsOneWay != e2->myIsOneWay) {91return e1->myIsOneWay > e2->myIsOneWay;92}93return e1->myCurrentNodes > e2->myCurrentNodes;94}95};9697// ===========================================================================98// method definitions99// ===========================================================================100// ---------------------------------------------------------------------------101// static methods102// ---------------------------------------------------------------------------103const std::string NIImporter_OpenStreetMap::compoundTypeSeparator("|"); //clang-tidy says: "compundTypeSeparator with104// static storage duration my throw an exception that cannot be caught105106void107NIImporter_OpenStreetMap::loadNetwork(const OptionsCont& oc, NBNetBuilder& nb) {108NIImporter_OpenStreetMap importer;109importer.load(oc, nb);110}111112NIImporter_OpenStreetMap::NIImporter_OpenStreetMap() = default;113114NIImporter_OpenStreetMap::~NIImporter_OpenStreetMap() {115// delete nodes116for (auto myUniqueNode : myUniqueNodes) {117delete myUniqueNode;118}119// delete edges120for (auto& myEdge : myEdges) {121delete myEdge.second;122}123// delete platform shapes124for (auto& myPlatformShape : myPlatformShapes) {125delete myPlatformShape.second;126}127}128129void130NIImporter_OpenStreetMap::load(const OptionsCont& oc, NBNetBuilder& nb) {131if (!oc.isSet("osm-files")) {132return;133}134const std::vector<std::string> files = oc.getStringVector("osm-files");135std::vector<SUMOSAXReader*> readers;136137myImportLaneAccess = oc.getBool("osm.lane-access");138myImportTurnSigns = oc.getBool("osm.turn-lanes");139myImportSidewalks = oc.getBool("osm.sidewalks");140myImportBikeAccess = oc.getBool("osm.bike-access");141myImportCrossings = oc.getBool("osm.crossings");142myOnewayDualSidewalk = oc.getBool("osm.oneway-reverse-sidewalk");143myAnnotateDefaults = oc.getBool("osm.annotate-defaults");144145myAllAttributes = OptionsCont::getOptions().getBool("osm.all-attributes");146std::vector<std::string> extra = OptionsCont::getOptions().getStringVector("osm.extra-attributes");147myExtraAttributes.insert(extra.begin(), extra.end());148if (myExtraAttributes.count("all") != 0) {149// import all150myExtraAttributes.clear();151}152153// load nodes, first154NodesHandler nodesHandler(myOSMNodes, myUniqueNodes, oc);155for (const std::string& file : files) {156if (!FileHelpers::isReadable(file)) {157WRITE_ERRORF(TL("Could not open osm-file '%'."), file);158return;159}160nodesHandler.setFileName(file);161nodesHandler.resetHierarchy();162const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing nodes from osm-file '" + file + "'");163readers.push_back(XMLSubSys::getSAXReader(nodesHandler));164if (!readers.back()->parseFirst(file) || !readers.back()->parseSection(SUMO_TAG_NODE) ||165MsgHandler::getErrorInstance()->wasInformed()) {166return;167}168if (nodesHandler.getDuplicateNodes() > 0) {169WRITE_MESSAGEF(TL("Found and substituted % osm nodes."), toString(nodesHandler.getDuplicateNodes()));170}171PROGRESS_TIME_MESSAGE(before);172}173174// load edges, then175EdgesHandler edgesHandler(myOSMNodes, myEdges, myPlatformShapes, nb.getTypeCont());176int idx = 0;177for (const std::string& file : files) {178edgesHandler.setFileName(file);179readers[idx]->setHandler(edgesHandler);180const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing edges from osm-file '" + file + "'");181if (!readers[idx]->parseSection(SUMO_TAG_WAY)) {182// eof already reached, no relations183delete readers[idx];184readers[idx] = nullptr;185}186PROGRESS_TIME_MESSAGE(before);187idx++;188}189190/* Remove duplicate edges with the same shape and attributes */191if (!oc.getBool("osm.skip-duplicates-check")) {192int numRemoved = 0;193PROGRESS_BEGIN_MESSAGE(TL("Removing duplicate edges"));194if (myEdges.size() > 1) {195std::set<const Edge*, CompareEdges> dupsFinder;196for (auto it = myEdges.begin(); it != myEdges.end();) {197if (dupsFinder.count(it->second) > 0) {198numRemoved++;199delete it->second;200myEdges.erase(it++);201} else {202dupsFinder.insert(it->second);203it++;204}205}206}207if (numRemoved > 0) {208WRITE_MESSAGEF(TL("Removed % duplicate osm edges."), toString(numRemoved));209}210PROGRESS_DONE_MESSAGE();211}212213/* Mark which nodes are used (by edges or traffic lights).214* This is necessary to detect which OpenStreetMap nodes are for215* geometry only */216std::map<long long int, int> nodeUsage;217// Mark which nodes are used by edges (begin and end)218for (const auto& edgeIt : myEdges) {219assert(edgeIt.second->myCurrentIsRoad);220for (const long long int node : edgeIt.second->myCurrentNodes) {221nodeUsage[node]++;222}223}224// Mark which nodes are used by traffic lights or are pedestrian crossings225for (const auto& nodesIt : myOSMNodes) {226if (nodesIt.second->tlsControlled || nodesIt.second->railwaySignal || (nodesIt.second->pedestrianCrossing && myImportCrossings) /* || nodesIt->second->railwayCrossing*/) {227// If the key is not found in the map, the value is automatically228// initialized with 0.229nodeUsage[nodesIt.first]++;230}231}232233/* Instantiate edges234* Only those nodes in the middle of an edge which are used by more than235* one edge are instantiated. Other nodes are considered as geometry nodes. */236NBNodeCont& nc = nb.getNodeCont();237NBTrafficLightLogicCont& tlsc = nb.getTLLogicCont();238for (const auto& edgeIt : myEdges) {239Edge* const e = edgeIt.second;240if (!e->myCurrentIsRoad) {241continue;242}243if (e->myCurrentNodes.size() < 2) {244WRITE_WARNINGF(TL("Discarding way '%' because it has only % node(s)"), e->id, e->myCurrentNodes.size());245continue;246}247extendRailwayDistances(e, nb.getTypeCont());248// build nodes;249// - the from- and to-nodes must be built in any case250// - the in-between nodes are only built if more than one edge references them251NBNode* first = insertNodeChecking(e->myCurrentNodes.front(), nc, tlsc);252NBNode* last = insertNodeChecking(e->myCurrentNodes.back(), nc, tlsc);253NBNode* currentFrom = first;254int running = 0;255std::vector<long long int> passed;256for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {257passed.push_back(*j);258if (nodeUsage[*j] > 1 && j != e->myCurrentNodes.end() - 1 && j != e->myCurrentNodes.begin()) {259NBNode* currentTo = insertNodeChecking(*j, nc, tlsc);260running = insertEdge(e, running, currentFrom, currentTo, passed, nb, first, last);261currentFrom = currentTo;262passed.clear();263passed.push_back(*j);264}265}266if (running == 0) {267running = -1;268}269insertEdge(e, running, currentFrom, last, passed, nb, first, last);270}271272/* Collect edges which explicitly are part of a roundabout and store the edges of each273* detected roundabout */274nb.getEdgeCont().extractRoundabouts();275276if (myImportCrossings) {277/* After edges are instantiated278* nodes are parsed again to add pedestrian crossings to them279* This is only executed if crossings are imported and not guessed */280const double crossingWidth = OptionsCont::getOptions().getFloat("default.crossing-width");281282for (auto item : nodeUsage) {283NIOSMNode* osmNode = myOSMNodes.find(item.first)->second;284if (osmNode->pedestrianCrossing) {285NBNode* n = osmNode->node;286EdgeVector incomingEdges = n->getIncomingEdges();287EdgeVector outgoingEdges = n->getOutgoingEdges();288size_t incomingEdgesNo = incomingEdges.size();289size_t outgoingEdgesNo = outgoingEdges.size();290291for (size_t i = 0; i < incomingEdgesNo; i++) {292/* Check if incoming edge has driving lanes(and sidewalks)293* if not, ignore294* if yes, check if there is a corresponding outgoing edge for the opposite direction295* -> if yes, check if it has driving lanes296* --> if yes, do the crossing297* --> if no, only do the crossing with the incoming edge (usually one lane roads with two sidewalks)298* -> if not, do nothing as we don't have a sidewalk in the opposite direction */299auto const iEdge = incomingEdges[i];300301if (iEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1302&& iEdge->getSpecialLane(SVC_PEDESTRIAN) > -1) {303std::string const& iEdgeId = iEdge->getID();304std::size_t const m = iEdgeId.find_first_of("#");305std::string const& iWayId = iEdgeId.substr(0, m);306for (size_t j = 0; j < outgoingEdgesNo; j++) {307auto const oEdge = outgoingEdges[j];308// Searching for a corresponding outgoing edge (based on OSM way identifier)309// with at least a pedestrian lane, going in the opposite direction310if (oEdge->getID().find(iWayId) != std::string::npos311&& oEdge->getSpecialLane(SVC_PEDESTRIAN) > -1312&& oEdge->getID().rfind(iWayId, 0) != 0) {313EdgeVector edgeVector = EdgeVector{ iEdge };314if (oEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1) {315edgeVector.push_back(oEdge);316}317318if (!n->checkCrossingDuplicated(edgeVector)) {319n->addCrossing(edgeVector, crossingWidth, false);320}321}322}323}324}325for (size_t i = 0; i < outgoingEdgesNo; i++) {326// Same checks as above for loop, but for outgoing edges327auto const oEdge = outgoingEdges[i];328329if (oEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1330&& oEdge->getSpecialLane(SVC_PEDESTRIAN) > -1) {331std::string const& oEdgeId = oEdge->getID();332std::size_t const m = oEdgeId.find_first_of("#");333std::string const& iWayId = oEdgeId.substr(0, m);334for (size_t j = 0; j < incomingEdgesNo; j++) {335auto const iEdge = incomingEdges[j];336if (iEdge->getID().find(iWayId) != std::string::npos337&& iEdge->getSpecialLane(SVC_PEDESTRIAN) > -1338&& iEdge->getID().rfind(iWayId, 0) != 0) {339EdgeVector edgeVector = EdgeVector{ oEdge };340if (iEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1) {341edgeVector.push_back(iEdge);342}343344if (!n->checkCrossingDuplicated(edgeVector)) {345n->addCrossing(edgeVector, crossingWidth, false);346}347}348}349}350}351}352}353}354355const double layerElevation = oc.getFloat("osm.layer-elevation");356if (layerElevation > 0) {357reconstructLayerElevation(layerElevation, nb);358}359360// revise pt stops; remove stops on deleted edges361nb.getPTStopCont().cleanupDeleted(nb.getEdgeCont());362363// load relations (after edges are built since we want to apply364// turn-restrictions directly to NBEdges)365RelationHandler relationHandler(myOSMNodes, myEdges, &(nb.getPTStopCont()), myPlatformShapes,366&nb.getPTLineCont(), oc);367idx = 0;368for (const std::string& file : files) {369if (readers[idx] != nullptr) {370relationHandler.setFileName(file);371readers[idx]->setHandler(relationHandler);372const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing relations from osm-file '" + file + "'");373readers[idx]->parseSection(SUMO_TAG_RELATION);374PROGRESS_TIME_MESSAGE(before);375delete readers[idx];376}377idx++;378}379380// declare additional stops that are not anchored to a (road)-way or route relation381std::set<std::string> stopNames;382for (const auto& item : nb.getPTStopCont().getStops()) {383stopNames.insert(item.second->getName());384}385for (const auto& item : myOSMNodes) {386const NIOSMNode* n = item.second;387if (n->ptStopPosition && stopNames.count(n->name) == 0) {388Position ptPos(n->lon, n->lat, n->ele);389if (!NBNetBuilder::transformCoordinate(ptPos)) {390WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);391}392std::shared_ptr<NBPTStop> ptStop = std::make_shared<NBPTStop>(toString(n->id), ptPos, "", "", n->ptStopLength, n->name, n->permissions);393nb.getPTStopCont().insert(ptStop, true);394}395}396}397398// ---------------------------------------------------------------------------399// definitions of NIImporter_OpenStreetMap-methods400// ---------------------------------------------------------------------------401402NBNode*403NIImporter_OpenStreetMap::insertNodeChecking(long long int id, NBNodeCont& nc, NBTrafficLightLogicCont& tlsc) {404NBNode* node = nc.retrieve(toString(id));405if (node == nullptr) {406NIOSMNode* n = myOSMNodes.find(id)->second;407Position pos(n->lon, n->lat, n->ele);408if (!NBNetBuilder::transformCoordinate(pos, true)) {409WRITE_ERRORF("Unable to project coordinates for junction '%'.", id);410return nullptr;411}412node = new NBNode(toString(id), pos);413if (!nc.insert(node)) {414WRITE_ERRORF(TL("Could not insert junction '%'."), toString(id));415delete node;416return nullptr;417}418n->node = node;419if (n->railwayCrossing) {420if (n->getParameter("crossing:barrier") != "no") {421node->reinit(pos, SumoXMLNodeType::RAIL_CROSSING);422} else if (n->getParameter("crossing.light") == "yes") {423node->reinit(pos, SumoXMLNodeType::TRAFFIC_LIGHT);424}425} else if (n->railwaySignal) {426node->reinit(pos, SumoXMLNodeType::RAIL_SIGNAL);427} else if (n->tlsControlled) {428// ok, this node is a traffic light node where no other nodes429// participate430// @note: The OSM-community has not settled on a schema for differentiating between fixed and actuated lights431TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(432OptionsCont::getOptions().getString("tls.default-type"));433NBOwnTLDef* tlDef = new NBOwnTLDef(toString(id), node, 0, type);434if (!tlsc.insert(tlDef)) {435// actually, nothing should fail here436delete tlDef;437throw ProcessError(TLF("Could not allocate tls '%'.", toString(id)));438}439}440if (n->railwayBufferStop) {441node->setParameter("buffer_stop", "true");442node->setFringeType(FringeType::INNER);443}444if (n->railwaySignal) {445if (n->myRailDirection == WAY_FORWARD) {446node->setParameter(NBTrafficLightDefinition::OSM_SIGNAL_DIRECTION, "forward");447} else if (n->myRailDirection == WAY_BACKWARD) {448node->setParameter(NBTrafficLightDefinition::OSM_SIGNAL_DIRECTION, "backward");449}450}451node->updateParameters(n->getParametersMap());452}453return node;454}455456457int458NIImporter_OpenStreetMap::insertEdge(Edge* e, int index, NBNode* from, NBNode* to,459const std::vector<long long int>& passed, NBNetBuilder& nb,460const NBNode* first, const NBNode* last) {461NBNodeCont& nc = nb.getNodeCont();462NBEdgeCont& ec = nb.getEdgeCont();463NBTypeCont& tc = nb.getTypeCont();464NBPTStopCont& sc = nb.getPTStopCont();465466NBTrafficLightLogicCont& tlsc = nb.getTLLogicCont();467// patch the id468std::string id = toString(e->id);469if (from == nullptr || to == nullptr) {470WRITE_ERRORF("Discarding edge '%' because the nodes could not be built.", id);471return index;472}473if (index >= 0) {474id = id + "#" + toString(index);475} else {476index = 0;477}478if (from == to) {479assert(passed.size() >= 2);480if (passed.size() == 2) {481WRITE_WARNINGF(TL("Discarding edge '%' which connects two identical nodes without geometry."), id);482return index;483}484// in the special case of a looped way split again using passed485int intermediateIndex = (int) passed.size() / 2;486NBNode* intermediate = insertNodeChecking(passed[intermediateIndex], nc, tlsc);487std::vector<long long int> part1(passed.begin(), passed.begin() + intermediateIndex + 1);488std::vector<long long int> part2(passed.begin() + intermediateIndex, passed.end());489index = insertEdge(e, index, from, intermediate, part1, nb, first, last);490return insertEdge(e, index, intermediate, to, part2, nb, first, last);491}492const int newIndex = index + 1;493const std::string type = usableType(e->myHighWayType, id, tc);494if (type == "") { // we do not want to import it495return newIndex;496}497498int numLanesForward = tc.getEdgeTypeNumLanes(type);499int numLanesBackward = tc.getEdgeTypeNumLanes(type);500double speed = tc.getEdgeTypeSpeed(type);501bool defaultsToOneWay = tc.getEdgeTypeIsOneWay(type);502const SVCPermissions defaultPermissions = tc.getEdgeTypePermissions(type);503SVCPermissions extra = myImportBikeAccess ? e->myExtraAllowed : (e->myExtraAllowed & ~SVC_BICYCLE);504const SVCPermissions extraDis = myImportBikeAccess ? e->myExtraDisallowed : (e->myExtraDisallowed & ~SVC_BICYCLE);505std::vector<SumoXMLAttr> defaults;506// extra permissions are more specific than extra prohibitions except for buses (which come from the less specific psv tag)507if ((extraDis & SVC_BUS) && (extra & SVC_BUS)) {508extra = extra & ~SVC_BUS;509}510SVCPermissions permissions = (defaultPermissions & ~extraDis) | extra;511if (defaultPermissions == SVC_SHIP) {512// extra permission apply to the ships operating on the route rather than the waterway513permissions = defaultPermissions;514}515if (defaultsToOneWay && defaultPermissions == SVC_PEDESTRIAN && (permissions & (~SVC_PEDESTRIAN)) != 0) {516defaultsToOneWay = false;517}518if ((permissions & SVC_RAIL) != 0 && e->myExtraTags.count("electrified") != 0) {519permissions |= (SVC_RAIL_ELECTRIC | SVC_RAIL_FAST);520}521522// convert the shape523PositionVector shape;524double distanceStart = myOSMNodes[passed.front()]->positionMeters;525double distanceEnd = myOSMNodes[passed.back()]->positionMeters;526const bool useDistance = distanceStart != std::numeric_limits<double>::max() && distanceEnd != std::numeric_limits<double>::max();527if (useDistance) {528// negative sign denotes counting in the other direction529if (distanceStart < distanceEnd) {530distanceStart *= -1;531} else {532distanceEnd *= -1;533}534} else {535distanceStart = 0;536distanceEnd = 0;537}538// get additional direction information539int nodeDirection = myOSMNodes.find(StringUtils::toLong(from->getID()))->second->myRailDirection |540myOSMNodes.find(StringUtils::toLong(to->getID()))->second->myRailDirection;541542std::vector<std::shared_ptr<NBPTStop> > ptStops;543for (long long i : passed) {544NIOSMNode* n = myOSMNodes.find(i)->second;545// recheck permissions, maybe they got assigned to a strange edge, see #11656546if (n->ptStopPosition && (n->permissions == 0 || (permissions & n->permissions) != 0)) {547std::shared_ptr<NBPTStop> existingPtStop = sc.get(toString(n->id));548if (existingPtStop != nullptr) {549existingPtStop->registerAdditionalEdge(toString(e->id), id);550} else {551Position ptPos(n->lon, n->lat, n->ele);552if (!NBNetBuilder::transformCoordinate(ptPos)) {553WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);554}555ptStops.push_back(std::make_shared<NBPTStop>(toString(n->id), ptPos, id, toString(e->id), n->ptStopLength, n->name, n->permissions));556sc.insert(ptStops.back());557}558}559if (n->railwaySignal) {560nodeDirection |= n->myRailDirection;561}562Position pos(n->lon, n->lat, n->ele);563shape.push_back(pos);564}565#ifdef DEBUG_LAYER_ELEVATION566if (e->id == "DEBUGID") {567std::cout568<< " id=" << id << " from=" << from->getID() << " fromRailDirection=" << myOSMNodes.find(StringUtils::toLong(from->getID()))->second->myRailDirection569<< " to=" << to->getID() << " toRailDirection=" << myOSMNodes.find(StringUtils::toLong(to->getID()))->second->myRailDirection570<< " origRailDirection=" << e->myRailDirection571<< " nodeDirection=" << nodeDirection572<< "\n";573}574#endif575if (e->myRailDirection == WAY_UNKNOWN && nodeDirection != WAY_UNKNOWN && nodeDirection != WAY_FORWARD576&& nodeDirection != (WAY_FORWARD | WAY_UNKNOWN)) {577//std::cout << "way " << e->id << " nodeDirection=" << nodeDirection << " origDirection=" << e->myRailDirection << "\n";578// heuristic: assume that the mapped way direction indicates579// potential driving direction580e->myRailDirection = WAY_BOTH;581}582if (!NBNetBuilder::transformCoordinates(shape)) {583WRITE_ERRORF("Unable to project coordinates for edge '%'.", id);584}585586SVCPermissions forwardPermissions = permissions;587SVCPermissions backwardPermissions = permissions;588const std::string streetName = isRailway(permissions) && e->ref != "" ? e->ref : e->streetName;589if (streetName == e->ref) {590e->unsetParameter("ref"); // avoid superfluous param for railways591}592double forwardWidth = tc.getEdgeTypeWidth(type);593double backwardWidth = tc.getEdgeTypeWidth(type);594double sidewalkWidth = tc.getEdgeTypeSidewalkWidth(type);595bool addSidewalk = sidewalkWidth != NBEdge::UNSPECIFIED_WIDTH;596if (myImportSidewalks) {597if (addSidewalk) {598// only use sidewalk width from typemap but don't add sidewalks599// unless OSM specifies them600addSidewalk = false;601} else {602sidewalkWidth = OptionsCont::getOptions().getFloat("default.sidewalk-width");603}604}605double bikeLaneWidth = tc.getEdgeTypeBikeLaneWidth(type);606const std::string& onewayBike = e->myExtraTags["oneway:bicycle"];607if (onewayBike == "false" || onewayBike == "no" || onewayBike == "0") {608e->myCyclewayType = e->myCyclewayType == WAY_UNKNOWN ? WAY_BACKWARD : (WayType)(e->myCyclewayType | WAY_BACKWARD);609}610611const bool addBikeLane = bikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH ||612(myImportBikeAccess && (((e->myCyclewayType & WAY_BOTH) != 0 || e->myExtraTags.count("segregated") != 0) &&613!(e->myCyclewayType == WAY_BACKWARD && (e->myBuswayType & WAY_BOTH) != 0)));614if (addBikeLane && bikeLaneWidth == NBEdge::UNSPECIFIED_WIDTH) {615bikeLaneWidth = OptionsCont::getOptions().getFloat("default.bikelane-width");616}617// check directions618bool addForward = true;619bool addBackward = true;620const bool explicitTwoWay = e->myIsOneWay == "no";621if ((e->myIsOneWay == "true" || e->myIsOneWay == "yes" || e->myIsOneWay == "1"622|| (defaultsToOneWay && e->myIsOneWay != "no" && e->myIsOneWay != "false" && e->myIsOneWay != "0"))623&& e->myRailDirection != WAY_BOTH) {624addBackward = false;625}626if (e->myIsOneWay == "-1" || e->myIsOneWay == "reverse" || e->myRailDirection == WAY_BACKWARD) {627// one-way in reversed direction of way628addForward = false;629addBackward = true;630}631if (!e->myIsOneWay.empty() && e->myIsOneWay != "false" && e->myIsOneWay != "no" && e->myIsOneWay != "true"632&& e->myIsOneWay != "yes" && e->myIsOneWay != "-1" && e->myIsOneWay != "1" && e->myIsOneWay != "reverse") {633WRITE_WARNINGF(TL("New value for oneway found: %"), e->myIsOneWay);634}635if ((permissions == SVC_BICYCLE || permissions == (SVC_BICYCLE | SVC_PEDESTRIAN) || permissions == SVC_PEDESTRIAN)) {636if (addBackward && (onewayBike == "true" || onewayBike == "yes" || onewayBike == "1")) {637addBackward = false;638}639if (addForward && (onewayBike == "reverse" || onewayBike == "-1")) {640addForward = false;641}642if (!addBackward && (onewayBike == "false" || onewayBike == "no" || onewayBike == "0")) {643addBackward = true;644}645}646bool ok = true;647// if we had been able to extract the number of lanes, override the highway type default648if (e->myNoLanes > 0) {649if (addForward && !addBackward) {650numLanesForward = e->myNoLanesForward > 0 ? e->myNoLanesForward : e->myNoLanes;651} else if (!addForward && addBackward) {652numLanesBackward = e->myNoLanesForward < 0 ? -e->myNoLanesForward : e->myNoLanes;653} else {654if (e->myNoLanesForward > 0) {655numLanesForward = e->myNoLanesForward;656} else if (e->myNoLanesForward < 0) {657numLanesForward = e->myNoLanes + e->myNoLanesForward;658} else {659numLanesForward = (int) std::ceil(e->myNoLanes / 2.0);660}661numLanesBackward = e->myNoLanes - numLanesForward;662// sometimes ways are tagged according to their physical width of a single663// lane but they are intended for traffic in both directions664numLanesForward = MAX2(1, numLanesForward);665numLanesBackward = MAX2(1, numLanesBackward);666}667} else if (e->myNoLanes == 0) {668WRITE_WARNINGF(TL("Skipping edge '%' because it has zero lanes."), id);669ok = false;670} else {671// the total number of lanes is not known but at least one direction672if (e->myNoLanesForward > 0) {673numLanesForward = e->myNoLanesForward;674} else if ((e->myBuswayType & WAY_FORWARD) != 0 && (extraDis & SVC_PASSENGER) == 0) {675// if we have a busway lane, yet cars may drive this implies at least two lanes676numLanesForward = MAX2(numLanesForward, 2);677}678if (e->myNoLanesForward < 0) {679numLanesBackward = -e->myNoLanesForward;680} else if ((e->myBuswayType & WAY_BACKWARD) != 0 && (extraDis & SVC_PASSENGER) == 0) {681// if we have a busway lane, yet cars may drive this implies at least two lanes682numLanesBackward = MAX2(numLanesForward, 2);683}684if (myAnnotateDefaults && e->myNoLanesForward == 0) {685defaults.push_back(SUMO_ATTR_NUMLANES);686}687}688// deal with busways that run in the opposite direction of a one-way street689if (!addForward && (e->myBuswayType & WAY_FORWARD) != 0) {690addForward = true;691forwardPermissions = SVC_BUS;692numLanesForward = 1;693}694if (!addBackward && (e->myBuswayType & WAY_BACKWARD) != 0) {695addBackward = true;696backwardPermissions = SVC_BUS;697numLanesBackward = 1;698}699// with is meant for raw lane count before adding sidewalks or cycleways700const int taggedLanes = (addForward ? numLanesForward : 0) + (addBackward ? numLanesBackward : 0);701if (e->myWidth > 0 && e->myWidthLanesForward.size() == 0 && e->myWidthLanesBackward.size() == 0 && taggedLanes != 0702&& !OptionsCont::getOptions().getBool("ignore-widths")) {703// width is tagged excluding sidewalks and cycleways704forwardWidth = e->myWidth / taggedLanes;705backwardWidth = forwardWidth;706}707708// if we had been able to extract the maximum speed, override the type's default709if (e->myMaxSpeed != MAXSPEED_UNGIVEN) {710speed = e->myMaxSpeed;711} else if (myAnnotateDefaults) {712defaults.push_back(SUMO_ATTR_SPEED);713}714double speedBackward = speed;715if (e->myMaxSpeedBackward != MAXSPEED_UNGIVEN) {716speedBackward = e->myMaxSpeedBackward;717}718if (speed <= 0 || speedBackward <= 0) {719WRITE_WARNINGF(TL("Skipping edge '%' because it has speed %."), id, speed);720ok = false;721}722// deal with cycleways that run in the opposite direction of a one-way street723WayType cyclewayType = e->myCyclewayType; // make a copy because we do some temporary modifications724if (addBikeLane) {725if (!addForward && (cyclewayType & WAY_FORWARD) != 0) {726addForward = true;727forwardPermissions = SVC_BICYCLE;728forwardWidth = bikeLaneWidth;729numLanesForward = 1;730// do not add an additional cycle lane731cyclewayType = (WayType)(cyclewayType & ~WAY_FORWARD);732}733if (!addBackward && (cyclewayType & WAY_BACKWARD) != 0) {734addBackward = true;735backwardPermissions = SVC_BICYCLE;736backwardWidth = bikeLaneWidth;737numLanesBackward = 1;738// do not add an additional cycle lane739cyclewayType = (WayType)(cyclewayType & ~WAY_BACKWARD);740}741}742// deal with sidewalks that run in the opposite direction of a one-way street743WayType sidewalkType = e->mySidewalkType; // make a copy because we do some temporary modifications744if (sidewalkType == WAY_UNKNOWN && (e->myExtraAllowed & SVC_PEDESTRIAN) != 0 && (permissions & SVC_PASSENGER) != 0) {745// do not assume shared space unless sidewalk is actively disabled746if (myOnewayDualSidewalk) {747sidewalkType = WAY_BOTH;748}749}750if (addSidewalk || (myImportSidewalks && (permissions & SVC_ROAD_CLASSES) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {751if (!addForward && (sidewalkType & WAY_FORWARD) != 0) {752addForward = true;753forwardPermissions = SVC_PEDESTRIAN;754forwardWidth = tc.getEdgeTypeSidewalkWidth(type);755numLanesForward = 1;756// do not add an additional sidewalk757sidewalkType = (WayType)(sidewalkType & ~WAY_FORWARD); //clang tidy thinks "!WAY_FORWARD" is always false758} else if (addSidewalk && addForward && (sidewalkType & WAY_BOTH) == 0759&& numLanesForward == 1 && numLanesBackward <= 1760&& (e->myExtraDisallowed & SVC_PEDESTRIAN) == 0) {761// our typemap says pedestrians should walk here but the data says762// there is no sidewalk at all. If the road is small, pedestrians can just walk763// on the road764forwardPermissions |= SVC_PEDESTRIAN;765}766if (!addBackward && (sidewalkType & WAY_BACKWARD) != 0) {767addBackward = true;768backwardPermissions = SVC_PEDESTRIAN;769backwardWidth = tc.getEdgeTypeSidewalkWidth(type);770numLanesBackward = 1;771// do not add an additional cycle lane772sidewalkType = (WayType)(sidewalkType & ~WAY_BACKWARD); //clang tidy thinks "!WAY_BACKWARD" is always false773} else if (addSidewalk && addBackward && (sidewalkType & WAY_BOTH) == 0774&& numLanesBackward == 1 && numLanesForward <= 1775&& (e->myExtraDisallowed & SVC_PEDESTRIAN) == 0) {776// our typemap says pedestrians should walk here but the data says777// there is no sidewalk at all. If the road is small, pedestrians can just walk778// on the road779backwardPermissions |= SVC_PEDESTRIAN;780}781}782783const std::string origID = OptionsCont::getOptions().getBool("output.original-names") ? toString(e->id) : "";784if (ok) {785const bool lefthand = OptionsCont::getOptions().getBool("lefthand");786const int offsetFactor = lefthand ? -1 : 1;787LaneSpreadFunction lsf = (addBackward || OptionsCont::getOptions().getBool("osm.oneway-spread-right")) &&788(e->myRailDirection == WAY_UNKNOWN || explicitTwoWay) ? LaneSpreadFunction::RIGHT : LaneSpreadFunction::CENTER;789if (addBackward && lsf == LaneSpreadFunction::RIGHT && OptionsCont::getOptions().getString("default.spreadtype") == toString(LaneSpreadFunction::ROADCENTER)) {790lsf = LaneSpreadFunction::ROADCENTER;791}792if (tc.getEdgeTypeSpreadType(type) != LaneSpreadFunction::RIGHT) {793// user defined value overrides defaults794lsf = tc.getEdgeTypeSpreadType(type);795}796if (defaults.size() > 0) {797e->setParameter("osmDefaults", joinToString(defaults, " "));798}799800id = StringUtils::escapeXML(id);801const std::string reverseID = "-" + id;802const bool markOSMDirection = from->getType() == SumoXMLNodeType::RAIL_SIGNAL || to->getType() == SumoXMLNodeType::RAIL_SIGNAL;803if (addForward) {804assert(numLanesForward > 0);805NBEdge* nbe = new NBEdge(id, from, to, type, speed, NBEdge::UNSPECIFIED_FRICTION, numLanesForward, tc.getEdgeTypePriority(type),806forwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape, lsf,807StringUtils::escapeXML(streetName), origID, true);808if (markOSMDirection) {809nbe->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "forward");810}811nbe->setPermissions(forwardPermissions, -1);812if ((e->myBuswayType & WAY_FORWARD) != 0) {813nbe->setPermissions(SVC_BUS, 0);814}815applyChangeProhibition(nbe, e->myChangeForward);816applyLaneUse(nbe, e, true);817applyTurnSigns(nbe, e->myTurnSignsForward);818nbe->setTurnSignTarget(last->getID());819if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_FORWARD) != 0)) {820nbe->addBikeLane(bikeLaneWidth * offsetFactor);821} else if (nbe->getPermissions(0) == SVC_BUS) {822// bikes drive on buslanes if no separate cycle lane is available823nbe->setPermissions(SVC_BUS | SVC_BICYCLE, 0);824}825if ((addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_FORWARD) != 0))826|| (myImportSidewalks && (sidewalkType & WAY_FORWARD) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {827nbe->addSidewalk(sidewalkWidth * offsetFactor);828}829if (!addBackward && (e->myExtraAllowed & SVC_PEDESTRIAN) != 0 && (nbe->getPermissions(0) & SVC_PEDESTRIAN) == 0) {830// Pedestrians are explicitly allowed (maybe through foot="yes") but did not get a sidewalk (maybe through sidewalk="no").831// Since we do not have a backward edge, we need to make sure they can at least walk somewhere, see #14124832nbe->setPermissions(nbe->getPermissions(0) | SVC_PEDESTRIAN, 0);833}834nbe->updateParameters(e->getParametersMap());835nbe->setDistance(distanceStart);836if (e->myAmInRoundabout) {837// ensure roundabout edges have the precedence838nbe->setJunctionPriority(to, NBEdge::JunctionPriority::ROUNDABOUT);839nbe->setJunctionPriority(from, NBEdge::JunctionPriority::ROUNDABOUT);840}841842// process forward lanes width843const int numForwardLanesFromWidthKey = (int)e->myWidthLanesForward.size();844if (numForwardLanesFromWidthKey > 0 && !OptionsCont::getOptions().getBool("ignore-widths")) {845if ((int)nbe->getLanes().size() != numForwardLanesFromWidthKey) {846WRITE_WARNINGF(TL("Forward lanes count for edge '%' ('%') is not matching the number of lanes defined in width:lanes:forward key ('%'). Using default width values."),847id, nbe->getLanes().size(), numForwardLanesFromWidthKey);848} else {849for (int i = 0; i < numForwardLanesFromWidthKey; i++) {850const double actualWidth = e->myWidthLanesForward[i] <= 0 ? forwardWidth : e->myWidthLanesForward[i];851const int laneIndex = lefthand ? i : numForwardLanesFromWidthKey - i - 1;852nbe->setLaneWidth(laneIndex, actualWidth);853}854}855}856857if (!ec.insert(nbe)) {858delete nbe;859throw ProcessError(TLF("Could not add edge '%'.", id));860}861}862if (addBackward) {863assert(numLanesBackward > 0);864NBEdge* nbe = new NBEdge(reverseID, to, from, type, speedBackward, NBEdge::UNSPECIFIED_FRICTION, numLanesBackward, tc.getEdgeTypePriority(type),865backwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape.reverse(), lsf,866StringUtils::escapeXML(streetName), origID, true);867if (markOSMDirection) {868nbe->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");869}870nbe->setPermissions(backwardPermissions);871if ((e->myBuswayType & WAY_BACKWARD) != 0) {872nbe->setPermissions(SVC_BUS, 0);873}874applyChangeProhibition(nbe, e->myChangeBackward);875applyLaneUse(nbe, e, false);876applyTurnSigns(nbe, e->myTurnSignsBackward);877nbe->setTurnSignTarget(first->getID());878if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_BACKWARD) != 0)) {879nbe->addBikeLane(bikeLaneWidth * offsetFactor);880} else if (nbe->getPermissions(0) == SVC_BUS) {881// bikes drive on buslanes if no separate cycle lane is available882nbe->setPermissions(SVC_BUS | SVC_BICYCLE, 0);883}884if ((addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_BACKWARD) != 0))885|| (myImportSidewalks && (sidewalkType & WAY_BACKWARD) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {886nbe->addSidewalk(sidewalkWidth * offsetFactor);887}888nbe->updateParameters(e->getParametersMap());889nbe->setDistance(distanceEnd);890if (e->myAmInRoundabout) {891// ensure roundabout edges have the precedence892nbe->setJunctionPriority(from, NBEdge::JunctionPriority::ROUNDABOUT);893nbe->setJunctionPriority(to, NBEdge::JunctionPriority::ROUNDABOUT);894}895// process backward lanes width896const int numBackwardLanesFromWidthKey = (int)e->myWidthLanesBackward.size();897if (numBackwardLanesFromWidthKey > 0 && !OptionsCont::getOptions().getBool("ignore-widths")) {898if ((int)nbe->getLanes().size() != numBackwardLanesFromWidthKey) {899WRITE_WARNINGF(TL("Backward lanes count for edge '%' ('%') is not matching the number of lanes defined in width:lanes:backward key ('%'). Using default width values."),900id, nbe->getLanes().size(), numBackwardLanesFromWidthKey);901} else {902for (int i = 0; i < numBackwardLanesFromWidthKey; i++) {903const double actualWidth = e->myWidthLanesBackward[i] <= 0 ? backwardWidth : e->myWidthLanesBackward[i];904const int laneIndex = lefthand ? i : numBackwardLanesFromWidthKey - i - 1;905nbe->setLaneWidth(laneIndex, actualWidth);906}907}908}909910if (!ec.insert(nbe)) {911delete nbe;912throw ProcessError(TLF("Could not add edge '-%'.", id));913}914}915if ((e->myParkingType & PARKING_BOTH) != 0 && OptionsCont::getOptions().isSet("parking-output")) {916if ((e->myParkingType & PARKING_RIGHT) != 0) {917if (addForward) {918nb.getParkingCont().push_back(NBParking(id, id));919} else {920/// XXX parking area should be added on the left side of a reverse one-way street921if ((e->myParkingType & PARKING_LEFT) == 0 && !addBackward) {922/// put it on the wrong side (better than nothing)923nb.getParkingCont().push_back(NBParking(reverseID, reverseID));924}925}926}927if ((e->myParkingType & PARKING_LEFT) != 0) {928if (addBackward) {929nb.getParkingCont().push_back(NBParking(reverseID, reverseID));930} else {931/// XXX parking area should be added on the left side of an one-way street932if ((e->myParkingType & PARKING_RIGHT) == 0 && !addForward) {933/// put it on the wrong side (better than nothing)934nb.getParkingCont().push_back(NBParking(id, id));935}936}937}938}939}940return newIndex;941}942943944void945NIImporter_OpenStreetMap::reconstructLayerElevation(const double layerElevation, NBNetBuilder& nb) {946NBNodeCont& nc = nb.getNodeCont();947NBEdgeCont& ec = nb.getEdgeCont();948// reconstruct elevation from layer info949// build a map of raising and lowering forces (attractor and distance)950// for all nodes unknownElevation951std::map<NBNode*, std::vector<std::pair<double, double> > > layerForces;952953// collect all nodes that belong to a way with layer information954std::set<NBNode*> knownElevation;955for (auto& myEdge : myEdges) {956Edge* e = myEdge.second;957if (e->myLayer != 0) {958for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {959NBNode* node = nc.retrieve(toString(*j));960if (node != nullptr) {961knownElevation.insert(node);962layerForces[node].emplace_back(e->myLayer * layerElevation, POSITION_EPS);963}964}965}966}967#ifdef DEBUG_LAYER_ELEVATION968std::cout << "known elevations:\n";969for (std::set<NBNode*>::iterator it = knownElevation.begin(); it != knownElevation.end(); ++it) {970const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];971std::cout << " node=" << (*it)->getID() << " ele=";972for (std::vector<std::pair<double, double> >::const_iterator it_ele = primaryLayers.begin(); it_ele != primaryLayers.end(); ++it_ele) {973std::cout << it_ele->first << " ";974}975std::cout << "\n";976}977#endif978// layer data only provides a lower bound on elevation since it is used to979// resolve the relation among overlapping ways.980// Perform a sanity check for steep inclines and raise the knownElevation if necessary981std::map<NBNode*, double> knownEleMax;982for (auto it : knownElevation) {983double eleMax = -std::numeric_limits<double>::max();984const std::vector<std::pair<double, double> >& primaryLayers = layerForces[it];985for (const auto& primaryLayer : primaryLayers) {986eleMax = MAX2(eleMax, primaryLayer.first);987}988knownEleMax[it] = eleMax;989}990const double gradeThreshold = OptionsCont::getOptions().getFloat("osm.layer-elevation.max-grade") / 100;991bool changed = true;992while (changed) {993changed = false;994for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {995std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it,996knownEleMax[*it]997/ gradeThreshold * 3,998knownElevation);999for (auto& neighbor : neighbors) {1000if (knownElevation.count(neighbor.first) != 0) {1001const double grade = fabs(knownEleMax[*it] - knownEleMax[neighbor.first])1002/ MAX2(POSITION_EPS, neighbor.second.first);1003#ifdef DEBUG_LAYER_ELEVATION1004std::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";1005#endif1006if (grade > gradeThreshold * 50 / 3.6 / neighbor.second.second) {1007// raise the lower node to the higher level1008const double eleMax = MAX2(knownEleMax[*it], knownEleMax[neighbor.first]);1009if (knownEleMax[*it] < eleMax) {1010knownEleMax[*it] = eleMax;1011} else {1012knownEleMax[neighbor.first] = eleMax;1013}1014changed = true;1015}1016}1017}1018}1019}10201021// collect all nodes within a grade-dependent range around knownElevation-nodes and apply knowElevation forces1022std::set<NBNode*> unknownElevation;1023for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {1024const double eleMax = knownEleMax[*it];1025const double maxDist = fabs(eleMax) * 100 / layerElevation;1026std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);1027for (auto& neighbor : neighbors) {1028if (knownElevation.count(neighbor.first) == 0) {1029unknownElevation.insert(neighbor.first);1030layerForces[neighbor.first].emplace_back(eleMax, neighbor.second.first);1031}1032}1033}10341035// apply forces to ground-level nodes (neither in knownElevation nor unknownElevation)1036for (auto it = unknownElevation.begin(); it != unknownElevation.end(); ++it) {1037double eleMax = -std::numeric_limits<double>::max();1038const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];1039for (const auto& primaryLayer : primaryLayers) {1040eleMax = MAX2(eleMax, primaryLayer.first);1041}1042const double maxDist = fabs(eleMax) * 100 / layerElevation;1043std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);1044for (auto& neighbor : neighbors) {1045if (knownElevation.count(neighbor.first) == 0 && unknownElevation.count(neighbor.first) == 0) {1046layerForces[*it].emplace_back(0, neighbor.second.first);1047}1048}1049}1050// compute the elevation for each node as the weighted average of all forces1051#ifdef DEBUG_LAYER_ELEVATION1052std::cout << "summation of forces\n";1053#endif1054std::map<NBNode*, double> nodeElevation;1055for (auto& layerForce : layerForces) {1056const std::vector<std::pair<double, double> >& forces = layerForce.second;1057if (knownElevation.count(layerForce.first) != 0) {1058// use the maximum value1059/*1060double eleMax = -std::numeric_limits<double>::max();1061for (std::vector<std::pair<double, double> >::const_iterator it_force = forces.begin(); it_force != forces.end(); ++it_force) {1062eleMax = MAX2(eleMax, it_force->first);1063}1064*/1065#ifdef DEBUG_LAYER_ELEVATION1066std::cout << " node=" << it->first->getID() << " knownElevation=" << knownEleMax[it->first] << "\n";1067#endif1068nodeElevation[layerForce.first] = knownEleMax[layerForce.first];1069} else if (forces.size() == 1) {1070nodeElevation[layerForce.first] = forces.front().first;1071} else {1072// use the weighted sum1073double distSum = 0;1074for (const auto& force : forces) {1075distSum += force.second;1076}1077double weightSum = 0;1078double elevation = 0;1079#ifdef DEBUG_LAYER_ELEVATION1080std::cout << " node=" << it->first->getID() << " distSum=" << distSum << "\n";1081#endif1082for (const auto& force : forces) {1083const double weight = (distSum - force.second) / distSum;1084weightSum += weight;1085elevation += force.first * weight;10861087#ifdef DEBUG_LAYER_ELEVATION1088std::cout << " force=" << it_force->first << " dist=" << it_force->second << " weight=" << weight << " ele=" << elevation << "\n";1089#endif1090}1091nodeElevation[layerForce.first] = elevation / weightSum;1092}1093}1094#ifdef DEBUG_LAYER_ELEVATION1095std::cout << "final elevations:\n";1096for (std::map<NBNode*, double>::iterator it = nodeElevation.begin(); it != nodeElevation.end(); ++it) {1097std::cout << " node=" << (it->first)->getID() << " ele=" << it->second << "\n";1098}1099#endif1100// apply node elevations1101for (auto& it : nodeElevation) {1102NBNode* n = it.first;1103n->reinit(n->getPosition() + Position(0, 0, it.second), n->getType());1104}11051106// apply way elevation to all edges that had layer information1107for (const auto& it : ec) {1108NBEdge* edge = it.second;1109const PositionVector& geom = edge->getGeometry();1110const double length = geom.length2D();1111const double zFrom = nodeElevation[edge->getFromNode()];1112const double zTo = nodeElevation[edge->getToNode()];1113// XXX if the from- or to-node was part of multiple ways with1114// different layers, reconstruct the layer value from origID1115double dist = 0;1116PositionVector newGeom;1117for (auto it_pos = geom.begin(); it_pos != geom.end(); ++it_pos) {1118if (it_pos != geom.begin()) {1119dist += (*it_pos).distanceTo2D(*(it_pos - 1));1120}1121newGeom.push_back((*it_pos) + Position(0, 0, zFrom + (zTo - zFrom) * dist / length));1122}1123edge->setGeometry(newGeom);1124}1125}11261127std::map<NBNode*, std::pair<double, double> >1128NIImporter_OpenStreetMap::getNeighboringNodes(NBNode* node, double maxDist, const std::set<NBNode*>& knownElevation) {1129std::map<NBNode*, std::pair<double, double> > result;1130std::set<NBNode*> visited;1131std::vector<NBNode*> open;1132open.push_back(node);1133result[node] = std::make_pair(0, 0);1134while (!open.empty()) {1135NBNode* n = open.back();1136open.pop_back();1137if (visited.count(n) != 0) {1138continue;1139}1140visited.insert(n);1141const EdgeVector& edges = n->getEdges();1142for (auto e : edges) {1143NBNode* s = nullptr;1144if (n->hasIncoming(e)) {1145s = e->getFromNode();1146} else {1147s = e->getToNode();1148}1149const double dist = result[n].first + e->getGeometry().length2D();1150const double speed = MAX2(e->getSpeed(), result[n].second);1151if (result.count(s) == 0) {1152result[s] = std::make_pair(dist, speed);1153} else {1154result[s] = std::make_pair(MIN2(dist, result[s].first), MAX2(speed, result[s].second));1155}1156if (dist < maxDist && knownElevation.count(s) == 0) {1157open.push_back(s);1158}1159}1160}1161result.erase(node);1162return result;1163}116411651166std::string1167NIImporter_OpenStreetMap::usableType(const std::string& type, const std::string& id, NBTypeCont& tc) {1168if (tc.knows(type)) {1169return type;1170}1171if (myUnusableTypes.count(type) > 0) {1172return "";1173}1174if (myKnownCompoundTypes.count(type) > 0) {1175return myKnownCompoundTypes[type];1176}1177// this edge has a type which does not yet exist in the TypeContainer1178StringTokenizer tok = StringTokenizer(type, compoundTypeSeparator);1179std::vector<std::string> types;1180while (tok.hasNext()) {1181std::string t = tok.next();1182if (tc.knows(t)) {1183if (std::find(types.begin(), types.end(), t) == types.end()) {1184types.push_back(t);1185}1186} else if (tok.size() > 1) {1187if (!StringUtils::startsWith(t, "service.")) {1188WRITE_WARNINGF(TL("Discarding unknown compound '%' in type '%' (first occurrence for edge '%')."), t, type, id);1189}1190}1191}1192if (types.empty()) {1193if (!StringUtils::startsWith(type, "service.")) {1194WRITE_WARNINGF(TL("Discarding unusable type '%' (first occurrence for edge '%')."), type, id);1195}1196myUnusableTypes.insert(type);1197return "";1198}1199const std::string newType = joinToString(types, "|");1200if (tc.knows(newType)) {1201myKnownCompoundTypes[type] = newType;1202return newType;1203} else if (myKnownCompoundTypes.count(newType) > 0) {1204return myKnownCompoundTypes[newType];1205} else {1206// build a new type by merging all values1207int numLanes = 0;1208double maxSpeed = 0;1209int prio = 0;1210double width = NBEdge::UNSPECIFIED_WIDTH;1211double sidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;1212double bikelaneWidth = NBEdge::UNSPECIFIED_WIDTH;1213bool defaultIsOneWay = true;1214SVCPermissions permissions = 0;1215LaneSpreadFunction spreadType = LaneSpreadFunction::RIGHT;1216bool discard = true;1217bool hadDiscard = false;1218for (auto& type2 : types) {1219if (!tc.getEdgeTypeShallBeDiscarded(type2)) {1220numLanes = MAX2(numLanes, tc.getEdgeTypeNumLanes(type2));1221maxSpeed = MAX2(maxSpeed, tc.getEdgeTypeSpeed(type2));1222prio = MAX2(prio, tc.getEdgeTypePriority(type2));1223defaultIsOneWay &= tc.getEdgeTypeIsOneWay(type2);1224//std::cout << "merging component " << type2 << " into type " << newType << " allows=" << getVehicleClassNames(tc.getPermissions(type2)) << " oneway=" << defaultIsOneWay << "\n";1225permissions |= tc.getEdgeTypePermissions(type2);1226spreadType = tc.getEdgeTypeSpreadType(type2);1227width = MAX2(width, tc.getEdgeTypeWidth(type2));1228sidewalkWidth = MAX2(sidewalkWidth, tc.getEdgeTypeSidewalkWidth(type2));1229bikelaneWidth = MAX2(bikelaneWidth, tc.getEdgeTypeBikeLaneWidth(type2));1230discard = false;1231} else {1232hadDiscard = true;1233}1234}1235if (hadDiscard && permissions == 0) {1236discard = true;1237}1238if (discard) {1239WRITE_WARNINGF(TL("Discarding compound type '%' (first occurrence for edge '%')."), newType, id);1240myUnusableTypes.insert(newType);1241return "";1242}1243if (width != NBEdge::UNSPECIFIED_WIDTH) {1244width = MAX2(width, SUMO_const_laneWidth);1245}1246// ensure pedestrians don't run into trains1247if (sidewalkWidth == NBEdge::UNSPECIFIED_WIDTH1248&& (permissions & SVC_PEDESTRIAN) != 01249&& (permissions & SVC_RAIL_CLASSES) != 0) {1250//std::cout << "patching sidewalk for type '" << newType << "' which allows=" << getVehicleClassNames(permissions) << "\n";1251sidewalkWidth = OptionsCont::getOptions().getFloat("default.sidewalk-width");1252}12531254WRITE_MESSAGEF(TL("Adding new type '%' (first occurrence for edge '%')."), type, id);1255tc.insertEdgeType(newType, numLanes, maxSpeed, prio, permissions, spreadType, width,1256defaultIsOneWay, sidewalkWidth, bikelaneWidth, 0, 0, 0);1257for (auto& type3 : types) {1258if (!tc.getEdgeTypeShallBeDiscarded(type3)) {1259tc.copyEdgeTypeRestrictionsAndAttrs(type3, newType);1260}1261}1262myKnownCompoundTypes[type] = newType;1263return newType;1264}1265}12661267void1268NIImporter_OpenStreetMap::extendRailwayDistances(Edge* e, NBTypeCont& tc) {1269const std::string id = toString(e->id);1270std::string type = usableType(e->myHighWayType, id, tc);1271if (type != "" && isRailway(tc.getEdgeTypePermissions(type))) {1272std::vector<NIOSMNode*> nodes;1273std::vector<double> usablePositions;1274std::vector<int> usableIndex;1275for (long long int n : e->myCurrentNodes) {1276NIOSMNode* node = myOSMNodes[n];1277node->positionMeters = interpretDistance(node);1278if (node->positionMeters != std::numeric_limits<double>::max()) {1279usablePositions.push_back(node->positionMeters);1280usableIndex.push_back((int)nodes.size());1281}1282nodes.push_back(node);1283}1284if (usablePositions.size() == 0) {1285return;1286} else {1287bool forward = true;1288if (usablePositions.size() == 1) {1289WRITE_WARNINGF(TL("Ambiguous railway kilometrage direction for way '%' (assuming forward)"), id);1290} else {1291forward = usablePositions.front() < usablePositions.back();1292}1293// check for consistency1294for (int i = 1; i < (int)usablePositions.size(); i++) {1295if ((usablePositions[i - 1] < usablePositions[i]) != forward) {1296WRITE_WARNINGF(TL("Inconsistent railway kilometrage direction for way '%': % (skipping)"), id, toString(usablePositions));1297return;1298}1299}1300if (nodes.size() > usablePositions.size()) {1301// complete missing values1302PositionVector shape;1303for (NIOSMNode* node : nodes) {1304shape.push_back(Position(node->lon, node->lat, 0));1305}1306if (!NBNetBuilder::transformCoordinates(shape)) {1307return; // error will be given later1308}1309double sign = forward ? 1 : -1;1310// extend backward before first usable value1311for (int i = usableIndex.front() - 1; i >= 0; i--) {1312nodes[i]->positionMeters = nodes[i + 1]->positionMeters - sign * shape[i].distanceTo2D(shape[i + 1]);1313}1314// extend forward1315for (int i = usableIndex.front() + 1; i < (int)nodes.size(); i++) {1316if (nodes[i]->positionMeters == std::numeric_limits<double>::max()) {1317nodes[i]->positionMeters = nodes[i - 1]->positionMeters + sign * shape[i].distanceTo2D(shape[i - 1]);1318}1319}1320//std::cout << " way=" << id << " usable=" << toString(usablePositions) << "\n indices=" << toString(usableIndex)1321// << " final:\n";1322//for (auto n : nodes) {1323// std::cout << " " << n->id << " " << n->positionMeters << " " << n->position<< "\n";1324//}1325}1326}1327}1328}132913301331double1332NIImporter_OpenStreetMap::interpretDistance(NIOSMNode* node) {1333if (node->position.size() > 0) {1334try {1335if (StringUtils::startsWith(node->position, "mi:")) {1336return StringUtils::toDouble(node->position.substr(3)) * 1609.344; // meters per mile1337} else {1338return StringUtils::toDouble(node->position) * 1000;1339}1340} catch (...) {1341WRITE_WARNINGF(TL("Value of railway:position is not numeric ('%') in node '%'."), node->position, toString(node->id));1342}1343}1344return std::numeric_limits<double>::max();1345}13461347SUMOVehicleClass1348NIImporter_OpenStreetMap::interpretTransportType(const std::string& type, NIOSMNode* toSet) {1349SUMOVehicleClass result = SVC_IGNORING;1350if (type == "train") {1351result = SVC_RAIL;1352} else if (type == "subway") {1353result = SVC_SUBWAY;1354} else if (type == "aerialway") {1355result = SVC_CABLE_CAR;1356} else if (type == "light_rail" || type == "monorail") {1357result = SVC_RAIL_URBAN;1358} else if (type == "share_taxi") {1359result = SVC_TAXI;1360} else if (type == "minibus") {1361result = SVC_BUS;1362} else if (type == "trolleybus") {1363result = SVC_BUS;1364} else if (SumoVehicleClassStrings.hasString(type)) {1365result = SumoVehicleClassStrings.get(type);1366}1367std::string stop = "";1368if (result == SVC_TRAM) {1369stop = ".tram";1370} else if (result == SVC_BUS) {1371stop = ".bus";1372} else if (isRailway(result)) {1373stop = ".train";1374}1375if (toSet != nullptr && result != SVC_IGNORING) {1376toSet->permissions |= result;1377toSet->ptStopLength = OptionsCont::getOptions().getFloat("osm.stop-output.length" + stop);1378}1379return result;1380}138113821383void1384NIImporter_OpenStreetMap::applyChangeProhibition(NBEdge* e, int changeProhibition) {1385bool multiLane = changeProhibition > 3;1386//std::cout << "applyChangeProhibition e=" << e->getID() << " changeProhibition=" << std::bitset<32>(changeProhibition) << " val=" << changeProhibition << "\n";1387for (int lane = 0; changeProhibition > 0 && lane < e->getNumLanes(); lane++) {1388int code = changeProhibition % 4; // only look at the last 2 bits1389SVCPermissions changeLeft = (code & CHANGE_NO_LEFT) == 0 ? SVCAll : (SVCPermissions)SVC_AUTHORITY;1390SVCPermissions changeRight = (code & CHANGE_NO_RIGHT) == 0 ? SVCAll : (SVCPermissions)SVC_AUTHORITY;1391e->setPermittedChanging(lane, changeLeft, changeRight);1392if (multiLane) {1393changeProhibition = changeProhibition >> 2;1394}1395}1396}139713981399void1400NIImporter_OpenStreetMap::applyLaneUse(NBEdge* e, NIImporter_OpenStreetMap::Edge* nie, const bool forward) {1401if (myImportLaneAccess) {1402const int numLanes = e->getNumLanes();1403const bool lefthand = OptionsCont::getOptions().getBool("lefthand");1404const std::vector<bool>& designated = forward ? nie->myDesignatedLaneForward : nie->myDesignatedLaneBackward;1405const std::vector<SVCPermissions>& allowed = forward ? nie->myAllowedLaneForward : nie->myAllowedLaneBackward;1406const std::vector<SVCPermissions>& disallowed = forward ? nie->myDisallowedLaneForward : nie->myDisallowedLaneBackward;1407for (int lane = 0; lane < numLanes; lane++) {1408// laneUse stores from left to right1409const int i = lefthand ? lane : numLanes - 1 - lane;1410// Extra allowed SVCs for this lane or none if no info was present for the lane1411const SVCPermissions extraAllowed = i < (int)allowed.size() ? allowed[i] : (SVCPermissions)SVC_IGNORING;1412// Extra disallowed SVCs for this lane or none if no info was present for the lane1413const SVCPermissions extraDisallowed = i < (int)disallowed.size() ? disallowed[i] : (SVCPermissions)SVC_IGNORING;1414if (i < (int)designated.size() && designated[i]) {1415// if designated, delete all permissions1416e->setPermissions(SVC_IGNORING, lane);1417e->preferVehicleClass(lane, extraAllowed);1418}1419e->setPermissions((e->getPermissions(lane) | extraAllowed) & (~extraDisallowed), lane);1420}1421}1422}14231424void1425NIImporter_OpenStreetMap::mergeTurnSigns(std::vector<int>& signs, std::vector<int> signs2) {1426if (signs.empty()) {1427signs.insert(signs.begin(), signs2.begin(), signs2.end());1428} else {1429for (int i = 0; i < (int)MIN2(signs.size(), signs2.size()); i++) {1430signs[i] |= signs2[i];1431}1432}1433}143414351436void1437NIImporter_OpenStreetMap::applyTurnSigns(NBEdge* e, const std::vector<int>& turnSigns) {1438if (myImportTurnSigns && turnSigns.size() > 0) {1439// no sidewalks and bike lanes have been added yet1440if ((int)turnSigns.size() == e->getNumLanes()) {1441//std::cout << "apply turnSigns for " << e->getID() << " turnSigns=" << toString(turnSigns) << "\n";1442for (int i = 0; i < (int)turnSigns.size(); i++) {1443// laneUse stores from left to right1444const int laneIndex = e->getNumLanes() - 1 - i;1445NBEdge::Lane& lane = e->getLaneStruct(laneIndex);1446lane.turnSigns = turnSigns[i];1447}1448} else {1449WRITE_WARNINGF(TL("Ignoring turn sign information for % lanes on edge % with % driving lanes"), turnSigns.size(), e->getID(), e->getNumLanes());1450}1451}1452}145314541455// ---------------------------------------------------------------------------1456// definitions of NIImporter_OpenStreetMap::NodesHandler-methods1457// ---------------------------------------------------------------------------1458NIImporter_OpenStreetMap::NodesHandler::NodesHandler(std::map<long long int, NIOSMNode*>& toFill,1459std::set<NIOSMNode*, CompareNodes>& uniqueNodes, const OptionsCont& oc) :1460SUMOSAXHandler("osm - file"),1461myToFill(toFill),1462myCurrentNode(nullptr),1463myIsStation(false),1464myHierarchyLevel(0),1465myUniqueNodes(uniqueNodes),1466myImportElevation(oc.getBool("osm.elevation")),1467myDuplicateNodes(0),1468myOptionsCont(oc) {1469// init rail signal rules1470for (std::string kv : oc.getStringVector("osm.railsignals")) {1471if (kv == "DEFAULT") {1472myRailSignalRules.push_back("railway:signal:main=");1473myRailSignalRules.push_back("railway:signal:combined=");1474} else if (kv == "ALL") {1475myRailSignalRules.push_back("railway=signal");1476} else {1477myRailSignalRules.push_back("railway:signal:" + kv);1478}1479}1480}148114821483NIImporter_OpenStreetMap::NodesHandler::~NodesHandler() = default;14841485void1486NIImporter_OpenStreetMap::NodesHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {1487++myHierarchyLevel;1488if (element == SUMO_TAG_NODE) {1489bool ok = true;1490myLastNodeID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);1491if (myHierarchyLevel != 2) {1492WRITE_ERROR("Node element on wrong XML hierarchy level (id='" + myLastNodeID +1493"', level='" + toString(myHierarchyLevel) + "').");1494return;1495}1496const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, myLastNodeID.c_str(), ok);1497if (action == "delete" || !ok) {1498return;1499}1500try {1501// we do not use attrs.get here to save some time on parsing1502const long long int id = StringUtils::toLong(myLastNodeID);1503myCurrentNode = nullptr;1504const auto insertionIt = myToFill.lower_bound(id);1505if (insertionIt == myToFill.end() || insertionIt->first != id) {1506// assume we are loading multiple files, so we won't report duplicate nodes1507const double tlon = attrs.get<double>(SUMO_ATTR_LON, myLastNodeID.c_str(), ok);1508const double tlat = attrs.get<double>(SUMO_ATTR_LAT, myLastNodeID.c_str(), ok);1509if (!ok) {1510return;1511}1512myCurrentNode = new NIOSMNode(id, tlon, tlat);1513auto similarNode = myUniqueNodes.find(myCurrentNode);1514if (similarNode == myUniqueNodes.end()) {1515myUniqueNodes.insert(myCurrentNode);1516} else {1517delete myCurrentNode;1518myCurrentNode = *similarNode;1519myDuplicateNodes++;1520}1521myToFill.emplace_hint(insertionIt, id, myCurrentNode);1522}1523} catch (FormatException&) {1524WRITE_ERROR(TL("Attribute 'id' in the definition of a node is not of type long long int."));1525return;1526}1527}1528if (element == SUMO_TAG_TAG && myCurrentNode != nullptr) {1529if (myHierarchyLevel != 3) {1530WRITE_ERROR(TL("Tag element on wrong XML hierarchy level."));1531return;1532}1533bool ok = true;1534const std::string& key = attrs.get<std::string>(SUMO_ATTR_K, myLastNodeID.c_str(), ok, false);1535// we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #16361536if (key == "highway" || key == "ele" || key == "crossing" || key == "railway" || key == "public_transport"1537|| key == "name" || key == "train" || key == "bus" || key == "tram" || key == "light_rail" || key == "subway" || key == "station" || key == "noexit"1538|| key == "crossing:barrier"1539|| key == "crossing:light"1540|| key == "railway:ref"1541|| StringUtils::startsWith(key, "railway:signal")1542|| StringUtils::startsWith(key, "railway:position")1543) {1544const std::string& value = attrs.get<std::string>(SUMO_ATTR_V, myLastNodeID.c_str(), ok, false);1545if (key == "highway" && value.find("traffic_signal") != std::string::npos) {1546myCurrentNode->tlsControlled = true;1547} else if (key == "crossing" && value.find("traffic_signals") != std::string::npos) {1548myCurrentNode->tlsControlled = true;1549} else if (key == "highway" && value.find("crossing") != std::string::npos) {1550myCurrentNode->pedestrianCrossing = true;1551} else if ((key == "noexit" && value == "yes")1552|| (key == "railway" && value == "buffer_stop")) {1553myCurrentNode->railwayBufferStop = true;1554} else if (key == "railway" && value.find("crossing") != std::string::npos) {1555myCurrentNode->railwayCrossing = true;1556} else if (key == "crossing:barrier") {1557myCurrentNode->setParameter("crossing:barrier", value);1558} else if (key == "crossing:light") {1559myCurrentNode->setParameter("crossing:light", value);1560} else if (key == "railway:signal:direction") {1561if (value == "both") {1562myCurrentNode->myRailDirection = WAY_BOTH;1563} else if (value == "backward") {1564myCurrentNode->myRailDirection = WAY_BACKWARD;1565} else if (value == "forward") {1566myCurrentNode->myRailDirection = WAY_FORWARD;1567}1568} else if (StringUtils::startsWith(key, "railway:signal") || (key == "railway" && value == "signal")) {1569std::string kv = key + "=" + value;1570std::string kglob = key + "=";1571if ((std::find(myRailSignalRules.begin(), myRailSignalRules.end(), kv) != myRailSignalRules.end())1572|| (std::find(myRailSignalRules.begin(), myRailSignalRules.end(), kglob) != myRailSignalRules.end())) {1573myCurrentNode->railwaySignal = true;1574}1575} else if (StringUtils::startsWith(key, "railway:position") && value.size() > myCurrentNode->position.size()) {1576// use the entry with the highest precision (more digits)1577myCurrentNode->position = value;1578} else if ((key == "public_transport" && value == "stop_position") ||1579(key == "highway" && value == "bus_stop")) {1580myCurrentNode->ptStopPosition = true;1581if (myCurrentNode->ptStopLength == 0) {1582// default length1583myCurrentNode->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length");1584}1585} else if (key == "name") {1586myCurrentNode->name = value;1587} else if (myImportElevation && key == "ele") {1588try {1589const double elevation = StringUtils::parseDist(value);1590if (std::isnan(elevation)) {1591WRITE_WARNINGF(TL("Value of key '%' is invalid ('%') in node '%'."), key, value, myLastNodeID);1592} else {1593myCurrentNode->ele = elevation;1594}1595} catch (...) {1596WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in node '%'."), key, value, myLastNodeID);1597}1598} else if (key == "station") {1599interpretTransportType(value, myCurrentNode);1600myIsStation = true;1601} else if (key == "railway:ref") {1602myRailwayRef = value;1603} else {1604// v="yes"1605interpretTransportType(key, myCurrentNode);1606}1607}1608if (myAllAttributes && (myExtraAttributes.count(key) != 0 || myExtraAttributes.size() == 0)) {1609const std::string info = "node=" + toString(myCurrentNode->id) + ", k=" + key;1610myCurrentNode->setParameter(key, attrs.get<std::string>(SUMO_ATTR_V, info.c_str(), ok, false));1611}1612}1613}161416151616void1617NIImporter_OpenStreetMap::NodesHandler::myEndElement(int element) {1618if (element == SUMO_TAG_NODE && myHierarchyLevel == 2) {1619if (myIsStation && myRailwayRef != "") {1620myCurrentNode->setParameter("railway:ref", myRailwayRef);1621}1622myCurrentNode = nullptr;1623myIsStation = false;1624myRailwayRef = "";1625}1626--myHierarchyLevel;1627}162816291630// ---------------------------------------------------------------------------1631// definitions of NIImporter_OpenStreetMap::EdgesHandler-methods1632// ---------------------------------------------------------------------------1633NIImporter_OpenStreetMap::EdgesHandler::EdgesHandler(1634const std::map<long long int, NIOSMNode*>& osmNodes,1635std::map<long long int, Edge*>& toFill, std::map<long long int, Edge*>& platformShapes,1636const NBTypeCont& tc):1637SUMOSAXHandler("osm - file"),1638myOSMNodes(osmNodes),1639myEdgeMap(toFill),1640myPlatformShapesMap(platformShapes),1641myTypeCont(tc) {16421643const double unlimitedSpeed = OptionsCont::getOptions().getFloat("osm.speedlimit-none");16441645mySpeedMap["nan"] = MAXSPEED_UNGIVEN;1646mySpeedMap["sign"] = MAXSPEED_UNGIVEN;1647mySpeedMap["signals"] = MAXSPEED_UNGIVEN;1648mySpeedMap["none"] = unlimitedSpeed;1649mySpeedMap["no"] = unlimitedSpeed;1650mySpeedMap["walk"] = 5. / 3.6;1651// https://wiki.openstreetmap.org/wiki/Key:source:maxspeed#Commonly_used_values1652mySpeedMap["AT:urban"] = 50. / 3.6;1653mySpeedMap["AT:rural"] = 100. / 3.6;1654mySpeedMap["AT:trunk"] = 100. / 3.6;1655mySpeedMap["AT:motorway"] = 130. / 3.6;1656mySpeedMap["AU:urban"] = 50. / 3.6;1657mySpeedMap["BE:urban"] = 50. / 3.6;1658mySpeedMap["BE:zone"] = 30. / 3.6;1659mySpeedMap["BE:motorway"] = 120. / 3.6;1660mySpeedMap["BE:zone30"] = 30. / 3.6;1661mySpeedMap["BE-VLG:rural"] = 70. / 3.6;1662mySpeedMap["BE-WAL:rural"] = 90. / 3.6;1663mySpeedMap["BE:school"] = 30. / 3.6;1664mySpeedMap["CZ:motorway"] = 130. / 3.6;1665mySpeedMap["CZ:trunk"] = 110. / 3.6;1666mySpeedMap["CZ:rural"] = 90. / 3.6;1667mySpeedMap["CZ:urban_motorway"] = 80. / 3.6;1668mySpeedMap["CZ:urban_trunk"] = 80. / 3.6;1669mySpeedMap["CZ:urban"] = 50. / 3.6;1670mySpeedMap["DE:motorway"] = unlimitedSpeed;1671mySpeedMap["DE:rural"] = 100. / 3.6;1672mySpeedMap["DE:urban"] = 50. / 3.6;1673mySpeedMap["DE:bicycle_road"] = 30. / 3.6;1674mySpeedMap["DK:motorway"] = 130. / 3.6;1675mySpeedMap["DK:rural"] = 80. / 3.6;1676mySpeedMap["DK:urban"] = 50. / 3.6;1677mySpeedMap["EE:urban"] = 50. / 3.6;1678mySpeedMap["EE:rural"] = 90. / 3.6;1679mySpeedMap["ES:urban"] = 50. / 3.6;1680mySpeedMap["ES:zone30"] = 30. / 3.6;1681mySpeedMap["FR:motorway"] = 130. / 3.6; // 110 (raining)1682mySpeedMap["FR:rural"] = 80. / 3.6;1683mySpeedMap["FR:urban"] = 50. / 3.6;1684mySpeedMap["FR:zone30"] = 30. / 3.6;1685mySpeedMap["HU:living_street"] = 20. / 3.6;1686mySpeedMap["HU:motorway"] = 130. / 3.6;1687mySpeedMap["HU:rural"] = 90. / 3.6;1688mySpeedMap["HU:trunk"] = 110. / 3.6;1689mySpeedMap["HU:urban"] = 50. / 3.6;1690mySpeedMap["IT:rural"] = 90. / 3.6;1691mySpeedMap["IT:motorway"] = 130. / 3.6;1692mySpeedMap["IT:urban"] = 50. / 3.6;1693mySpeedMap["JP:nsl"] = 60. / 3.6;1694mySpeedMap["JP:express"] = 100. / 3.6;1695mySpeedMap["LT:rural"] = 90. / 3.6;1696mySpeedMap["LT:urban"] = 50. / 3.6;1697mySpeedMap["NO:rural"] = 80. / 3.6;1698mySpeedMap["NO:urban"] = 50. / 3.6;1699mySpeedMap["ON:urban"] = 50. / 3.6;1700mySpeedMap["ON:rural"] = 80. / 3.6;1701mySpeedMap["PT:motorway"] = 120. / 3.6;1702mySpeedMap["PT:rural"] = 90. / 3.6;1703mySpeedMap["PT:trunk"] = 100. / 3.6;1704mySpeedMap["PT:urban"] = 50. / 3.6;1705mySpeedMap["RO:motorway"] = 130. / 3.6;1706mySpeedMap["RO:rural"] = 90. / 3.6;1707mySpeedMap["RO:trunk"] = 100. / 3.6;1708mySpeedMap["RO:urban"] = 50. / 3.6;1709mySpeedMap["RS:living_street"] = 30. / 3.6;1710mySpeedMap["RS:motorway"] = 130. / 3.6;1711mySpeedMap["RS:rural"] = 80. / 3.6;1712mySpeedMap["RS:trunk"] = 100. / 3.6;1713mySpeedMap["RS:urban"] = 50. / 3.6;1714mySpeedMap["RU:living_street"] = 20. / 3.6;1715mySpeedMap["RU:urban"] = 60. / 3.6;1716mySpeedMap["RU:rural"] = 90. / 3.6;1717mySpeedMap["RU:motorway"] = 110. / 3.6;1718const double seventy = StringUtils::parseSpeed("70mph");1719const double sixty = StringUtils::parseSpeed("60mph");1720mySpeedMap["GB:motorway"] = seventy;1721mySpeedMap["GB:nsl_dual"] = seventy;1722mySpeedMap["GB:nsl_single"] = sixty;1723mySpeedMap["UK:motorway"] = seventy;1724mySpeedMap["UK:nsl_dual"] = seventy;1725mySpeedMap["UK:nsl_single"] = sixty;1726mySpeedMap["UZ:living_street"] = 30. / 3.6;1727mySpeedMap["UZ:urban"] = 70. / 3.6;1728mySpeedMap["UZ:rural"] = 100. / 3.6;1729mySpeedMap["UZ:motorway"] = 110. / 3.6;1730}17311732NIImporter_OpenStreetMap::EdgesHandler::~EdgesHandler() = default;17331734void1735NIImporter_OpenStreetMap::EdgesHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {1736if (element == SUMO_TAG_WAY) {1737bool ok = true;1738const long long int id = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);1739const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, nullptr, ok);1740if (action == "delete" || !ok) {1741myCurrentEdge = nullptr;1742return;1743}1744myCurrentEdge = new Edge(id);1745}1746// parse "nd" (node) elements1747if (element == SUMO_TAG_ND && myCurrentEdge != nullptr) {1748bool ok = true;1749long long int ref = attrs.get<long long int>(SUMO_ATTR_REF, nullptr, ok);1750if (ok) {1751auto node = myOSMNodes.find(ref);1752if (node == myOSMNodes.end()) {1753WRITE_WARNINGF(TL("The referenced geometry information (ref='%') is not known"), toString(ref));1754return;1755}17561757ref = node->second->id; // node may have been substituted1758if (myCurrentEdge->myCurrentNodes.empty() ||1759myCurrentEdge->myCurrentNodes.back() != ref) { // avoid consecutive duplicates1760myCurrentEdge->myCurrentNodes.push_back(ref);1761}17621763}1764}1765if (element == SUMO_TAG_TAG && myCurrentEdge != nullptr) {1766bool ok = true;1767std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentEdge->id).c_str(), ok, false);1768if (key.size() > 6 && StringUtils::startsWith(key, "busway:")) {1769// handle special busway keys1770const std::string buswaySpec = key.substr(7);1771key = "busway";1772if (buswaySpec == "right") {1773myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_FORWARD);1774} else if (buswaySpec == "left") {1775myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BACKWARD);1776} else if (buswaySpec == "both") {1777myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BOTH);1778} else {1779key = "ignore";1780}1781}1782if (myAllAttributes && (myExtraAttributes.count(key) != 0 || myExtraAttributes.size() == 0)) {1783const std::string info = "way=" + toString(myCurrentEdge->id) + ", k=" + key;1784myCurrentEdge->setParameter(key, attrs.get<std::string>(SUMO_ATTR_V, info.c_str(), ok, false));1785}1786// we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #16361787if (!StringUtils::endsWith(key, "way")1788&& !StringUtils::startsWith(key, "lanes")1789&& key != "maxspeed" && key != "maxspeed:type"1790&& key != "zone:maxspeed"1791&& key != "maxspeed:forward" && key != "maxspeed:backward"1792&& key != "junction" && key != "name" && key != "tracks" && key != "layer"1793&& key != "route"1794&& !StringUtils::startsWith(key, "cycleway")1795&& !StringUtils::startsWith(key, "sidewalk")1796&& key != "ref"1797&& key != "highspeed"1798&& !StringUtils::startsWith(key, "parking")1799&& !StringUtils::startsWith(key, "change")1800&& !StringUtils::startsWith(key, "vehicle:lanes")1801&& key != "postal_code"1802&& key != "railway:preferred_direction"1803&& key != "railway:bidirectional"1804&& key != "railway:track_ref"1805&& key != "usage"1806&& key != "access"1807&& key != "emergency"1808&& key != "service"1809&& key != "electrified"1810&& key != "segregated"1811&& key != "bus"1812&& key != "psv"1813&& key != "foot"1814&& key != "bicycle"1815&& key != "oneway:bicycle"1816&& key != "oneway:bus"1817&& key != "oneway:psv"1818&& key != "bus:lanes"1819&& key != "bus:lanes:forward"1820&& key != "bus:lanes:backward"1821&& key != "psv:lanes"1822&& key != "psv:lanes:forward"1823&& key != "psv:lanes:backward"1824&& key != "bicycle:lanes"1825&& key != "bicycle:lanes:forward"1826&& key != "bicycle:lanes:backward"1827&& !StringUtils::startsWith(key, "width")1828&& !(StringUtils::startsWith(key, "turn:") && key.find(":lanes") != std::string::npos)1829&& key != "public_transport") {1830return;1831}1832const std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentEdge->id).c_str(), ok, false);18331834if (key == "highway" || key == "railway" || key == "waterway" || StringUtils::startsWith(key, "cycleway")1835|| key == "busway" || key == "route" || StringUtils::startsWith(key, "sidewalk") || key == "highspeed"1836|| key == "aeroway" || key == "aerialway" || key == "usage" || key == "service") {1837// build type id1838if (key != "highway" || myTypeCont.knows(key + "." + value)) {1839myCurrentEdge->myCurrentIsRoad = true;1840}1841// special cycleway stuff https://wiki.openstreetmap.org/wiki/Key:cycleway1842if (key == "cycleway") {1843if (value == "no" || value == "none" || value == "separate") {1844myCurrentEdge->myCyclewayType = WAY_NONE;1845} else if (value == "both") {1846myCurrentEdge->myCyclewayType = WAY_BOTH;1847} else if (value == "right") {1848myCurrentEdge->myCyclewayType = WAY_FORWARD;1849} else if (value == "left") {1850myCurrentEdge->myCyclewayType = WAY_BACKWARD;1851} else if (value == "opposite_track") {1852myCurrentEdge->myCyclewayType = WAY_BACKWARD;1853} else if (value == "opposite_lane") {1854myCurrentEdge->myCyclewayType = WAY_BACKWARD;1855} else if (value == "opposite") {1856// according to the wiki ref above, this should rather be a bidi lane, see #134381857myCurrentEdge->myCyclewayType = WAY_BACKWARD;1858}1859}1860if (key == "cycleway:left") {1861if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {1862myCurrentEdge->myCyclewayType = WAY_NONE;1863}1864if (value == "yes" || value == "lane" || value == "track") {1865myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_BACKWARD);1866}1867key = "cycleway"; // for type adaption1868}1869if (key == "cycleway:right") {1870if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {1871myCurrentEdge->myCyclewayType = WAY_NONE;1872}1873if (value == "yes" || value == "lane" || value == "track") {1874myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_FORWARD);1875}1876key = "cycleway"; // for type adaption1877}1878if (key == "cycleway:both") {1879if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {1880if (value == "no" || value == "none" || value == "separate") {1881myCurrentEdge->myCyclewayType = WAY_NONE;1882}1883if (value == "yes" || value == "lane" || value == "track") {1884myCurrentEdge->myCyclewayType = WAY_BOTH;1885}1886}1887key = "cycleway"; // for type adaption1888}1889if (key == "cycleway" && value != "lane" && value != "track" && value != "opposite_track" && value != "opposite_lane") {1890// typemap covers only the lane and track cases1891return;1892}1893if (StringUtils::startsWith(key, "cycleway:")) {1894// no need to extend the type id for other cycleway sub tags1895return;1896}1897// special sidewalk stuff1898if (key == "sidewalk") {1899if (value == "no" || value == "none" || value == "separate") {1900myCurrentEdge->mySidewalkType = WAY_NONE;1901} else if (value == "both") {1902myCurrentEdge->mySidewalkType = WAY_BOTH;1903} else if (value == "right") {1904myCurrentEdge->mySidewalkType = WAY_FORWARD;1905} else if (value == "left") {1906myCurrentEdge->mySidewalkType = WAY_BACKWARD;1907}1908}1909if (key == "sidewalk:left") {1910if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {1911myCurrentEdge->mySidewalkType = WAY_NONE;1912}1913if (value == "yes") {1914myCurrentEdge->mySidewalkType = (WayType)(myCurrentEdge->mySidewalkType | WAY_BACKWARD);1915}1916}1917if (key == "sidewalk:right") {1918if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {1919myCurrentEdge->mySidewalkType = WAY_NONE;1920}1921if (value == "yes") {1922myCurrentEdge->mySidewalkType = (WayType)(myCurrentEdge->mySidewalkType | WAY_FORWARD);1923}1924}1925if (key == "sidewalk:both") {1926if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {1927if (value == "no" || value == "none" || value == "separate") {1928myCurrentEdge->mySidewalkType = WAY_NONE;1929}1930if (value == "yes") {1931myCurrentEdge->mySidewalkType = WAY_BOTH;1932}1933}1934}1935if (StringUtils::startsWith(key, "sidewalk")) {1936// no need to extend the type id1937return;1938}1939// special busway stuff1940if (key == "busway") {1941if (value == "no") {1942return;1943}1944if (value == "opposite_track") {1945myCurrentEdge->myBuswayType = WAY_BACKWARD;1946} else if (value == "opposite_lane") {1947myCurrentEdge->myBuswayType = WAY_BACKWARD;1948}1949// no need to extend the type id1950return;1951}1952std::string singleTypeID = key + "." + value;1953if (key == "highspeed") {1954if (value == "no") {1955return;1956}1957singleTypeID = "railway.highspeed";1958}1959addType(singleTypeID);19601961} else if (key == "bus" || key == "psv") {1962// 'psv' includes taxi in the UK but not in germany1963try {1964if (StringUtils::toBool(value)) {1965myCurrentEdge->myExtraAllowed |= SVC_BUS;1966addType(key);1967} else {1968myCurrentEdge->myExtraDisallowed |= SVC_BUS;1969}1970} catch (const BoolFormatException&) {1971myCurrentEdge->myExtraAllowed |= SVC_BUS;1972addType(key);1973}1974} else if (key == "emergency") {1975try {1976if (StringUtils::toBool(value)) {1977myCurrentEdge->myExtraAllowed |= SVC_AUTHORITY | SVC_EMERGENCY;1978}1979} catch (const BoolFormatException&) {1980myCurrentEdge->myExtraAllowed |= SVC_AUTHORITY | SVC_EMERGENCY;1981}1982} else if (key == "access") {1983if (value == "no") {1984myCurrentEdge->myExtraDisallowed |= ~(SVC_PUBLIC_CLASSES | SVC_EMERGENCY | SVC_AUTHORITY);1985}1986} else if (StringUtils::startsWith(key, "width:lanes")) {1987try {1988const std::vector<std::string> values = StringTokenizer(value, "|").getVector();1989std::vector<double> widthLanes;1990for (std::string width : values) {1991const double parsedWidth = width == "" ? -1 : StringUtils::parseDist(width);1992widthLanes.push_back(parsedWidth);1993}19941995if (key == "width:lanes" || key == "width:lanes:forward") {1996myCurrentEdge->myWidthLanesForward = widthLanes;1997} else if (key == "width:lanes:backward") {1998myCurrentEdge->myWidthLanesBackward = widthLanes;1999} else {2000WRITE_WARNINGF(TL("Using default lane width for edge '%' as key '%' could not be parsed."), toString(myCurrentEdge->id), key);2001}2002} catch (const NumberFormatException&) {2003WRITE_WARNINGF(TL("Using default lane width for edge '%' as value '%' could not be parsed."), toString(myCurrentEdge->id), value);2004}2005} else if (key == "width") {2006try {2007myCurrentEdge->myWidth = StringUtils::parseDist(value);2008} catch (const NumberFormatException&) {2009WRITE_WARNINGF(TL("Using default width for edge '%' as value '%' could not be parsed."), toString(myCurrentEdge->id), value);2010}2011} else if (key == "foot") {2012if (value == "use_sidepath" || value == "no") {2013myCurrentEdge->myExtraDisallowed |= SVC_PEDESTRIAN;2014} else if (value == "yes" || value == "designated" || value == "permissive") {2015myCurrentEdge->myExtraAllowed |= SVC_PEDESTRIAN;2016}2017} else if (key == "bicycle") {2018if (value == "use_sidepath" || value == "no") {2019myCurrentEdge->myExtraDisallowed |= SVC_BICYCLE;2020} else if (value == "yes" || value == "designated" || value == "permissive") {2021myCurrentEdge->myExtraAllowed |= SVC_BICYCLE;2022}2023} else if (key == "oneway:bicycle") {2024myCurrentEdge->myExtraTags["oneway:bicycle"] = value;2025} else if (key == "oneway:bus" || key == "oneway:psv") {2026if (value == "no") {2027// need to add a bus way in reversed direction of way2028myCurrentEdge->myBuswayType = WAY_BACKWARD;2029}2030} else if (key == "lanes") {2031try {2032myCurrentEdge->myNoLanes = StringUtils::toInt(value);2033} catch (NumberFormatException&) {2034// might be a list of values2035StringTokenizer st(value, ";", true);2036std::vector<std::string> list = st.getVector();2037if (list.size() >= 2) {2038int minLanes = std::numeric_limits<int>::max();2039try {2040for (auto& i : list) {2041const int numLanes = StringUtils::toInt(StringUtils::prune(i));2042minLanes = MIN2(minLanes, numLanes);2043}2044myCurrentEdge->myNoLanes = minLanes;2045WRITE_WARNINGF(TL("Using minimum lane number from list (%) for edge '%'."), value, toString(myCurrentEdge->id));2046} catch (NumberFormatException&) {2047WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);2048}2049}2050} catch (EmptyData&) {2051WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);2052}2053} else if (key == "lanes:forward") {2054try {2055const int numLanes = StringUtils::toInt(value);2056if (myCurrentEdge->myNoLanesForward < 0 && myCurrentEdge->myNoLanes < 0) {2057// fix lane count in case only lanes:forward and lanes:backward are set2058myCurrentEdge->myNoLanes = numLanes - myCurrentEdge->myNoLanesForward;2059}2060myCurrentEdge->myNoLanesForward = numLanes;2061} catch (...) {2062WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);2063}2064} else if (key == "lanes:backward") {2065try {2066const int numLanes = StringUtils::toInt(value);2067if (myCurrentEdge->myNoLanesForward > 0 && myCurrentEdge->myNoLanes < 0) {2068// fix lane count in case only lanes:forward and lanes:backward are set2069myCurrentEdge->myNoLanes = numLanes + myCurrentEdge->myNoLanesForward;2070}2071// denote backwards count with a negative sign2072myCurrentEdge->myNoLanesForward = -numLanes;2073} catch (...) {2074WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);2075}2076} else if (myCurrentEdge->myMaxSpeed == MAXSPEED_UNGIVEN &&2077(key == "maxspeed" || key == "maxspeed:type" || key == "maxspeed:forward" || key == "zone:maxspeed")) {2078// both 'maxspeed' and 'maxspeed:type' may be given so we must take care not to overwrite an already seen value2079myCurrentEdge->myMaxSpeed = interpretSpeed(key, value);2080} else if (key == "maxspeed:backward" && myCurrentEdge->myMaxSpeedBackward == MAXSPEED_UNGIVEN) {2081myCurrentEdge->myMaxSpeedBackward = interpretSpeed(key, value);2082} else if (key == "junction") {2083if ((value == "roundabout" || value == "circular") && myCurrentEdge->myIsOneWay.empty()) {2084myCurrentEdge->myIsOneWay = "yes";2085}2086if (value == "roundabout") {2087myCurrentEdge->myAmInRoundabout = true;2088}2089} else if (key == "oneway") {2090myCurrentEdge->myIsOneWay = value;2091} else if (key == "name") {2092myCurrentEdge->streetName = value;2093} else if (key == "ref") {2094myCurrentEdge->ref = value;2095myCurrentEdge->setParameter("ref", value);2096} else if (key == "layer") {2097try {2098myCurrentEdge->myLayer = StringUtils::toInt(value);2099} catch (...) {2100WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);2101}2102} else if (key == "tracks") {2103try {2104if (StringUtils::toInt(value) == 1) {2105myCurrentEdge->myIsOneWay = "true";2106} else {2107WRITE_WARNINGF(TL("Ignoring track count % for edge '%'."), value, myCurrentEdge->id);2108}2109} catch (...) {2110WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);2111}2112} else if (key == "railway:preferred_direction") {2113if (value == "both") {2114myCurrentEdge->myRailDirection = WAY_BOTH;2115} else if (myCurrentEdge->myRailDirection == WAY_UNKNOWN) {2116if (value == "backward") {2117myCurrentEdge->myRailDirection = WAY_BACKWARD;2118} else if (value == "forward") {2119myCurrentEdge->myRailDirection = WAY_FORWARD;2120}2121}2122} else if (key == "railway:bidirectional") {2123if (value == "regular") {2124myCurrentEdge->myRailDirection = WAY_BOTH;2125}2126} else if (key == "electrified" || key == "segregated") {2127if (value != "no") {2128myCurrentEdge->myExtraTags[key] = value;2129}2130} else if (key == "railway:track_ref") {2131myCurrentEdge->setParameter(key, value);2132} else if (key == "public_transport" && value == "platform") {2133myCurrentEdge->myExtraTags["platform"] = "yes";2134} else if ((key == "parking:both" || key == "parking:lane:both") && !StringUtils::startsWith(value, "no")) {2135myCurrentEdge->myParkingType |= PARKING_BOTH;2136} else if ((key == "parking:left" || key == "parking:lane:left") && !StringUtils::startsWith(value, "no")) {2137myCurrentEdge->myParkingType |= PARKING_LEFT;2138} else if ((key == "parking:right" || key == "parking:lane:right") && !StringUtils::startsWith(value, "no")) {2139myCurrentEdge->myParkingType |= PARKING_RIGHT;2140} else if (key == "change" || key == "change:lanes") {2141myCurrentEdge->myChangeForward = myCurrentEdge->myChangeBackward = interpretChangeType(value);2142} else if (key == "change:forward" || key == "change:lanes:forward") {2143myCurrentEdge->myChangeForward = interpretChangeType(value);2144} else if (key == "change:backward" || key == "change:lanes:backward") {2145myCurrentEdge->myChangeBackward = interpretChangeType(value);2146} else if (key == "vehicle:lanes" || key == "vehicle:lanes:forward") {2147interpretLaneUse(value, SVC_PASSENGER, true);2148interpretLaneUse(value, SVC_PRIVATE, true);2149} else if (key == "vehicle:lanes:backward") {2150interpretLaneUse(value, SVC_PASSENGER, false);2151interpretLaneUse(value, SVC_PRIVATE, false);2152} else if (key == "bus:lanes" || key == "bus:lanes:forward") {2153interpretLaneUse(value, SVC_BUS, true);2154} else if (key == "bus:lanes:backward") {2155interpretLaneUse(value, SVC_BUS, false);2156} else if (key == "psv:lanes" || key == "psv:lanes:forward") {2157interpretLaneUse(value, SVC_BUS, true);2158interpretLaneUse(value, SVC_TAXI, true);2159} else if (key == "psv:lanes:backward") {2160interpretLaneUse(value, SVC_BUS, false);2161interpretLaneUse(value, SVC_TAXI, false);2162} else if (key == "bicycle:lanes" || key == "bicycle:lanes:forward") {2163interpretLaneUse(value, SVC_BICYCLE, true);2164} else if (key == "bicycle:lanes:backward") {2165interpretLaneUse(value, SVC_BICYCLE, false);2166} else if (StringUtils::startsWith(key, "turn:") && key.find(":lanes") != std::string::npos) {2167int shift = 0;2168// use the first 8 bit to encode permitted directions for all classes2169// and the successive 8 bit blocks for selected classes2170if (StringUtils::startsWith(key, "turn:bus") || StringUtils::startsWith(key, "turn:psv:")) {2171shift = NBEdge::TURN_SIGN_SHIFT_BUS;2172} else if (StringUtils::startsWith(key, "turn:taxi")) {2173shift = NBEdge::TURN_SIGN_SHIFT_TAXI;2174} else if (StringUtils::startsWith(key, "turn:bicycle")) {2175shift = NBEdge::TURN_SIGN_SHIFT_BICYCLE;2176}2177const std::vector<std::string> values = StringTokenizer(value, "|").getVector();2178std::vector<int> turnCodes;2179for (std::string codeList : values) {2180const std::vector<std::string> codes = StringTokenizer(codeList, ";").getVector();2181int turnCode = 0;2182if (codes.size() == 0) {2183turnCode = (int)LinkDirection::STRAIGHT;2184}2185for (std::string code : codes) {2186if (code == "" || code == "none" || code == "through") {2187turnCode |= (int)LinkDirection::STRAIGHT << shift ;2188} else if (code == "left" || code == "sharp_left") {2189turnCode |= (int)LinkDirection::LEFT << shift;2190} else if (code == "right" || code == "sharp_right") {2191turnCode |= (int)LinkDirection::RIGHT << shift;2192} else if (code == "slight_left") {2193turnCode |= (int)LinkDirection::PARTLEFT << shift;2194} else if (code == "slight_right") {2195turnCode |= (int)LinkDirection::PARTRIGHT << shift;2196} else if (code == "reverse") {2197turnCode |= (int)LinkDirection::TURN << shift;2198} else if (code == "merge_to_left" || code == "merge_to_right") {2199turnCode |= (int)LinkDirection::NODIR << shift;2200}2201}2202turnCodes.push_back(turnCode);2203}2204if (StringUtils::endsWith(key, "lanes") || StringUtils::endsWith(key, "lanes:forward")) {2205mergeTurnSigns(myCurrentEdge->myTurnSignsForward, turnCodes);2206} else if (StringUtils::endsWith(key, "lanes:backward")) {2207mergeTurnSigns(myCurrentEdge->myTurnSignsBackward, turnCodes);2208} else if (StringUtils::endsWith(key, "lanes:both_ways")) {2209mergeTurnSigns(myCurrentEdge->myTurnSignsForward, turnCodes);2210mergeTurnSigns(myCurrentEdge->myTurnSignsBackward, turnCodes);2211}2212}2213}2214}221522162217void2218NIImporter_OpenStreetMap::EdgesHandler::addType(const std::string& singleTypeID) {2219// special case: never build compound type for highspeed rail2220if (!myCurrentEdge->myHighWayType.empty() && singleTypeID != "railway.highspeed") {2221if (myCurrentEdge->myHighWayType == "railway.highspeed") {2222return;2223}2224// osm-ways may be used by more than one mode (eg railway.tram + highway.residential. this is relevant for multimodal traffic)2225// we create a new type for this kind of situation which must then be resolved in insertEdge()2226std::vector<std::string> types = StringTokenizer(myCurrentEdge->myHighWayType,2227compoundTypeSeparator).getVector();2228types.push_back(singleTypeID);2229myCurrentEdge->myHighWayType = joinToStringSorting(types, compoundTypeSeparator);2230} else {2231myCurrentEdge->myHighWayType = singleTypeID;2232}2233}223422352236double2237NIImporter_OpenStreetMap::EdgesHandler::interpretSpeed(const std::string& key, std::string value) {2238if (mySpeedMap.find(value) != mySpeedMap.end()) {2239return mySpeedMap[value];2240} else {2241// handle symbolic names of the form DE:30 / DE:zone302242if (value.size() > 3 && value[2] == ':') {2243if (value.substr(3, 4) == "zone") {2244value = value.substr(7);2245} else {2246value = value.substr(3);2247}2248}2249try {2250return StringUtils::parseSpeed(value);2251} catch (...) {2252WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +2253toString(myCurrentEdge->id) + "'.");2254return MAXSPEED_UNGIVEN;2255}2256}2257}225822592260int2261NIImporter_OpenStreetMap::EdgesHandler::interpretChangeType(const std::string& value) const {2262int result = 0;2263const std::vector<std::string> values = StringTokenizer(value, "|").getVector();2264for (const std::string& val : values) {2265if (val == "no") {2266result += CHANGE_NO;2267} else if (val == "not_left") {2268result += CHANGE_NO_LEFT;2269} else if (val == "not_right") {2270result += CHANGE_NO_RIGHT;2271}2272result = result << 2;2273}2274// last shift was superfluous2275result = result >> 2;22762277if (values.size() > 1) {2278result += 2 << 29; // mark multi-value input2279}2280//std::cout << " way=" << myCurrentEdge->id << " value=" << value << " result=" << std::bitset<32>(result) << "\n";2281return result;2282}228322842285void2286NIImporter_OpenStreetMap::EdgesHandler::interpretLaneUse(const std::string& value, SUMOVehicleClass svc, const bool forward) const {2287const std::vector<std::string> values = StringTokenizer(value, "|").getVector();2288std::vector<bool>& designated = forward ? myCurrentEdge->myDesignatedLaneForward : myCurrentEdge->myDesignatedLaneBackward;2289std::vector<SVCPermissions>& allowed = forward ? myCurrentEdge->myAllowedLaneForward : myCurrentEdge->myAllowedLaneBackward;2290std::vector<SVCPermissions>& disallowed = forward ? myCurrentEdge->myDisallowedLaneForward : myCurrentEdge->myDisallowedLaneBackward;2291designated.resize(MAX2(designated.size(), values.size()), false);2292allowed.resize(MAX2(allowed.size(), values.size()), SVC_IGNORING);2293disallowed.resize(MAX2(disallowed.size(), values.size()), SVC_IGNORING);2294int i = 0;2295for (const std::string& val : values) {2296if (val == "yes" || val == "permissive") {2297allowed[i] |= svc;2298} else if (val == "lane" || val == "designated") {2299allowed[i] |= svc;2300designated[i] = true;2301} else if (val == "no") {2302disallowed[i] |= svc;2303} else {2304WRITE_WARNINGF(TL("Unknown lane use specifier '%' ignored for way '%'"), val, myCurrentEdge->id);2305}2306i++;2307}2308}230923102311void2312NIImporter_OpenStreetMap::EdgesHandler::myEndElement(int element) {2313if (element == SUMO_TAG_WAY && myCurrentEdge != nullptr) {2314if (myCurrentEdge->myCurrentIsRoad) {2315const auto insertionIt = myEdgeMap.lower_bound(myCurrentEdge->id);2316if (insertionIt == myEdgeMap.end() || insertionIt->first != myCurrentEdge->id) {2317// assume we are loading multiple files, so we won't report duplicate edges2318myEdgeMap.emplace_hint(insertionIt, myCurrentEdge->id, myCurrentEdge);2319} else {2320delete myCurrentEdge;2321}2322} else if (myCurrentEdge->myExtraTags.count("platform") != 0) {2323const auto insertionIt = myPlatformShapesMap.lower_bound(myCurrentEdge->id);2324if (insertionIt == myPlatformShapesMap.end() || insertionIt->first != myCurrentEdge->id) {2325// assume we are loading multiple files, so we won't report duplicate platforms2326myPlatformShapesMap.emplace_hint(insertionIt, myCurrentEdge->id, myCurrentEdge);2327} else {2328delete myCurrentEdge;2329}2330} else {2331delete myCurrentEdge;2332}2333myCurrentEdge = nullptr;2334}2335}233623372338// ---------------------------------------------------------------------------2339// definitions of NIImporter_OpenStreetMap::RelationHandler-methods2340// ---------------------------------------------------------------------------2341NIImporter_OpenStreetMap::RelationHandler::RelationHandler(2342const std::map<long long int, NIOSMNode*>& osmNodes,2343const std::map<long long int, Edge*>& osmEdges, NBPTStopCont* nbptStopCont,2344const std::map<long long int, Edge*>& platformShapes,2345NBPTLineCont* nbptLineCont,2346const OptionsCont& oc) :2347SUMOSAXHandler("osm - file"),2348myOSMNodes(osmNodes),2349myOSMEdges(osmEdges),2350myPlatformShapes(platformShapes),2351myNBPTStopCont(nbptStopCont),2352myNBPTLineCont(nbptLineCont),2353myOptionsCont(oc) {2354resetValues();2355}235623572358NIImporter_OpenStreetMap::RelationHandler::~RelationHandler() = default;235923602361void2362NIImporter_OpenStreetMap::RelationHandler::resetValues() {2363myCurrentRelation = INVALID_ID;2364myIsRestriction = false;2365myRestrictionException = SVC_IGNORING;2366myFromWay = INVALID_ID;2367myToWay = INVALID_ID;2368myViaNode = INVALID_ID;2369myViaWay = INVALID_ID;2370myStation = INVALID_ID;2371myRestrictionType = RestrictionType::UNKNOWN;2372myPlatforms.clear();2373myStops.clear();2374myPlatformStops.clear();2375myWays.clear();2376myIsStopArea = false;2377myIsRoute = false;2378myPTRouteType = "";2379myRouteColor.setValid(false);2380}238123822383void2384NIImporter_OpenStreetMap::RelationHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {2385if (element == SUMO_TAG_RELATION) {2386bool ok = true;2387myCurrentRelation = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);2388const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, nullptr, ok);2389if (action == "delete" || !ok) {2390myCurrentRelation = INVALID_ID;2391}2392myName = "";2393myRef = "";2394myInterval = -1;2395myNightService = "";2396return;2397}2398if (myCurrentRelation == INVALID_ID) {2399return;2400}2401if (element == SUMO_TAG_MEMBER) {2402bool ok = true;2403std::string role = attrs.hasAttribute("role") ? attrs.getStringSecure("role", "") : "";2404const long long int ref = attrs.get<long long int>(SUMO_ATTR_REF, nullptr, ok);2405if (role == "via") {2406// u-turns for divided ways may be given with 2 via-nodes or 1 via-way2407std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);2408if (memberType == "way" && checkEdgeRef(ref)) {2409myViaWay = ref;2410} else if (memberType == "node") {2411if (myOSMNodes.find(ref) != myOSMNodes.end()) {2412myViaNode = ref;2413} else {2414WRITE_WARNINGF(TL("No node found for reference '%' in relation '%'."), toString(ref), toString(myCurrentRelation));2415}2416}2417} else if (role == "from" && checkEdgeRef(ref)) {2418myFromWay = ref;2419} else if (role == "to" && checkEdgeRef(ref)) {2420myToWay = ref;2421} else if (StringUtils::startsWith(role, "stop")) {2422// permit _entry_only and _exit_only variants2423myStops.push_back(ref);2424} else if (StringUtils::startsWith(role, "platform")) {2425// permit _entry_only and _exit_only variants2426std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);2427if (memberType == "way") {2428const std::map<long long int, NIImporter_OpenStreetMap::Edge*>::const_iterator& wayIt = myPlatformShapes.find(ref);2429if (wayIt != myPlatformShapes.end()) {2430NIIPTPlatform platform;2431platform.isWay = true;2432platform.ref = ref;2433myPlatforms.push_back(platform);2434}2435} else if (memberType == "node") {2436// myIsStopArea may not be set yet2437myStops.push_back(ref);2438myPlatformStops.insert(ref);2439NIIPTPlatform platform;2440platform.isWay = false;2441platform.ref = ref;2442myPlatforms.push_back(platform);2443}24442445} else if (role == "station") {2446myStation = ref;2447} else if (role.empty()) {2448std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);2449if (memberType == "way") {2450myWays.push_back(ref);2451} else if (memberType == "node") {2452auto it = myOSMNodes.find(ref);2453if (it != myOSMNodes.end() && it->second->hasParameter("railway:ref")) {2454myStation = ref;2455} else {2456myStops.push_back(ref);2457}2458}2459}2460return;2461}2462// parse values2463if (element == SUMO_TAG_TAG) {2464bool ok = true;2465std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentRelation).c_str(), ok, false);2466// we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #16362467if (key == "type" || key == "restriction") {2468std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2469if (key == "type" && value == "restriction") {2470myIsRestriction = true;2471return;2472}2473if (key == "type" && value == "route") {2474myIsRoute = true;2475return;2476}2477if (key == "restriction") {2478// @note: the 'right/left/straight' part is ignored since the information is2479// redundantly encoded in the 'from', 'to' and 'via' members2480if (value.substr(0, 5) == "only_") {2481myRestrictionType = RestrictionType::ONLY;2482} else if (value.substr(0, 3) == "no_") {2483myRestrictionType = RestrictionType::NO;2484} else {2485WRITE_WARNINGF(TL("Found unknown restriction type '%' in relation '%'"), value, toString(myCurrentRelation));2486}2487return;2488}2489} else if (key == "except") {2490std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2491for (const std::string& v : StringTokenizer(value, ";").getVector()) {2492if (v == "psv") {2493myRestrictionException |= SVC_BUS;2494} else if (v == "bicycle") {2495myRestrictionException |= SVC_BICYCLE;2496} else if (v == "hgv") {2497myRestrictionException |= SVC_TRUCK | SVC_TRAILER;2498} else if (v == "motorcar") {2499myRestrictionException |= SVC_PASSENGER | SVC_TAXI;2500} else if (v == "emergency") {2501myRestrictionException |= SVC_EMERGENCY;2502}2503}2504} else if (key == "public_transport") {2505std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2506if (value == "stop_area") {2507myIsStopArea = true;2508}2509} else if (key == "route") {2510std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2511if (value == "train" || value == "subway" || value == "light_rail" || value == "monorail" || value == "tram" || value == "bus"2512|| value == "trolleybus" || value == "aerialway" || value == "ferry" || value == "share_taxi" || value == "minibus") {2513myPTRouteType = value;2514}25152516} else if (key == "name") {2517myName = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2518} else if (key == "colour") {2519std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2520try {2521myRouteColor = RGBColor::parseColor(value);2522} catch (...) {2523WRITE_WARNINGF(TL("Invalid color value '%' in relation %"), value, myCurrentRelation);2524}2525} else if (key == "ref") {2526myRef = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2527} else if (key == "interval" || key == "headway") {2528myInterval = attrs.get<int>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2529} else if (key == "by_night") {2530myNightService = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);2531}2532}2533}253425352536bool2537NIImporter_OpenStreetMap::RelationHandler::checkEdgeRef(long long int ref) const {2538if (myOSMEdges.find(ref) != myOSMEdges.end()) {2539return true;2540}2541WRITE_WARNINGF(TL("No way found for reference '%' in relation '%'"), toString(ref), toString(myCurrentRelation));2542return false;2543}254425452546void2547NIImporter_OpenStreetMap::RelationHandler::myEndElement(int element) {2548if (element == SUMO_TAG_RELATION) {2549if (myIsRestriction) {2550assert(myCurrentRelation != INVALID_ID);2551bool ok = true;2552if (myRestrictionType == RestrictionType::UNKNOWN) {2553WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown type."), toString(myCurrentRelation));2554ok = false;2555}2556if (myFromWay == INVALID_ID) {2557WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown from-way."), toString(myCurrentRelation));2558ok = false;2559}2560if (myToWay == INVALID_ID) {2561WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown to-way."), toString(myCurrentRelation));2562ok = false;2563}2564if (myViaNode == INVALID_ID && myViaWay == INVALID_ID) {2565WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown via."), toString(myCurrentRelation));2566ok = false;2567}2568if (ok && !applyRestriction()) {2569WRITE_WARNINGF(TL("Ignoring restriction relation '%'."), toString(myCurrentRelation));2570}2571} else if (myIsStopArea) {2572for (long long ref : myStops) {2573myStopAreas[ref] = myCurrentRelation;2574if (myOSMNodes.find(ref) == myOSMNodes.end()) {2575//WRITE_WARNING(2576// "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)2577// + "' does not exist. Probably OSM file is incomplete.");2578continue;2579}25802581NIOSMNode* n = myOSMNodes.find(ref)->second;2582std::shared_ptr<NBPTStop> ptStop = myNBPTStopCont->get(toString(n->id));2583if (ptStop == nullptr) {2584//WRITE_WARNING(2585// "Relation '" + toString(myCurrentRelation) + "' refers to a non existing pt stop at node: '"2586// + toString(n->id) + "'. Probably OSM file is incomplete.");2587continue;2588}2589for (NIIPTPlatform& myPlatform : myPlatforms) {2590if (myPlatform.isWay) {2591assert(myPlatformShapes.find(myPlatform.ref) != myPlatformShapes.end()); //already tested earlier2592Edge* edge = (*myPlatformShapes.find(myPlatform.ref)).second;2593if (edge->myCurrentNodes.size() > 1 && edge->myCurrentNodes[0] == *(edge->myCurrentNodes.end() - 1)) {2594WRITE_WARNINGF(TL("Platform '%' in relation: '%' is given as polygon, which currently is not supported."), myPlatform.ref, myCurrentRelation);2595continue;25962597}2598PositionVector p;2599for (auto nodeRef : edge->myCurrentNodes) {2600if (myOSMNodes.find(nodeRef) == myOSMNodes.end()) {2601//WRITE_WARNING(2602// "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)2603// + "' does not exist. Probably OSM file is incomplete.");2604continue;2605}2606NIOSMNode* pNode = myOSMNodes.find(nodeRef)->second;2607Position pNodePos(pNode->lon, pNode->lat, pNode->ele);2608if (!NBNetBuilder::transformCoordinate(pNodePos)) {2609WRITE_ERRORF("Unable to project coordinates for node '%'.", pNode->id);2610continue;2611}2612p.push_back(pNodePos);2613}2614if (p.size() == 0) {2615WRITE_WARNINGF(TL("Referenced platform: '%' in relation: '%' is corrupt. Probably OSM file is incomplete."),2616toString(myPlatform.ref), toString(myCurrentRelation));2617continue;2618}2619NBPTPlatform platform(p[(int)p.size() / 2], p.length());2620ptStop->addPlatformCand(platform);2621} else {2622if (myOSMNodes.find(myPlatform.ref) == myOSMNodes.end()) {2623//WRITE_WARNING(2624// "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)2625// + "' does not exist. Probably OSM file is incomplete.");2626continue;2627}2628NIOSMNode* pNode = myOSMNodes.find(myPlatform.ref)->second;2629Position platformPos(pNode->lon, pNode->lat, pNode->ele);2630if (!NBNetBuilder::transformCoordinate(platformPos)) {2631WRITE_ERRORF("Unable to project coordinates for node '%'.", pNode->id);2632}2633NBPTPlatform platform(platformPos, myOptionsCont.getFloat("osm.stop-output.length"));2634ptStop->addPlatformCand(platform);26352636}2637}2638ptStop->setIsMultipleStopPositions(myStops.size() > 1, myCurrentRelation);2639if (myStation != INVALID_ID) {2640const auto& nodeIt = myOSMNodes.find(myStation);2641if (nodeIt != myOSMNodes.end()) {2642NIOSMNode* station = nodeIt->second;2643if (station != nullptr) {2644if (station->hasParameter("railway:ref")) {2645ptStop->setParameter("stationRef", station->getParameter("railway:ref"));2646}2647}2648}2649}2650}2651} else if (myPTRouteType != "" && myIsRoute) {2652NBPTLine* ptLine = new NBPTLine(toString(myCurrentRelation), myName, myPTRouteType, myRef, myInterval, myNightService,2653interpretTransportType(myPTRouteType), myRouteColor);2654bool hadGap = false;2655int missingBefore = 0;2656int missingAfter = 0;2657for (long long ref : myStops) {2658const auto& nodeIt = myOSMNodes.find(ref);2659if (nodeIt == myOSMNodes.end()) {2660if (ptLine->getStops().empty()) {2661missingBefore++;2662} else {2663missingAfter++;2664if (!hadGap) {2665hadGap = true;2666}2667}2668continue;2669}2670if (hadGap) {2671WRITE_WARNINGF(TL("PT line '%' in relation % seems to be split, only keeping first part."), myName, myCurrentRelation);2672missingAfter = (int)myStops.size() - missingBefore - (int)ptLine->getStops().size();2673break;2674}26752676const NIOSMNode* const n = nodeIt->second;2677std::shared_ptr<NBPTStop> ptStop = myNBPTStopCont->get(toString(n->id));2678if (ptStop == nullptr) {2679// loose stop, which must later be mapped onto a line way2680Position ptPos(n->lon, n->lat, n->ele);2681if (!NBNetBuilder::transformCoordinate(ptPos)) {2682WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);2683}2684ptStop = std::make_shared<NBPTStop>(toString(n->id), ptPos, "", "", n->ptStopLength, n->name, n->permissions);2685myNBPTStopCont->insert(ptStop);2686if (myStopAreas.count(n->id)) {2687ptStop->setIsMultipleStopPositions(false, myStopAreas[n->id]);2688}2689if (myPlatformStops.count(n->id) > 0) {2690ptStop->setIsPlatform();2691}2692}2693ptLine->addPTStop(ptStop);2694}2695for (long long& myWay : myWays) {2696auto entr = myOSMEdges.find(myWay);2697if (entr != myOSMEdges.end()) {2698Edge* edge = entr->second;2699for (long long& myCurrentNode : edge->myCurrentNodes) {2700ptLine->addWayNode(myWay, myCurrentNode);2701}2702}2703}2704ptLine->setNumOfStops((int)myStops.size(), missingBefore, missingAfter);2705if (ptLine->getStops().empty()) {2706WRITE_WARNINGF(TL("PT line in relation % with no stops ignored. Probably OSM file is incomplete."), myCurrentRelation);2707delete ptLine;2708resetValues();2709return;2710}2711if (!myNBPTLineCont->insert(ptLine)) {2712WRITE_WARNINGF(TL("Ignoring duplicate PT line '%'."), myCurrentRelation);2713delete ptLine;2714}2715}2716// other relations might use similar subelements so reset in any case2717resetValues();2718}2719}27202721bool2722NIImporter_OpenStreetMap::RelationHandler::applyRestriction() const {2723// since OSM ways are bidirectional we need the via to figure out which direction was meant2724if (myViaNode != INVALID_ID) {2725NBNode* viaNode = myOSMNodes.find(myViaNode)->second->node;2726if (viaNode == nullptr) {2727WRITE_WARNINGF(TL("Via-node '%' was not instantiated"), toString(myViaNode));2728return false;2729}2730NBEdge* from = findEdgeRef(myFromWay, viaNode->getIncomingEdges());2731NBEdge* to = findEdgeRef(myToWay, viaNode->getOutgoingEdges());2732if (from == nullptr) {2733WRITE_WARNINGF(TL("from-edge '%' of restriction relation could not be determined"), toString(myFromWay));2734return false;2735}2736if (to == nullptr) {2737WRITE_WARNINGF(TL("to-edge '%' of restriction relation could not be determined"), toString(myToWay));2738return false;2739}2740if (myRestrictionType == RestrictionType::ONLY) {2741from->addEdge2EdgeConnection(to, true);2742// make sure that these connections remain disabled even if network2743// modifications (ramps.guess) reset existing connections2744for (NBEdge* cand : from->getToNode()->getOutgoingEdges()) {2745if (!from->isConnectedTo(cand)) {2746if (myRestrictionException == SVC_IGNORING) {2747from->removeFromConnections(cand, -1, -1, true);2748} else {2749from->addEdge2EdgeConnection(cand, true, myRestrictionException);2750}2751}2752}2753} else {2754if (myRestrictionException == SVC_IGNORING) {2755from->removeFromConnections(to, -1, -1, true);2756} else {2757from->addEdge2EdgeConnection(to, true, myRestrictionException);2758for (NBEdge* cand : from->getToNode()->getOutgoingEdges()) {2759if (!from->isConnectedTo(cand)) {2760from->addEdge2EdgeConnection(cand, true);2761}2762}2763}2764}2765} else {2766// XXX interpreting via-ways or via-node lists not yet implemented2767WRITE_WARNINGF(TL("direction of restriction relation could not be determined%"), "");2768return false;2769}2770return true;2771}27722773NBEdge*2774NIImporter_OpenStreetMap::RelationHandler::findEdgeRef(long long int wayRef,2775const std::vector<NBEdge*>& candidates) const {2776const std::string prefix = toString(wayRef);2777const std::string backPrefix = "-" + prefix;2778NBEdge* result = nullptr;2779int found = 0;2780for (auto candidate : candidates) {2781if ((candidate->getID().substr(0, prefix.size()) == prefix) ||2782(candidate->getID().substr(0, backPrefix.size()) == backPrefix)) {2783result = candidate;2784found++;2785}2786}2787if (found > 1) {2788WRITE_WARNINGF(TL("Ambiguous way reference '%' in restriction relation"), prefix);2789result = nullptr;2790}2791return result;2792}279327942795/****************************************************************************/279627972798