LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_SUMO.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 93.6 % 626 586
Test Date: 2025-11-13 15:38:19 Functions: 95.0 % 20 19

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    NIImporter_SUMO.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @author  Leonhard Luecken
      19              : /// @date    Mon, 14.04.2008
      20              : ///
      21              : // Importer for networks stored in SUMO format
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : #include <string>
      25              : #include <utils/common/UtilExceptions.h>
      26              : #include <utils/common/StringUtils.h>
      27              : #include <utils/common/MsgHandler.h>
      28              : #include <utils/common/StringTokenizer.h>
      29              : #include <utils/common/FileHelpers.h>
      30              : #include <utils/common/ToString.h>
      31              : #include <utils/common/StringUtils.h>
      32              : #include <utils/xml/SUMOXMLDefinitions.h>
      33              : #include <utils/xml/SUMOSAXHandler.h>
      34              : #include <utils/xml/XMLSubSys.h>
      35              : #include <utils/geom/GeoConvHelper.h>
      36              : #include <utils/geom/GeomConvHelper.h>
      37              : #include <utils/options/OptionsCont.h>
      38              : #include <netbuild/NBEdge.h>
      39              : #include <netbuild/NBEdgeCont.h>
      40              : #include <netbuild/NBNode.h>
      41              : #include <netbuild/NBNodeCont.h>
      42              : #include <netbuild/NBAlgorithms_Ramps.h>
      43              : #include <netbuild/NBNetBuilder.h>
      44              : #include "NILoader.h"
      45              : #include "NIXMLTypesHandler.h"
      46              : #include "NIImporter_SUMO.h"
      47              : 
      48              : 
      49              : // ===========================================================================
      50              : // method definitions
      51              : // ===========================================================================
      52              : // ---------------------------------------------------------------------------
      53              : // static methods (interface in this case)
      54              : // ---------------------------------------------------------------------------
      55              : void
      56         2003 : NIImporter_SUMO::loadNetwork(OptionsCont& oc, NBNetBuilder& nb) {
      57         2003 :     NIImporter_SUMO importer(nb);
      58         2003 :     importer._loadNetwork(oc);
      59         2003 : }
      60              : 
      61              : 
      62              : // ---------------------------------------------------------------------------
      63              : // loader methods
      64              : // ---------------------------------------------------------------------------
      65         2003 : NIImporter_SUMO::NIImporter_SUMO(NBNetBuilder& nb)
      66              :     : SUMOSAXHandler("sumo-network"),
      67         2003 :       myNetBuilder(nb),
      68         2003 :       myNodeCont(nb.getNodeCont()),
      69         2003 :       myTLLCont(nb.getTLLogicCont()),
      70         2003 :       myTypesHandler(nb.getTypeCont()),
      71         2003 :       myCurrentEdge(nullptr),
      72         2003 :       myCurrentLane(nullptr),
      73         2003 :       myCurrentTL(nullptr),
      74         2003 :       myLocation(nullptr),
      75              :       myNetworkVersion(0, 0),
      76         2003 :       myHaveSeenInternalEdge(false),
      77         2003 :       myAmLefthand(false),
      78         2003 :       myChangeLefthand(false),
      79         2003 :       myCornerDetail(0),
      80         2003 :       myLinkDetail(-1),
      81         2003 :       myRectLaneCut(false),
      82         2003 :       myWalkingAreas(false),
      83         2003 :       myLimitTurnSpeed(-1),
      84         2003 :       myCheckLaneFoesAll(false),
      85         2003 :       myCheckLaneFoesRoundabout(true),
      86         2003 :       myTlsIgnoreInternalJunctionJam(false),
      87         2003 :       myDefaultSpreadType(toString(LaneSpreadFunction::RIGHT)),
      88         2003 :       myGeomAvoidOverlap(false),
      89         2003 :       myJunctionsHigherSpeed(false),
      90         2003 :       myInternalJunctionsVehicleWidth(OptionsCont::getOptions().getFloat("internal-junctions.vehicle-width")),
      91         2003 :       myJunctionsMinimalShape(OptionsCont::getOptions().getBool("junctions.minimal-shape")),
      92         6009 :       myJunctionsEndpointShape(OptionsCont::getOptions().getBool("junctions.endpoint-shape")) {
      93         2003 : }
      94              : 
      95              : 
      96         2003 : NIImporter_SUMO::~NIImporter_SUMO() {
      97        66124 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
      98        64121 :         EdgeAttrs* ed = (*i).second;
      99       142481 :         for (std::vector<LaneAttrs*>::const_iterator j = ed->lanes.begin(); j != ed->lanes.end(); ++j) {
     100        78360 :             delete *j;
     101              :         }
     102              :         delete ed;
     103              :     }
     104         2003 :     delete myLocation;
     105         6009 : }
     106              : 
     107              : 
     108              : void
     109         2003 : NIImporter_SUMO::_loadNetwork(OptionsCont& oc) {
     110              :     // check whether the option is set (properly)
     111         4006 :     if (!oc.isUsableFileList("sumo-net-file")) {
     112         1320 :         return;
     113              :     }
     114         1366 :     const std::vector<std::string> discardableParams = oc.getStringVector("discard-params");
     115              :     myDiscardableParams.insert(discardableParams.begin(), discardableParams.end());
     116              :     // parse file(s)
     117         1366 :     const std::vector<std::string> files = oc.getStringVector("sumo-net-file");
     118         1376 :     for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
     119         1386 :         if (!FileHelpers::isReadable(*file)) {
     120            0 :             WRITE_ERRORF(TL("Could not open sumo-net-file '%'."), *file);
     121              :             return;
     122              :         }
     123          693 :         setFileName(*file);
     124         2079 :         const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing sumo-net from '" + *file + "'");
     125          693 :         XMLSubSys::runParser(*this, *file, true);
     126          693 :         PROGRESS_TIME_MESSAGE(before);
     127              :     }
     128              :     // build edges
     129         1366 :     const double maxSegmentLength = oc.getFloat("geometry.max-segment-length");
     130        64804 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     131        64121 :         EdgeAttrs* ed = (*i).second;
     132              :         // skip internal edges
     133        64121 :         if (ed->func == SumoXMLEdgeFunc::INTERNAL || ed->func == SumoXMLEdgeFunc::CROSSING || ed->func == SumoXMLEdgeFunc::WALKINGAREA) {
     134        29859 :             continue;
     135              :         }
     136              :         // get and check the nodes
     137        34262 :         NBNode* from = myNodeCont.retrieve(ed->fromNode);
     138        34262 :         NBNode* to = myNodeCont.retrieve(ed->toNode);
     139        34262 :         if (from == nullptr) {
     140          228 :             WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), ed->id, ed->fromNode);
     141           76 :             continue;
     142              :         }
     143        34186 :         if (to == nullptr) {
     144          114 :             WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), ed->id, ed->toNode);
     145           38 :             continue;
     146              :         }
     147        34148 :         if (from == to) {
     148            0 :             WRITE_ERRORF(TL("Edge's '%' from-node and to-node '%' are identical."), ed->id, ed->toNode);
     149            0 :             continue;
     150              :         }
     151        34148 :         if (ed->shape.size() == 0 && maxSegmentLength > 0) {
     152           13 :             ed->shape.push_back(from->getPosition());
     153           13 :             ed->shape.push_back(to->getPosition());
     154              :             // shape is already cartesian but we must use a copy because the original will be modified
     155           13 :             NBNetBuilder::addGeometrySegments(ed->shape, PositionVector(ed->shape), maxSegmentLength);
     156              :         }
     157              :         // build and insert the edge
     158        34148 :         NBEdge* e = new NBEdge(ed->id, from, to,
     159              :                                ed->type, ed->maxSpeed, NBEdge::UNSPECIFIED_FRICTION,
     160              :                                (int) ed->lanes.size(),
     161              :                                ed->priority, NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
     162       102444 :                                ed->shape, ed->lsf, ed->streetName, "", true); // always use tryIgnoreNodePositions to keep original shape
     163        34148 :         e->setLoadedLength(ed->length);
     164        34148 :         e->updateParameters(ed->getParametersMap());
     165        34148 :         e->setDistance(ed->distance);
     166        34148 :         if (!myNetBuilder.getEdgeCont().insert(e)) {
     167            0 :             WRITE_ERRORF(TL("Could not insert edge '%'."), ed->id);
     168            0 :             delete e;
     169            0 :             continue;
     170              :         }
     171        34148 :         ed->builtEdge = myNetBuilder.getEdgeCont().retrieve(ed->id);
     172        34148 :         if (ed->builtEdge != nullptr) {
     173        33978 :             ed->builtEdge->setEdgeStopOffset(-1, ed->edgeStopOffset);
     174        33978 :             ed->builtEdge->setBidi(ed->bidi != "");
     175        33978 :             ed->builtEdge->setRoutingType(ed->routingType);
     176              :         }
     177              :     }
     178              :     // assign further lane attributes (edges are built)
     179              :     EdgeVector toRemove;
     180         1366 :     const bool dismissVclasses = oc.getBool("dismiss-vclasses");
     181        64804 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     182        64121 :         EdgeAttrs* ed = (*i).second;
     183        64121 :         NBEdge* nbe = ed->builtEdge;
     184        64121 :         if (nbe == nullptr) { // inner edge or removed by explicit list, vclass, ...
     185        30143 :             continue;
     186              :         }
     187              :         const SumoXMLNodeType toType = nbe->getToNode()->getType();
     188        78333 :         for (int fromLaneIndex = 0; fromLaneIndex < (int) ed->lanes.size(); ++fromLaneIndex) {
     189        44355 :             LaneAttrs* lane = ed->lanes[fromLaneIndex];
     190              :             // connections
     191              :             const std::vector<Connection>& connections = lane->connections;
     192       111092 :             for (const Connection& c : connections) {
     193        66737 :                 if (myEdges.count(c.toEdgeID) == 0) {
     194           66 :                     WRITE_ERRORF(TL("Unknown edge '%' given in connection."), c.toEdgeID);
     195           22 :                     continue;
     196              :                 }
     197        66715 :                 NBEdge* toEdge = myEdges[c.toEdgeID]->builtEdge;
     198        66715 :                 if (toEdge == nullptr) { // removed by explicit list, vclass, ...
     199          420 :                     continue;
     200              :                 }
     201        66295 :                 if (toEdge->getFromNode() != nbe->getToNode()) { // inconsistency may occur when merging networks
     202           84 :                     WRITE_WARNINGF("Removing invalid connection from edge '%' to edge '%'", nbe->getID(), toEdge->getID());
     203           21 :                     continue;
     204              :                 }
     205              :                 // patch attribute uncontrolled for legacy networks where it is not set explicitly
     206        66274 :                 bool uncontrolled = c.uncontrolled;
     207              : 
     208       125417 :                 if ((NBNode::isTrafficLight(toType) || toType == SumoXMLNodeType::RAIL_SIGNAL)
     209        67197 :                         && c.tlLinkIndex == NBConnection::InvalidTlIndex) {
     210              :                     uncontrolled = true;
     211              :                 }
     212       132548 :                 nbe->addLane2LaneConnection(
     213        66274 :                     fromLaneIndex, toEdge, c.toLaneIdx, NBEdge::Lane2LaneInfoType::VALIDATED,
     214        66274 :                     true, c.mayDefinitelyPass, c.keepClear ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE,
     215        66274 :                     c.contPos, c.visibility, c.speed, c.friction, c.customLength, c.customShape, uncontrolled, c.permissions, c.indirectLeft, c.edgeType, c.changeLeft, c.changeRight);
     216        66274 :                 if (c.getParametersMap().size() > 0) {
     217            0 :                     nbe->getConnectionRef(fromLaneIndex, toEdge, c.toLaneIdx).updateParameters(c.getParametersMap());
     218              :                 }
     219              :                 // maybe we have a tls-controlled connection
     220        66274 :                 if (c.tlID != "" && myRailSignals.count(c.tlID) == 0) {
     221         7116 :                     const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(c.tlID);
     222         7116 :                     if (programs.size() > 0) {
     223              :                         std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
     224        14250 :                         for (it = programs.begin(); it != programs.end(); it++) {
     225         7134 :                             NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
     226         7134 :                             if (tlDef) {
     227         7134 :                                 tlDef->addConnection(nbe, toEdge, fromLaneIndex, c.toLaneIdx, c.tlLinkIndex, c.tlLinkIndex2, false);
     228              :                             } else {
     229            0 :                                 throw ProcessError("Corrupt traffic light definition '" + c.tlID + "' (program '" + it->first + "')");
     230              :                             }
     231              :                         }
     232              :                     } else {
     233            0 :                         WRITE_ERRORF(TL("The traffic light '%' is not known."), c.tlID);
     234              :                     }
     235              :                 }
     236              :             }
     237              :             // allow/disallow XXX preferred
     238        44355 :             if (!dismissVclasses) {
     239        44349 :                 nbe->setPermissions(parseVehicleClasses(lane->allow, lane->disallow, myNetworkVersion), fromLaneIndex);
     240              :             }
     241        88710 :             nbe->setPermittedChanging(fromLaneIndex, parseVehicleClasses(lane->changeLeft, ""), parseVehicleClasses(lane->changeRight, ""));
     242              :             // width, offset
     243        44355 :             nbe->setLaneWidth(fromLaneIndex, lane->width);
     244        44355 :             nbe->setEndOffset(fromLaneIndex, lane->endOffset);
     245        44355 :             nbe->setSpeed(fromLaneIndex, lane->maxSpeed);
     246        44355 :             nbe->setFriction(fromLaneIndex, lane->friction);
     247        44355 :             nbe->setAcceleration(fromLaneIndex, lane->accelRamp);
     248        44355 :             nbe->getLaneStruct(fromLaneIndex).oppositeID = lane->oppositeID;
     249        44355 :             nbe->getLaneStruct(fromLaneIndex).type = lane->type;
     250        44355 :             nbe->getLaneStruct(fromLaneIndex).updateParameters(lane->getParametersMap());
     251        44355 :             if (lane->customShape) {
     252            5 :                 nbe->setLaneShape(fromLaneIndex, lane->shape);
     253              :             }
     254              :             // stop offset for lane
     255              :             bool stopOffsetSet = false;
     256        44355 :             if (lane->laneStopOffset.isDefined() || nbe->getEdgeStopOffset().isDefined()) {
     257              :                 // apply lane-specific stopOffset (might be none as well)
     258           26 :                 stopOffsetSet = nbe->setEdgeStopOffset(fromLaneIndex, lane->laneStopOffset);
     259              :             }
     260           26 :             if (!stopOffsetSet) {
     261              :                 // apply default stop offset to lane
     262        44329 :                 nbe->setEdgeStopOffset(fromLaneIndex, nbe->getEdgeStopOffset());
     263              :             }
     264              :         }
     265        33978 :         nbe->declareConnectionsAsLoaded();
     266        33978 :         if (!nbe->hasLaneSpecificWidth() && nbe->getLanes()[0].width != NBEdge::UNSPECIFIED_WIDTH) {
     267          404 :             nbe->setLaneWidth(-1, nbe->getLaneWidth(0));
     268              :         }
     269        33978 :         if (!nbe->hasLaneSpecificEndOffset() && nbe->getEndOffset(0) != NBEdge::UNSPECIFIED_OFFSET) {
     270            0 :             nbe->setEndOffset(-1, nbe->getEndOffset(0));
     271              :         }
     272        33978 :         if (!nbe->hasLaneSpecificStopOffsets() && nbe->getEdgeStopOffset().isDefined()) {
     273            4 :             nbe->setEdgeStopOffset(-1, nbe->getEdgeStopOffset());
     274              :         }
     275              :         // check again after permissions are set
     276        33978 :         if (myNetBuilder.getEdgeCont().ignoreFilterMatch(nbe)) {
     277           12 :             myNetBuilder.getEdgeCont().ignore(nbe->getID());
     278           12 :             toRemove.push_back(nbe);
     279              :         }
     280              :     }
     281          695 :     for (EdgeVector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) {
     282           12 :         myNetBuilder.getEdgeCont().erase(myNetBuilder.getDistrictCont(), *i);
     283              :     }
     284              :     // insert loaded prohibitions
     285          703 :     for (std::vector<Prohibition>::const_iterator it = myProhibitions.begin(); it != myProhibitions.end(); it++) {
     286           20 :         NBEdge* prohibitedFrom = myEdges[it->prohibitedFrom]->builtEdge;
     287           20 :         NBEdge* prohibitedTo = myEdges[it->prohibitedTo]->builtEdge;
     288           20 :         NBEdge* prohibitorFrom = myEdges[it->prohibitorFrom]->builtEdge;
     289           20 :         NBEdge* prohibitorTo = myEdges[it->prohibitorTo]->builtEdge;
     290           20 :         if (prohibitedFrom == nullptr) {
     291            0 :             WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitedFrom);
     292           20 :         } else if (prohibitedTo == nullptr) {
     293            0 :             WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitedTo);
     294           20 :         } else if (prohibitorFrom == nullptr) {
     295            9 :             WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitorFrom);
     296           17 :         } else if (prohibitorTo == nullptr) {
     297            0 :             WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitorTo);
     298              :         } else {
     299              :             NBNode* n = prohibitedFrom->getToNode();
     300           17 :             n->addSortedLinkFoes(
     301           34 :                 NBConnection(prohibitorFrom, prohibitorTo),
     302           34 :                 NBConnection(prohibitedFrom, prohibitedTo));
     303              :         }
     304              :     }
     305         1102 :     if (!myHaveSeenInternalEdge && oc.isWriteable("no-internal-links")) {
     306          764 :         oc.set("no-internal-links", "true");
     307              :     }
     308         1366 :     if (oc.isWriteable("lefthand")) {
     309         1360 :         oc.set("lefthand", toString(myAmLefthand));
     310              :     }
     311         1366 :     if (oc.isWriteable("junctions.corner-detail")) {
     312         1290 :         oc.set("junctions.corner-detail", toString(myCornerDetail));
     313              :     }
     314         1366 :     if (oc.isWriteable("junctions.internal-link-detail") && myLinkDetail > 0) {
     315            0 :         oc.set("junctions.internal-link-detail", toString(myLinkDetail));
     316              :     }
     317         1366 :     if (oc.isWriteable("rectangular-lane-cut")) {
     318         1334 :         oc.set("rectangular-lane-cut", toString(myRectLaneCut));
     319              :     }
     320         1366 :     if (oc.isWriteable("walkingareas")) {
     321         1366 :         oc.set("walkingareas", toString(myWalkingAreas));
     322              :     }
     323         1366 :     if (oc.isWriteable("junctions.limit-turn-speed")) {
     324         1364 :         oc.set("junctions.limit-turn-speed", toString(myLimitTurnSpeed));
     325              :     }
     326         2048 :     if (oc.isWriteable("check-lane-foes.all") && oc.getBool("check-lane-foes.all") != myCheckLaneFoesAll) {
     327            2 :         oc.set("check-lane-foes.all", toString(myCheckLaneFoesAll));
     328              :     }
     329         2049 :     if (oc.isWriteable("check-lane-foes.roundabout") && oc.getBool("check-lane-foes.roundabout") != myCheckLaneFoesRoundabout) {
     330            0 :         oc.set("check-lane-foes.roundabout", toString(myCheckLaneFoesRoundabout));
     331              :     }
     332         2048 :     if (oc.isWriteable("tls.ignore-internal-junction-jam") && oc.getBool("tls.ignore-internal-junction-jam") != myTlsIgnoreInternalJunctionJam) {
     333            2 :         oc.set("tls.ignore-internal-junction-jam", toString(myTlsIgnoreInternalJunctionJam));
     334              :     }
     335         2648 :     if (oc.isWriteable("default.spreadtype") && oc.getString("default.spreadtype") != myDefaultSpreadType) {
     336            0 :         oc.set("default.spreadtype", myDefaultSpreadType);
     337              :     }
     338          683 :     if (oc.isWriteable("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap") != myGeomAvoidOverlap) {
     339            0 :         oc.set("geometry.avoid-overlap", toString(myGeomAvoidOverlap));
     340              :     }
     341         2049 :     if (oc.isWriteable("junctions.higher-speed") && oc.getBool("junctions.higher-speed") != myJunctionsHigherSpeed) {
     342            0 :         oc.set("junctions.higher-speed", toString(myJunctionsHigherSpeed));
     343              :     }
     344         2048 :     if (oc.isWriteable("internal-junctions.vehicle-width") && oc.getFloat("internal-junctions.vehicle-width") != myInternalJunctionsVehicleWidth) {
     345            2 :         oc.set("internal-junctions.vehicle-width", toString(myInternalJunctionsVehicleWidth));
     346              :     }
     347         2048 :     if (oc.isWriteable("junctions.minimal-shape") && oc.getBool("junctions.minimal-shape") != myJunctionsMinimalShape) {
     348            2 :         oc.set("junctions.minimal-shape", toString(myJunctionsMinimalShape));
     349              :     }
     350         2048 :     if (oc.isWriteable("junctions.endpoint-shape") && oc.getBool("junctions.endpoint-shape") != myJunctionsEndpointShape) {
     351            2 :         oc.set("junctions.endpoint-shape", toString(myJunctionsEndpointShape));
     352              :     }
     353          683 :     if (!deprecatedVehicleClassesSeen.empty()) {
     354            2 :         WRITE_WARNINGF(TL("Deprecated vehicle class(es) '%' in input network."), toString(deprecatedVehicleClassesSeen));
     355              :         deprecatedVehicleClassesSeen.clear();
     356              :     }
     357         1366 :     if (!oc.getBool("no-internal-links")) {
     358              :         // add loaded crossings
     359          311 :         for (const auto& crossIt : myPedestrianCrossings) {
     360           59 :             NBNode* const node = myNodeCont.retrieve(crossIt.first);
     361          204 :             for (const Crossing& crossing : crossIt.second) {
     362              :                 EdgeVector edges;
     363          372 :                 for (const std::string& edgeID : crossing.crossingEdges) {
     364          227 :                     NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
     365              :                     // edge might have been removed due to options
     366          227 :                     if (edge != nullptr) {
     367          225 :                         edges.push_back(edge);
     368              :                     }
     369              :                 }
     370          145 :                 if (!edges.empty()) {
     371          144 :                     node->addCrossing(edges, crossing.width, crossing.priority,
     372          144 :                                       crossing.customTLIndex, crossing.customTLIndex2, crossing.customShape, true, &crossing);
     373              :                 }
     374          145 :             }
     375              :         }
     376          252 :         myNetBuilder.setHaveNetworkCrossings(myPedestrianCrossings.size() > 0);
     377              :         // add walking area custom shapes
     378          255 :         for (const auto& item : myWACustomShapes) {
     379            3 :             std::string nodeID = SUMOXMLDefinitions::getJunctionIDFromInternalEdge(item.first);
     380            3 :             NBNode* node = myNodeCont.retrieve(nodeID);
     381              :             std::vector<std::string> edgeIDs;
     382            3 :             if (item.second.fromEdges.size() + item.second.toEdges.size() == 0) {
     383              :                 // must be a split crossing
     384              :                 assert(item.second.fromCrossed.size() > 0);
     385              :                 assert(item.second.toCrossed.size() > 0);
     386            2 :                 edgeIDs = item.second.fromCrossed;
     387            2 :                 edgeIDs.insert(edgeIDs.end(), item.second.toCrossed.begin(), item.second.toCrossed.end());
     388            1 :             } else if (item.second.fromEdges.size() > 0) {
     389            1 :                 edgeIDs = item.second.fromEdges;
     390              :             } else {
     391            0 :                 edgeIDs = item.second.toEdges;
     392              :             }
     393              :             EdgeVector edges;
     394            8 :             for (const std::string& edgeID : edgeIDs) {
     395            5 :                 NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
     396              :                 // edge might have been removed due to options
     397            5 :                 if (edge != nullptr) {
     398            5 :                     edges.push_back(edge);
     399              :                 }
     400              :             }
     401            3 :             if (edges.size() > 0) {
     402            3 :                 node->addWalkingAreaShape(edges, item.second.shape, item.second.width);
     403              :             }
     404            3 :         }
     405              :     }
     406              :     // add roundabouts
     407          743 :     for (const std::vector<std::string>& ra : myRoundabouts) {
     408              :         EdgeSet roundabout;
     409          337 :         for (const std::string& edgeID : ra) {
     410          277 :             NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
     411          277 :             if (edge == nullptr) {
     412           18 :                 if (!myNetBuilder.getEdgeCont().wasIgnored(edgeID)) {
     413            0 :                     WRITE_ERRORF(TL("Unknown edge '%' in roundabout"), (edgeID));
     414              :                 }
     415              :             } else {
     416              :                 roundabout.insert(edge);
     417              :             }
     418              :         }
     419           60 :         myNetBuilder.getEdgeCont().addRoundabout(roundabout);
     420              :     }
     421          683 : }
     422              : 
     423              : 
     424              : void
     425       361456 : NIImporter_SUMO::myStartElement(int element,
     426              :                                 const SUMOSAXAttributes& attrs) {
     427              :     /* our goal is to reproduce the input net faithfully
     428              :      * there are different types of objects in the netfile:
     429              :      * 1) those which must be loaded into NBNetBuilder-Containers for processing
     430              :      * 2) those which can be ignored because they are recomputed based on group 1
     431              :      * 3) those which are of no concern to NBNetBuilder but should be exposed to
     432              :      *      netedit. We will probably have to patch NBNetBuilder to contain them
     433              :      *      and hand them over to netedit
     434              :      *    alternative idea: those shouldn't really be contained within the
     435              :      *    network but rather in separate files. teach netedit how to open those
     436              :      *    (POI?)
     437              :      * 4) those which are of concern neither to NBNetBuilder nor netedit and
     438              :      *    must be copied over - need to patch NBNetBuilder for this.
     439              :      *    copy unknown by default
     440              :      */
     441       361456 :     switch (element) {
     442          693 :         case SUMO_TAG_NET: {
     443              :             bool ok;
     444          693 :             myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
     445          693 :             myAmLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
     446          693 :             myCornerDetail = attrs.getOpt<int>(SUMO_ATTR_CORNERDETAIL, nullptr, ok, 0);
     447          693 :             myLinkDetail = attrs.getOpt<int>(SUMO_ATTR_LINKDETAIL, nullptr, ok, -1);
     448          693 :             myRectLaneCut = attrs.getOpt<bool>(SUMO_ATTR_RECTANGULAR_LANE_CUT, nullptr, ok, false);
     449          693 :             myWalkingAreas = attrs.getOpt<bool>(SUMO_ATTR_WALKINGAREAS, nullptr, ok, false);
     450          693 :             myLimitTurnSpeed = attrs.getOpt<double>(SUMO_ATTR_LIMIT_TURN_SPEED, nullptr, ok, -1);
     451          693 :             myCheckLaneFoesAll = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ALL, nullptr, ok, false);
     452          693 :             myCheckLaneFoesRoundabout = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT, nullptr, ok, true);
     453          693 :             myTlsIgnoreInternalJunctionJam = attrs.getOpt<bool>(SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM, nullptr, ok, false);
     454          693 :             myDefaultSpreadType = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, nullptr, ok, myDefaultSpreadType);
     455          693 :             myGeomAvoidOverlap = attrs.getOpt<bool>(SUMO_ATTR_AVOID_OVERLAP, nullptr, ok, myGeomAvoidOverlap);
     456          693 :             myJunctionsHigherSpeed = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, myJunctionsHigherSpeed);
     457          693 :             myInternalJunctionsVehicleWidth = attrs.getOpt<double>(SUMO_ATTR_INTERNAL_JUNCTIONS_VEHICLE_WIDTH, nullptr, ok, myInternalJunctionsVehicleWidth);
     458          693 :             myJunctionsMinimalShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE, nullptr, ok, myJunctionsMinimalShape);
     459          693 :             myJunctionsEndpointShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE, nullptr, ok, myJunctionsEndpointShape);
     460              :             // derived
     461          693 :             const OptionsCont& oc = OptionsCont::getOptions();
     462          696 :             myChangeLefthand = !oc.isDefault("lefthand") && (oc.getBool("lefthand") != myAmLefthand);
     463              : 
     464              :             break;
     465              :         }
     466        64232 :         case SUMO_TAG_EDGE:
     467        64232 :             addEdge(attrs);
     468        64232 :             break;
     469        78467 :         case SUMO_TAG_LANE:
     470        78467 :             addLane(attrs);
     471        78467 :             break;
     472           26 :         case SUMO_TAG_STOPOFFSET: {
     473           26 :             bool ok = true;
     474           26 :             addStopOffsets(attrs, ok);
     475              :         }
     476           26 :         break;
     477           87 :         case SUMO_TAG_NEIGH:
     478           87 :             myCurrentLane->oppositeID = attrs.getString(SUMO_ATTR_LANE);
     479           87 :             break;
     480        22285 :         case SUMO_TAG_JUNCTION:
     481        22285 :             addJunction(attrs);
     482        22284 :             break;
     483        67015 :         case SUMO_TAG_REQUEST:
     484        67015 :             addRequest(attrs);
     485        67015 :             break;
     486       100978 :         case SUMO_TAG_CONNECTION:
     487       100978 :             addConnection(attrs);
     488       100978 :             break;
     489          745 :         case SUMO_TAG_TLLOGIC:
     490          745 :             myCurrentTL = initTrafficLightLogic(attrs, myCurrentTL);
     491          745 :             if (myCurrentTL) {
     492          745 :                 myLastParameterised.push_back(myCurrentTL);
     493              :             }
     494              :             break;
     495         4513 :         case SUMO_TAG_PHASE:
     496         4513 :             addPhase(attrs, myCurrentTL);
     497         4513 :             break;
     498          693 :         case SUMO_TAG_LOCATION:
     499          693 :             delete myLocation;
     500          693 :             myLocation = loadLocation(attrs);
     501          693 :             break;
     502           20 :         case SUMO_TAG_PROHIBITION:
     503           20 :             addProhibition(attrs);
     504           20 :             break;
     505           60 :         case SUMO_TAG_ROUNDABOUT:
     506           60 :             addRoundabout(attrs);
     507           60 :             break;
     508        19681 :         case SUMO_TAG_PARAM:
     509        19681 :             if (myLastParameterised.size() != 0) {
     510        19681 :                 bool ok = true;
     511        19681 :                 const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
     512              :                 if (myDiscardableParams.count(key) == 0) {
     513              :                     // circumventing empty string test
     514        19384 :                     const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
     515        19384 :                     myLastParameterised.back()->setParameter(key, val);
     516              :                 }
     517              :             }
     518              :             break;
     519         1961 :         default:
     520         1961 :             myTypesHandler.myStartElement(element, attrs);
     521         1961 :             break;
     522              :     }
     523       361455 : }
     524              : 
     525              : 
     526              : void
     527       361309 : NIImporter_SUMO::myEndElement(int element) {
     528       361309 :     switch (element) {
     529        64172 :         case SUMO_TAG_EDGE:
     530        64172 :             if (myCurrentEdge != nullptr) {
     531        64168 :                 if (myEdges.find(myCurrentEdge->id) != myEdges.end()) {
     532           94 :                     WRITE_WARNINGF(TL("Edge '%' occurred at least twice in the input."), myCurrentEdge->id);
     533          108 :                     for (LaneAttrs* const lane : myCurrentEdge->lanes) {
     534           61 :                         delete lane;
     535              :                     }
     536           47 :                     delete myCurrentEdge;
     537              :                 } else {
     538        64121 :                     myEdges[myCurrentEdge->id] = myCurrentEdge;
     539              :                 }
     540        64168 :                 myCurrentEdge = nullptr;
     541              :                 myLastParameterised.pop_back();
     542              :             }
     543              :             break;
     544        78467 :         case SUMO_TAG_LANE:
     545        78467 :             if (myCurrentEdge != nullptr && myCurrentLane != nullptr) {
     546        78451 :                 myCurrentEdge->maxSpeed = MAX2(myCurrentEdge->maxSpeed, myCurrentLane->maxSpeed);
     547        78451 :                 myCurrentEdge->lanes.push_back(myCurrentLane);
     548              :                 myLastParameterised.pop_back();
     549              :             }
     550        78467 :             myCurrentLane = nullptr;
     551        78467 :             break;
     552          745 :         case SUMO_TAG_TLLOGIC:
     553          745 :             if (myCurrentTL == nullptr) {
     554            0 :                 WRITE_ERROR(TL("Unmatched closing tag for tl-logic."));
     555              :             } else {
     556          745 :                 if (!myTLLCont.insert(myCurrentTL)) {
     557            6 :                     WRITE_WARNINGF(TL("Could not add program '%' for traffic light '%'"), myCurrentTL->getProgramID(), myCurrentTL->getID());
     558            2 :                     delete myCurrentTL;
     559              :                 }
     560          745 :                 myCurrentTL = nullptr;
     561              :                 myLastParameterised.pop_back();
     562              :             }
     563              :             break;
     564        22284 :         case SUMO_TAG_JUNCTION:
     565        22284 :             if (myCurrentJunction.node != nullptr) {
     566              :                 myLastParameterised.pop_back();
     567              :             }
     568              :             break;
     569       100978 :         case SUMO_TAG_CONNECTION:
     570              :             // !!! this just avoids a crash but is not a real check that it was a connection
     571       100978 :             if (!myLastParameterised.empty()) {
     572              :                 myLastParameterised.pop_back();
     573              :             }
     574              :             break;
     575              :         default:
     576              :             break;
     577              :     }
     578       361309 : }
     579              : 
     580              : 
     581              : void
     582        64232 : NIImporter_SUMO::addEdge(const SUMOSAXAttributes& attrs) {
     583              :     // get the id, report an error if not given or empty...
     584        64232 :     bool ok = true;
     585        64232 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     586        64232 :     if (!ok) {
     587              :         return;
     588              :     }
     589        64228 :     myCurrentEdge = new EdgeAttrs();
     590        64228 :     myLastParameterised.push_back(myCurrentEdge);
     591        64228 :     myCurrentEdge->builtEdge = nullptr;
     592        64228 :     myCurrentEdge->id = id;
     593              :     // get the function
     594        64228 :     myCurrentEdge->func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
     595        64228 :     if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
     596              :         // add the crossing but don't do anything else
     597          149 :         Crossing c(id);
     598          149 :         c.crossingEdges = attrs.get<std::vector<std::string> >(SUMO_ATTR_CROSSING_EDGES, nullptr, ok);
     599          298 :         myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(id)].push_back(c);
     600              :         return;
     601        64228 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL || myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
     602        29725 :         myHaveSeenInternalEdge = true;
     603        29725 :         return; // skip internal edges
     604              :     }
     605              :     // get the origin and the destination node
     606        34354 :     myCurrentEdge->fromNode = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id.c_str(), ok, "");
     607        68708 :     myCurrentEdge->toNode = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
     608        34354 :     myCurrentEdge->priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1);
     609        34354 :     myCurrentEdge->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     610        34354 :     myCurrentEdge->routingType = attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, id.c_str(), ok, "");
     611        34354 :     myCurrentEdge->shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
     612        34354 :     NBNetBuilder::transformCoordinates(myCurrentEdge->shape, true, myLocation);
     613        34354 :     myCurrentEdge->length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
     614        34354 :     myCurrentEdge->maxSpeed = 0;
     615        68708 :     myCurrentEdge->streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     616        34354 :     myCurrentEdge->distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
     617        34354 :     myCurrentEdge->bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
     618        52985 :     if (myCurrentEdge->streetName != "" && OptionsCont::getOptions().isDefault("output.street-names")) {
     619          154 :         OptionsCont::getOptions().set("output.street-names", "true");
     620              :     }
     621              : 
     622        68708 :     std::string lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, id.c_str(), ok, myDefaultSpreadType);
     623              :     if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
     624        34354 :         myCurrentEdge->lsf = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
     625              :     } else {
     626            0 :         WRITE_ERRORF(TL("Unknown spreadType '%' for edge '%'."), lsfS, id);
     627              :     }
     628              : }
     629              : 
     630              : 
     631              : void
     632        78467 : NIImporter_SUMO::addLane(const SUMOSAXAttributes& attrs) {
     633        78467 :     bool ok = true;
     634        78467 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     635        78467 :     if (!ok) {
     636              :         return;
     637              :     }
     638        78459 :     if (myCurrentEdge == nullptr) {
     639           24 :         WRITE_ERRORF(TL("Found lane '%' not within edge element."), id);
     640            8 :         return;
     641              :     }
     642       156902 :     const std::string expectedID = myCurrentEdge->id + "_" + toString(myCurrentEdge->lanes.size());
     643        78451 :     if (id != expectedID) {
     644          108 :         WRITE_WARNINGF(TL("Renaming lane '%' to '%'."), id, expectedID);
     645              :     }
     646        78451 :     myCurrentLane = new LaneAttrs();
     647        78451 :     myLastParameterised.push_back(myCurrentLane);
     648        78451 :     myCurrentLane->customShape = attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, nullptr, ok, false);
     649        78451 :     myCurrentLane->shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
     650        78451 :     myCurrentLane->width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_WIDTH);
     651        78451 :     myCurrentLane->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     652        78451 :     if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
     653              :         // save the width and the lane id of the crossing but don't do anything else
     654          447 :         std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(myCurrentEdge->id)];
     655              :         assert(crossings.size() > 0);
     656          149 :         crossings.back().width = attrs.get<double>(SUMO_ATTR_WIDTH, id.c_str(), ok);
     657              :         myLastParameterised.pop_back();
     658          149 :         myLastParameterised.push_back(&crossings.back());
     659          149 :         if (myCurrentLane->customShape) {
     660              :             crossings.back().customShape = myCurrentLane->shape;
     661           11 :             NBNetBuilder::transformCoordinates(crossings.back().customShape, true, myLocation);
     662              :         }
     663        78302 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
     664              :         // save custom shape if needed but don't do anything else
     665          344 :         if (myCurrentLane->customShape) {
     666              :             WalkingAreaParsedCustomShape wacs;
     667            3 :             wacs.shape = myCurrentLane->shape;
     668            3 :             wacs.width = myCurrentLane->width;
     669            3 :             NBNetBuilder::transformCoordinates(wacs.shape, true, myLocation);
     670            3 :             myWACustomShapes[myCurrentEdge->id] = wacs;
     671            3 :         }
     672          344 :         return;
     673        77958 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL) {
     674              :         return; // skip internal edges
     675              :     }
     676        89988 :     if (attrs.hasAttribute("maxspeed")) {
     677              :         // !!! deprecated
     678            0 :         myCurrentLane->maxSpeed = attrs.getFloat("maxspeed");
     679              :     } else {
     680        44994 :         myCurrentLane->maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
     681              :     }
     682        44994 :     myCurrentLane->friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, NBEdge::UNSPECIFIED_FRICTION, false); //sets 1 on empty
     683              :     try {
     684        89988 :         myCurrentLane->allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, id.c_str(), ok, "", false);
     685            0 :     } catch (EmptyData&) {
     686              :         // !!! deprecated
     687            0 :         myCurrentLane->allow = "";
     688            0 :     }
     689        44994 :     myCurrentLane->disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
     690        44994 :     myCurrentLane->endOffset = attrs.getOpt<double>(SUMO_ATTR_ENDOFFSET, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_OFFSET);
     691        44994 :     myCurrentLane->accelRamp = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
     692        44994 :     myCurrentLane->changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
     693        44994 :     myCurrentLane->changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
     694        44994 :     if (myChangeLefthand) {
     695           10 :         std::swap(myCurrentLane->changeLeft, myCurrentLane->changeRight);
     696              :     }
     697              : 
     698              :     // lane coordinates are derived (via lane spread) do not include them in convex boundary
     699        44994 :     NBNetBuilder::transformCoordinates(myCurrentLane->shape, false, myLocation);
     700              : }
     701              : 
     702              : 
     703              : void
     704           26 : NIImporter_SUMO::addStopOffsets(const SUMOSAXAttributes& attrs, bool& ok) {
     705           26 :     const StopOffset offset(attrs, ok);
     706           26 :     if (!ok) {
     707            1 :         return;
     708              :     }
     709              :     // Admissibility of value will be checked in _loadNetwork(), when lengths are known
     710           25 :     if (myCurrentLane == nullptr) {
     711           12 :         if (myCurrentEdge->edgeStopOffset.isDefined()) {
     712            6 :             WRITE_WARNINGF(TL("Duplicate definition of stopOffset for edge %.\nIgnoring duplicate specification."), myCurrentEdge->id);
     713              :         } else {
     714            9 :             myCurrentEdge->edgeStopOffset = offset;
     715              :         }
     716              :     } else {
     717           13 :         if (myCurrentLane->laneStopOffset.isDefined()) {
     718            4 :             WRITE_WARNINGF(TL("Duplicate definition of lane's stopOffset on edge %.\nIgnoring duplicate specifications."), myCurrentEdge->id);
     719              :         } else {
     720           11 :             myCurrentLane->laneStopOffset = offset;
     721              :         }
     722              :     }
     723              : }
     724              : 
     725              : 
     726              : void
     727        22285 : NIImporter_SUMO::addJunction(const SUMOSAXAttributes& attrs) {
     728              :     // get the id, report an error if not given or empty...
     729        22285 :     myCurrentJunction.node = nullptr;
     730              :     myCurrentJunction.response.clear();
     731        22285 :     bool ok = true;
     732        22285 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     733        22285 :     if (!ok) {
     734              :         return;
     735              :     }
     736        22279 :     if (id[0] == ':') { // internal node
     737              :         return;
     738              :     }
     739        19641 :     SumoXMLNodeType type = attrs.getOpt<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok, SumoXMLNodeType::UNKNOWN);
     740        19641 :     if (ok) {
     741        19635 :         if (type == SumoXMLNodeType::DEAD_END_DEPRECATED || type == SumoXMLNodeType::DEAD_END) {
     742              :             // dead end is a computed status. Reset this to unknown so it will
     743              :             // be corrected if additional connections are loaded
     744              :             type = SumoXMLNodeType::UNKNOWN;
     745        17256 :         } else if (type == SumoXMLNodeType::INTERNAL) {
     746            3 :             WRITE_WARNINGF("Invalid node type '%' for junction '%' in input network", toString(SumoXMLNodeType::INTERNAL), id);
     747              :             type = SumoXMLNodeType::UNKNOWN;
     748              :         }
     749              :     }
     750        19641 :     Position pos = readPosition(attrs, id, ok);
     751        19641 :     NBNetBuilder::transformCoordinate(pos, true, myLocation);
     752        19641 :     NBNode* node = new NBNode(id, pos, type);
     753        19640 :     if (!myNodeCont.insert(node)) {
     754           66 :         WRITE_WARNINGF(TL("Junction '%' occurred at least twice in the input."), id);
     755           22 :         delete node;
     756           22 :         myLastParameterised.push_back(myNodeCont.retrieve(id));
     757           22 :         return;
     758              :     } else {
     759        19618 :         myLastParameterised.push_back(node);
     760              :     }
     761        19618 :     myCurrentJunction.node = node;
     762              :     // set optional radius
     763        19618 :     if (attrs.hasAttribute(SUMO_ATTR_RADIUS)) {
     764           14 :         node->setRadius(attrs.get<double>(SUMO_ATTR_RADIUS, id.c_str(), ok));
     765              :     }
     766              :     // handle custom shape
     767        19618 :     if (attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, id.c_str(), ok, false)) {
     768           17 :         PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
     769           17 :         NBNetBuilder::transformCoordinates(shape, true, myLocation);
     770           17 :         node->setCustomShape(shape);
     771           17 :     }
     772        19618 :     if (type == SumoXMLNodeType::RAIL_SIGNAL || type == SumoXMLNodeType::RAIL_CROSSING) {
     773              :         // both types of nodes come without a tlLogic
     774              :         myRailSignals.insert(id);
     775              :     }
     776        19618 :     node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, id.c_str(), ok, node->getRightOfWay()));
     777        19618 :     node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, id.c_str(), ok, node->getFringeType()));
     778        19618 :     node->setRoundaboutType(attrs.getOpt<RoundaboutType>(SUMO_ATTR_ROUNDABOUT, id.c_str(), ok, node->getRoundaboutType()));
     779        19618 :     if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
     780            5 :         node->setName(attrs.get<std::string>(SUMO_ATTR_NAME, id.c_str(), ok));
     781              :     }
     782              : }
     783              : 
     784              : 
     785              : void
     786        67015 : NIImporter_SUMO::addRequest(const SUMOSAXAttributes& attrs) {
     787        67015 :     if (myCurrentJunction.node != nullptr) {
     788        66945 :         bool ok = true;
     789       133890 :         myCurrentJunction.response.push_back(attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok));
     790              :     }
     791        67015 : }
     792              : 
     793              : 
     794              : void
     795       100978 : NIImporter_SUMO::addConnection(const SUMOSAXAttributes& attrs) {
     796       100978 :     bool ok = true;
     797       100978 :     std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
     798              :     if (myEdges.count(fromID) == 0) {
     799           66 :         WRITE_ERRORF(TL("Unknown edge '%' given in connection."), fromID);
     800           22 :         return;
     801              :     }
     802       100956 :     EdgeAttrs* from = myEdges[fromID];
     803       100956 :     if (from->func == SumoXMLEdgeFunc::INTERNAL) {
     804              :         // internal junction connection
     805              :         return;
     806              :     }
     807              : 
     808        67875 :     Connection conn;
     809        67875 :     conn.toEdgeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
     810        67875 :     int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
     811        67875 :     conn.toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
     812        67875 :     conn.tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
     813        67875 :     conn.mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, false);
     814        67875 :     conn.keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
     815        67875 :     conn.indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
     816        67875 :     conn.edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
     817        67875 :     double contPos = NBEdge::UNSPECIFIED_CONTPOS;
     818       135750 :     if (OptionsCont::getOptions().isSet("default.connection.cont-pos")) {
     819       135750 :         contPos = OptionsCont::getOptions().getFloat("default.connection.cont-pos");
     820              :     }
     821        67875 :     conn.contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, contPos);
     822        67875 :     conn.visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE);
     823        67875 :     std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "", false);
     824        67875 :     std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "", false);
     825        67875 :     if (allow == "" && disallow == "") {
     826        67873 :         conn.permissions = SVC_UNSPECIFIED;
     827              :     } else {
     828            2 :         conn.permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
     829              :     }
     830        67875 :     if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT)) {
     831            1 :         conn.changeLeft = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok), "");
     832              :     } else {
     833        67874 :         conn.changeLeft = SVC_UNSPECIFIED;
     834              :     }
     835        67875 :     if (attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
     836            0 :         conn.changeRight = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok), "");
     837              :     } else {
     838        67875 :         conn.changeRight = SVC_UNSPECIFIED;
     839              :     }
     840        67875 :     if (myChangeLefthand) {
     841              :         std::swap(conn.changeLeft, conn.changeRight);
     842              :     }
     843        67875 :     conn.speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, NBEdge::UNSPECIFIED_SPEED);
     844        67875 :     conn.friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, NBEdge::UNSPECIFIED_FRICTION);
     845        67875 :     conn.customLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
     846       135750 :     conn.customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
     847        67875 :     NBNetBuilder::transformCoordinates(conn.customShape, false, myLocation);
     848        67875 :     conn.uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED, false);
     849        67875 :     if (conn.tlID != "") {
     850         8149 :         conn.tlLinkIndex = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
     851         8149 :         conn.tlLinkIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
     852              :     } else {
     853        59726 :         conn.tlLinkIndex = NBConnection::InvalidTlIndex;
     854              :     }
     855        67875 :     if ((int)from->lanes.size() <= fromLaneIdx) {
     856           24 :         WRITE_ERRORF(TL("Invalid lane index '%' for connection from '%'."), toString(fromLaneIdx), fromID);
     857              :         return;
     858              :     }
     859        67867 :     from->lanes[fromLaneIdx]->connections.push_back(conn);
     860        67867 :     myLastParameterised.push_back(&from->lanes[fromLaneIdx]->connections.back());
     861              : 
     862              :     // determine crossing priority and tlIndex
     863        67867 :     if (myPedestrianCrossings.size() > 0) {
     864         1670 :         if (from->func == SumoXMLEdgeFunc::WALKINGAREA && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::CROSSING) {
     865              :             // connection from walkingArea to crossing
     866          450 :             std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)];
     867          730 :             for (std::vector<Crossing>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
     868          580 :                 if (conn.toEdgeID == (*it).edgeID) {
     869          150 :                     if (conn.tlID != "") {
     870          104 :                         (*it).priority = true;
     871          104 :                         (*it).customTLIndex = conn.tlLinkIndex;
     872              :                     } else {
     873           46 :                         LinkState state = SUMOXMLDefinitions::LinkStates.get(attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok));
     874           46 :                         (*it).priority = state == LINKSTATE_MAJOR;
     875              :                     }
     876              :                 }
     877              :             }
     878         1520 :         } else if (from->func == SumoXMLEdgeFunc::CROSSING && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::WALKINGAREA) {
     879              :             // connection from crossing to walkingArea (set optional linkIndex2)
     880         1022 :             for (Crossing& c : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
     881          575 :                 if (fromID == c.edgeID) {
     882          149 :                     c.customTLIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
     883              :                 }
     884              :             }
     885              :         }
     886              :     }
     887              :     // determine walking area reference edges
     888        67867 :     if (myWACustomShapes.size() > 0) {
     889           88 :         EdgeAttrs* to = myEdges[conn.toEdgeID];
     890           88 :         if (from->func == SumoXMLEdgeFunc::WALKINGAREA) {
     891              :             std::map<std::string, WalkingAreaParsedCustomShape>::iterator it = myWACustomShapes.find(fromID);
     892           28 :             if (it != myWACustomShapes.end()) {
     893            4 :                 if (to->func == SumoXMLEdgeFunc::NORMAL) {
     894              :                     // add target sidewalk as reference
     895            1 :                     it->second.toEdges.push_back(conn.toEdgeID);
     896            3 :                 } else if (to->func == SumoXMLEdgeFunc::CROSSING) {
     897              :                     // add target crossing edges as reference
     898           27 :                     for (Crossing crossing : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
     899           18 :                         if (conn.toEdgeID == crossing.edgeID) {
     900            3 :                             it->second.toCrossed.insert(it->second.toCrossed.end(), crossing.crossingEdges.begin(), crossing.crossingEdges.end());
     901              :                         }
     902           18 :                     }
     903              :                 }
     904              :             }
     905           60 :         } else if (to->func == SumoXMLEdgeFunc::WALKINGAREA) {
     906              :             std::map<std::string, WalkingAreaParsedCustomShape>::iterator it = myWACustomShapes.find(conn.toEdgeID);
     907           28 :             if (it != myWACustomShapes.end()) {
     908            4 :                 if (from->func == SumoXMLEdgeFunc::NORMAL) {
     909              :                     // add origin sidewalk as reference
     910            1 :                     it->second.fromEdges.push_back(fromID);
     911            3 :                 } else if (from->func == SumoXMLEdgeFunc::CROSSING) {
     912              :                     // add origin crossing edges as reference
     913           27 :                     for (Crossing crossing : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
     914           18 :                         if (fromID == crossing.edgeID) {
     915            3 :                             it->second.fromCrossed.insert(it->second.fromCrossed.end(), crossing.crossingEdges.begin(), crossing.crossingEdges.end());
     916              :                         }
     917           18 :                     }
     918              :                 }
     919              :             }
     920              :         }
     921              :     }
     922        67875 : }
     923              : 
     924              : 
     925              : void
     926           20 : NIImporter_SUMO::addProhibition(const SUMOSAXAttributes& attrs) {
     927           20 :     bool ok = true;
     928           20 :     std::string prohibitor = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITOR, nullptr, ok, "");
     929           20 :     std::string prohibited = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITED, nullptr, ok, "");
     930           20 :     if (!ok) {
     931              :         return;
     932              :     }
     933              :     Prohibition p;
     934           20 :     parseProhibitionConnection(prohibitor, p.prohibitorFrom, p.prohibitorTo, ok);
     935           20 :     parseProhibitionConnection(prohibited, p.prohibitedFrom, p.prohibitedTo, ok);
     936           20 :     if (!ok) {
     937              :         return;
     938              :     }
     939           20 :     myProhibitions.push_back(p);
     940           20 : }
     941              : 
     942              : 
     943              : NBLoadedSUMOTLDef*
     944          745 : NIImporter_SUMO::initTrafficLightLogic(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
     945          745 :     if (currentTL) {
     946            0 :         WRITE_ERRORF(TL("Definition of tl-logic '%' was not finished."), currentTL->getID());
     947            0 :         return nullptr;
     948              :     }
     949          745 :     bool ok = true;
     950          745 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     951          745 :     SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
     952          745 :     std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
     953          745 :     std::string typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
     954              :     TrafficLightType type;
     955              :     if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
     956          745 :         type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
     957              :     } else {
     958            0 :         WRITE_ERRORF(TL("Unknown traffic light type '%' for tlLogic '%'."), typeS, id);
     959            0 :         return nullptr;
     960              :     }
     961          745 :     if (ok) {
     962          745 :         return new NBLoadedSUMOTLDef(id, programID, offset, type);
     963              :     } else {
     964              :         return nullptr;
     965              :     }
     966              : }
     967              : 
     968              : 
     969              : void
     970         4757 : NIImporter_SUMO::addPhase(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
     971         4757 :     if (!currentTL) {
     972            0 :         WRITE_ERROR(TL("found phase without tl-logic"));
     973            0 :         return;
     974              :     }
     975              :     const std::string& id = currentTL->getID();
     976         4757 :     bool ok = true;
     977         4757 :     std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, id.c_str(), ok);
     978         4757 :     SUMOTime duration = TIME2STEPS(attrs.get<double>(SUMO_ATTR_DURATION, id.c_str(), ok));
     979         4757 :     if (duration < 0) {
     980            0 :         WRITE_ERRORF(TL("Phase duration for tl-logic '%/%' must be positive."), id, currentTL->getProgramID());
     981              :         return;
     982              :     }
     983              :     // if the traffic light is an actuated traffic light, try to get the minimum and maximum durations and ends
     984         4758 :     std::vector<int> nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
     985         4757 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, nullptr, ok);
     986              :     // Specific from actuated
     987         4757 :     const SUMOTime minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     988         4757 :     const SUMOTime maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     989         4757 :     const SUMOTime earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     990         4757 :     const SUMOTime latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     991              :     // specific von NEMA
     992         4757 :     const SUMOTime vehExt = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     993         4757 :     const SUMOTime yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     994         4757 :     const SUMOTime red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     995         4757 :     if (ok) {
     996         4757 :         currentTL->addPhase(duration, state, minDuration, maxDuration, earliestEnd, latestEnd, vehExt, yellow, red, nextPhases, name);
     997              :     }
     998         4757 : }
     999              : 
    1000              : 
    1001              : GeoConvHelper*
    1002         1282 : NIImporter_SUMO::loadLocation(const SUMOSAXAttributes& attrs, bool setLoaded) {
    1003              :     // @todo refactor parsing of location since its duplicated in NLHandler and PCNetProjectionLoader
    1004         1282 :     bool ok = true;
    1005              :     GeoConvHelper* result = nullptr;
    1006         1282 :     const Position offset = attrs.get<Position>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
    1007         1282 :     const Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
    1008         1282 :     const Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
    1009         1282 :     const std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
    1010         1282 :     if (ok) {
    1011         1282 :         result = new GeoConvHelper(proj, offset, origBoundary, convBoundary);
    1012         1282 :         result->resolveAbstractProjection();
    1013         1282 :         if (setLoaded) {
    1014         1256 :             GeoConvHelper::setLoaded(*result);
    1015              :         }
    1016              :     }
    1017         1282 :     return result;
    1018              : }
    1019              : 
    1020              : 
    1021              : Position
    1022        19641 : NIImporter_SUMO::readPosition(const SUMOSAXAttributes& attrs, const std::string& id, bool& ok) {
    1023        19641 :     const double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
    1024        19641 :     const double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
    1025        19641 :     const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0.);
    1026        19641 :     return Position(x, y, z);
    1027              : }
    1028              : 
    1029              : 
    1030              : void
    1031           40 : NIImporter_SUMO::parseProhibitionConnection(const std::string& attr, std::string& from, std::string& to, bool& ok) {
    1032              :     // split from/to
    1033              :     const std::string::size_type div = attr.find("->");
    1034           40 :     if (div == std::string::npos) {
    1035            0 :         WRITE_ERRORF(TL("Missing connection divider in prohibition attribute '%'"), attr);
    1036            0 :         ok = false;
    1037              :     }
    1038           40 :     from = attr.substr(0, div);
    1039           80 :     to = attr.substr(div + 2);
    1040              :     // check whether the edges are known
    1041              :     if (myEdges.count(from) == 0) {
    1042            0 :         WRITE_ERRORF(TL("Unknown edge prohibition '%'"), from);
    1043            0 :         ok = false;
    1044              :     }
    1045              :     if (myEdges.count(to) == 0) {
    1046            0 :         WRITE_ERRORF(TL("Unknown edge prohibition '%'"), to);
    1047            0 :         ok = false;
    1048              :     }
    1049           40 : }
    1050              : 
    1051              : 
    1052              : void
    1053           60 : NIImporter_SUMO::addRoundabout(const SUMOSAXAttributes& attrs) {
    1054           60 :     bool ok = true;
    1055           60 :     const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
    1056           60 :     if (ok) {
    1057           60 :         myRoundabouts.push_back(edgeIDs);
    1058              :     }
    1059           60 : }
    1060              : 
    1061              : 
    1062              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1