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: 2026-03-02 16:00:03 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-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    NIImporter_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         2031 : NIImporter_SUMO::loadNetwork(OptionsCont& oc, NBNetBuilder& nb) {
      57         2031 :     NIImporter_SUMO importer(nb);
      58         2031 :     importer._loadNetwork(oc);
      59         2031 : }
      60              : 
      61              : 
      62              : // ---------------------------------------------------------------------------
      63              : // loader methods
      64              : // ---------------------------------------------------------------------------
      65         2031 : NIImporter_SUMO::NIImporter_SUMO(NBNetBuilder& nb)
      66              :     : SUMOSAXHandler("sumo-network"),
      67         2031 :       myNetBuilder(nb),
      68         2031 :       myNodeCont(nb.getNodeCont()),
      69         2031 :       myTLLCont(nb.getTLLogicCont()),
      70         2031 :       myTypesHandler(nb.getTypeCont()),
      71         2031 :       myCurrentEdge(nullptr),
      72         2031 :       myCurrentLane(nullptr),
      73         2031 :       myCurrentTL(nullptr),
      74         2031 :       myLocation(nullptr),
      75              :       myNetworkVersion(0, 0),
      76         2031 :       myHaveSeenInternalEdge(false),
      77         2031 :       myAmLefthand(false),
      78         2031 :       myChangeLefthand(false),
      79         2031 :       myCornerDetail(0),
      80         2031 :       myLinkDetail(-1),
      81         2031 :       myRectLaneCut(false),
      82         2031 :       myWalkingAreas(false),
      83         2031 :       myLimitTurnSpeed(-1),
      84         2031 :       myCheckLaneFoesAll(false),
      85         2031 :       myCheckLaneFoesRoundabout(true),
      86         2031 :       myTlsIgnoreInternalJunctionJam(false),
      87         2031 :       myDefaultSpreadType(toString(LaneSpreadFunction::RIGHT)),
      88         2031 :       myGeomAvoidOverlap(false),
      89         2031 :       myJunctionsHigherSpeed(false),
      90         2031 :       myInternalJunctionsVehicleWidth(OptionsCont::getOptions().getFloat("internal-junctions.vehicle-width")),
      91         2031 :       myJunctionsMinimalShape(OptionsCont::getOptions().getBool("junctions.minimal-shape")),
      92         6093 :       myJunctionsEndpointShape(OptionsCont::getOptions().getBool("junctions.endpoint-shape")) {
      93         2031 : }
      94              : 
      95              : 
      96         2031 : NIImporter_SUMO::~NIImporter_SUMO() {
      97        67375 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
      98        65344 :         EdgeAttrs* ed = (*i).second;
      99       144932 :         for (std::vector<LaneAttrs*>::const_iterator j = ed->lanes.begin(); j != ed->lanes.end(); ++j) {
     100        79588 :             delete *j;
     101              :         }
     102              :         delete ed;
     103              :     }
     104         2031 :     delete myLocation;
     105         6093 : }
     106              : 
     107              : 
     108              : void
     109         2031 : NIImporter_SUMO::_loadNetwork(OptionsCont& oc) {
     110              :     // check whether the option is set (properly)
     111         4062 :     if (!oc.isUsableFileList("sumo-net-file")) {
     112         1333 :         return;
     113              :     }
     114         1396 :     const std::vector<std::string> discardableParams = oc.getStringVector("discard-params");
     115              :     myDiscardableParams.insert(discardableParams.begin(), discardableParams.end());
     116              :     // parse file(s)
     117         1396 :     const std::vector<std::string> files = oc.getStringVector("sumo-net-file");
     118         1406 :     for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
     119         1416 :         if (!FileHelpers::isReadable(*file)) {
     120            0 :             WRITE_ERRORF(TL("Could not open sumo-net-file '%'."), *file);
     121              :             return;
     122              :         }
     123          708 :         setFileName(*file);
     124         2124 :         const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing sumo-net from '" + *file + "'");
     125          708 :         XMLSubSys::runParser(*this, *file, true);
     126          708 :         PROGRESS_TIME_MESSAGE(before);
     127              :     }
     128              :     // build edges
     129         1396 :     const double maxSegmentLength = oc.getFloat("geometry.max-segment-length");
     130        66042 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     131        65344 :         EdgeAttrs* ed = (*i).second;
     132              :         // skip internal edges
     133        65344 :         if (ed->func == SumoXMLEdgeFunc::INTERNAL || ed->func == SumoXMLEdgeFunc::CROSSING || ed->func == SumoXMLEdgeFunc::WALKINGAREA) {
     134        30342 :             continue;
     135              :         }
     136              :         // get and check the nodes
     137        35002 :         NBNode* from = myNodeCont.retrieve(ed->fromNode);
     138        35002 :         NBNode* to = myNodeCont.retrieve(ed->toNode);
     139        35002 :         if (from == nullptr) {
     140          228 :             WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), ed->id, ed->fromNode);
     141           76 :             continue;
     142              :         }
     143        34926 :         if (to == nullptr) {
     144          114 :             WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), ed->id, ed->toNode);
     145           38 :             continue;
     146              :         }
     147        34888 :         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        34888 :         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        34888 :         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       104664 :                                ed->shape, ed->lsf, ed->streetName, "", true); // always use tryIgnoreNodePositions to keep original shape
     163        34888 :         e->setLoadedLength(ed->length);
     164        34888 :         e->updateParameters(ed->getParametersMap());
     165        34888 :         e->setDistance(ed->distance);
     166        34888 :         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        34888 :         ed->builtEdge = myNetBuilder.getEdgeCont().retrieve(ed->id);
     172        34888 :         if (ed->builtEdge != nullptr) {
     173        34718 :             ed->builtEdge->setEdgeStopOffset(-1, ed->edgeStopOffset);
     174        34718 :             ed->builtEdge->setBidi(ed->bidi != "");
     175        34718 :             ed->builtEdge->setRoutingType(ed->routingType);
     176              :         }
     177              :     }
     178              :     // assign further lane attributes (edges are built)
     179              :     EdgeVector toRemove;
     180         1396 :     const bool dismissVclasses = oc.getBool("dismiss-vclasses");
     181        66042 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     182        65344 :         EdgeAttrs* ed = (*i).second;
     183        65344 :         NBEdge* nbe = ed->builtEdge;
     184        65344 :         if (nbe == nullptr) { // inner edge or removed by explicit list, vclass, ...
     185        30626 :             continue;
     186              :         }
     187              :         const SumoXMLNodeType toType = nbe->getToNode()->getType();
     188        79818 :         for (int fromLaneIndex = 0; fromLaneIndex < (int) ed->lanes.size(); ++fromLaneIndex) {
     189        45100 :             LaneAttrs* lane = ed->lanes[fromLaneIndex];
     190              :             // connections
     191              :             const std::vector<Connection>& connections = lane->connections;
     192       112689 :             for (const Connection& c : connections) {
     193        67589 :                 if (myEdges.count(c.toEdgeID) == 0) {
     194           66 :                     WRITE_ERRORF(TL("Unknown edge '%' given in connection."), c.toEdgeID);
     195           22 :                     continue;
     196              :                 }
     197        67567 :                 NBEdge* toEdge = myEdges[c.toEdgeID]->builtEdge;
     198        67567 :                 if (toEdge == nullptr) { // removed by explicit list, vclass, ...
     199          424 :                     continue;
     200              :                 }
     201        67143 :                 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        67122 :                 bool uncontrolled = c.uncontrolled;
     207              : 
     208       127081 :                 if ((NBNode::isTrafficLight(toType) || toType == SumoXMLNodeType::RAIL_SIGNAL)
     209        68045 :                         && c.tlLinkIndex == NBConnection::InvalidTlIndex) {
     210              :                     uncontrolled = true;
     211              :                 }
     212       134244 :                 nbe->addLane2LaneConnection(
     213        67122 :                     fromLaneIndex, toEdge, c.toLaneIdx, NBEdge::Lane2LaneInfoType::VALIDATED,
     214        67122 :                     true, c.mayDefinitelyPass, c.keepClear ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE,
     215        67122 :                     c.contPos, c.visibility, c.speed, c.friction, c.customLength, c.customShape, uncontrolled, c.permissions, c.indirectLeft, c.edgeType, c.changeLeft, c.changeRight);
     216        67122 :                 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        67122 :                 if (c.tlID != "" && myRailSignals.count(c.tlID) == 0) {
     221         7148 :                     const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(c.tlID);
     222         7148 :                     if (programs.size() > 0) {
     223              :                         std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
     224        14314 :                         for (it = programs.begin(); it != programs.end(); it++) {
     225         7166 :                             NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
     226         7166 :                             if (tlDef) {
     227         7166 :                                 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        45100 :             if (!dismissVclasses) {
     239        45094 :                 nbe->setPermissions(parseVehicleClasses(lane->allow, lane->disallow, myNetworkVersion), fromLaneIndex);
     240              :             }
     241        90200 :             nbe->setPermittedChanging(fromLaneIndex, parseVehicleClasses(lane->changeLeft, ""), parseVehicleClasses(lane->changeRight, ""));
     242              :             // width, offset
     243        45100 :             nbe->setLaneWidth(fromLaneIndex, lane->width);
     244        45100 :             nbe->setEndOffset(fromLaneIndex, lane->endOffset);
     245        45100 :             nbe->setSpeed(fromLaneIndex, lane->maxSpeed);
     246        45100 :             nbe->setFriction(fromLaneIndex, lane->friction);
     247        45100 :             nbe->setAcceleration(fromLaneIndex, lane->accelRamp);
     248        45100 :             nbe->getLaneStruct(fromLaneIndex).oppositeID = lane->oppositeID;
     249        45100 :             nbe->getLaneStruct(fromLaneIndex).type = lane->type;
     250        45100 :             nbe->getLaneStruct(fromLaneIndex).updateParameters(lane->getParametersMap());
     251        45100 :             if (lane->customShape) {
     252            5 :                 nbe->setLaneShape(fromLaneIndex, lane->shape);
     253              :             }
     254              :             // stop offset for lane
     255              :             bool stopOffsetSet = false;
     256        45100 :             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        45074 :                 nbe->setEdgeStopOffset(fromLaneIndex, nbe->getEdgeStopOffset());
     263              :             }
     264              :         }
     265        34718 :         nbe->declareConnectionsAsLoaded();
     266        34718 :         if (!nbe->hasLaneSpecificWidth() && nbe->getLanes()[0].width != NBEdge::UNSPECIFIED_WIDTH) {
     267          410 :             nbe->setLaneWidth(-1, nbe->getLaneWidth(0));
     268              :         }
     269        34718 :         if (!nbe->hasLaneSpecificEndOffset() && nbe->getEndOffset(0) != NBEdge::UNSPECIFIED_OFFSET) {
     270            0 :             nbe->setEndOffset(-1, nbe->getEndOffset(0));
     271              :         }
     272        34718 :         if (!nbe->hasLaneSpecificStopOffsets() && nbe->getEdgeStopOffset().isDefined()) {
     273            4 :             nbe->setEdgeStopOffset(-1, nbe->getEdgeStopOffset());
     274              :         }
     275              :         // check again after permissions are set
     276        34718 :         if (myNetBuilder.getEdgeCont().ignoreFilterMatch(nbe)) {
     277           12 :             myNetBuilder.getEdgeCont().ignore(nbe->getID());
     278           12 :             toRemove.push_back(nbe);
     279              :         }
     280              :     }
     281          710 :     for (EdgeVector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) {
     282           12 :         myNetBuilder.getEdgeCont().erase(myNetBuilder.getDistrictCont(), *i);
     283              :     }
     284              :     // insert loaded prohibitions
     285          718 :     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         1121 :     if (!myHaveSeenInternalEdge && oc.isWriteable("no-internal-links")) {
     306          766 :         oc.set("no-internal-links", "true");
     307              :     }
     308         1396 :     if (oc.isWriteable("lefthand")) {
     309         1390 :         oc.set("lefthand", toString(myAmLefthand));
     310              :     }
     311         1396 :     if (oc.isWriteable("junctions.corner-detail")) {
     312         1308 :         oc.set("junctions.corner-detail", toString(myCornerDetail));
     313              :     }
     314         1396 :     if (oc.isWriteable("junctions.internal-link-detail") && myLinkDetail > 0) {
     315            0 :         oc.set("junctions.internal-link-detail", toString(myLinkDetail));
     316              :     }
     317         1396 :     if (oc.isWriteable("rectangular-lane-cut")) {
     318         1364 :         oc.set("rectangular-lane-cut", toString(myRectLaneCut));
     319              :     }
     320         1396 :     if (oc.isWriteable("walkingareas")) {
     321         1396 :         oc.set("walkingareas", toString(myWalkingAreas));
     322              :     }
     323         1396 :     if (oc.isWriteable("junctions.limit-turn-speed")) {
     324         1394 :         oc.set("junctions.limit-turn-speed", toString(myLimitTurnSpeed));
     325              :     }
     326         2093 :     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         2094 :     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         2093 :     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         2708 :     if (oc.isWriteable("default.spreadtype") && oc.getString("default.spreadtype") != myDefaultSpreadType) {
     336            0 :         oc.set("default.spreadtype", myDefaultSpreadType);
     337              :     }
     338          698 :     if (oc.isWriteable("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap") != myGeomAvoidOverlap) {
     339            0 :         oc.set("geometry.avoid-overlap", toString(myGeomAvoidOverlap));
     340              :     }
     341         2094 :     if (oc.isWriteable("junctions.higher-speed") && oc.getBool("junctions.higher-speed") != myJunctionsHigherSpeed) {
     342            0 :         oc.set("junctions.higher-speed", toString(myJunctionsHigherSpeed));
     343              :     }
     344         2093 :     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         2093 :     if (oc.isWriteable("junctions.minimal-shape") && oc.getBool("junctions.minimal-shape") != myJunctionsMinimalShape) {
     348            2 :         oc.set("junctions.minimal-shape", toString(myJunctionsMinimalShape));
     349              :     }
     350         2093 :     if (oc.isWriteable("junctions.endpoint-shape") && oc.getBool("junctions.endpoint-shape") != myJunctionsEndpointShape) {
     351            2 :         oc.set("junctions.endpoint-shape", toString(myJunctionsEndpointShape));
     352              :     }
     353          698 :     if (!deprecatedVehicleClassesSeen.empty()) {
     354            2 :         WRITE_WARNINGF(TL("Deprecated vehicle class(es) '%' in input network."), toString(deprecatedVehicleClassesSeen));
     355              :         deprecatedVehicleClassesSeen.clear();
     356              :     }
     357         1396 :     if (!oc.getBool("no-internal-links")) {
     358              :         // add loaded crossings
     359          320 :         for (const auto& crossIt : myPedestrianCrossings) {
     360           60 :             NBNode* const node = myNodeCont.retrieve(crossIt.first);
     361          206 :             for (const Crossing& crossing : crossIt.second) {
     362              :                 EdgeVector edges;
     363          375 :                 for (const std::string& edgeID : crossing.crossingEdges) {
     364          229 :                     NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
     365              :                     // edge might have been removed due to options
     366          229 :                     if (edge != nullptr) {
     367          227 :                         edges.push_back(edge);
     368              :                     }
     369              :                 }
     370          146 :                 if (!edges.empty()) {
     371          145 :                     node->addCrossing(edges, crossing.width, crossing.priority,
     372          145 :                                       crossing.customTLIndex, crossing.customTLIndex2, crossing.customShape, true, &crossing);
     373              :                 }
     374          146 :             }
     375              :         }
     376          260 :         myNetBuilder.setHaveNetworkCrossings(myPedestrianCrossings.size() > 0);
     377              :         // add walking area custom shapes
     378          263 :         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          758 :     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          698 : }
     422              : 
     423              : 
     424              : void
     425       367230 : 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       367230 :     switch (element) {
     442          708 :         case SUMO_TAG_NET: {
     443              :             bool ok;
     444          708 :             myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
     445          708 :             myAmLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
     446          708 :             myCornerDetail = attrs.getOpt<int>(SUMO_ATTR_CORNERDETAIL, nullptr, ok, 0);
     447          708 :             myLinkDetail = attrs.getOpt<int>(SUMO_ATTR_LINKDETAIL, nullptr, ok, -1);
     448          708 :             myRectLaneCut = attrs.getOpt<bool>(SUMO_ATTR_RECTANGULAR_LANE_CUT, nullptr, ok, false);
     449          708 :             myWalkingAreas = attrs.getOpt<bool>(SUMO_ATTR_WALKINGAREAS, nullptr, ok, false);
     450          708 :             myLimitTurnSpeed = attrs.getOpt<double>(SUMO_ATTR_LIMIT_TURN_SPEED, nullptr, ok, -1);
     451          708 :             myCheckLaneFoesAll = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ALL, nullptr, ok, false);
     452          708 :             myCheckLaneFoesRoundabout = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT, nullptr, ok, true);
     453          708 :             myTlsIgnoreInternalJunctionJam = attrs.getOpt<bool>(SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM, nullptr, ok, false);
     454          708 :             myDefaultSpreadType = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, nullptr, ok, myDefaultSpreadType);
     455          708 :             myGeomAvoidOverlap = attrs.getOpt<bool>(SUMO_ATTR_AVOID_OVERLAP, nullptr, ok, myGeomAvoidOverlap);
     456          708 :             myJunctionsHigherSpeed = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, myJunctionsHigherSpeed);
     457          708 :             myInternalJunctionsVehicleWidth = attrs.getOpt<double>(SUMO_ATTR_INTERNAL_JUNCTIONS_VEHICLE_WIDTH, nullptr, ok, myInternalJunctionsVehicleWidth);
     458          708 :             myJunctionsMinimalShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE, nullptr, ok, myJunctionsMinimalShape);
     459          708 :             myJunctionsEndpointShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE, nullptr, ok, myJunctionsEndpointShape);
     460              :             // derived
     461          708 :             const OptionsCont& oc = OptionsCont::getOptions();
     462          711 :             myChangeLefthand = !oc.isDefault("lefthand") && (oc.getBool("lefthand") != myAmLefthand);
     463              : 
     464              :             break;
     465              :         }
     466        65455 :         case SUMO_TAG_EDGE:
     467        65455 :             addEdge(attrs);
     468        65455 :             break;
     469        79695 :         case SUMO_TAG_LANE:
     470        79695 :             addLane(attrs);
     471        79695 :             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        22703 :         case SUMO_TAG_JUNCTION:
     481        22703 :             addJunction(attrs);
     482        22702 :             break;
     483        67864 :         case SUMO_TAG_REQUEST:
     484        67864 :             addRequest(attrs);
     485        67864 :             break;
     486       102314 :         case SUMO_TAG_CONNECTION:
     487       102314 :             addConnection(attrs);
     488       102314 :             break;
     489          749 :         case SUMO_TAG_TLLOGIC:
     490          749 :             myCurrentTL = initTrafficLightLogic(attrs, myCurrentTL);
     491          749 :             if (myCurrentTL) {
     492          749 :                 myLastParameterised.push_back(myCurrentTL);
     493              :             }
     494              :             break;
     495         4529 :         case SUMO_TAG_PHASE:
     496         4529 :             addPhase(attrs, myCurrentTL);
     497         4529 :             break;
     498          708 :         case SUMO_TAG_LOCATION:
     499          708 :             delete myLocation;
     500          708 :             myLocation = loadLocation(attrs);
     501          708 :             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        20344 :         case SUMO_TAG_PARAM:
     509        20344 :             if (myLastParameterised.size() != 0) {
     510        20344 :                 bool ok = true;
     511        20344 :                 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        19723 :                     const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
     515        19723 :                     myLastParameterised.back()->setParameter(key, val);
     516              :                 }
     517              :             }
     518              :             break;
     519         1968 :         default:
     520         1968 :             myTypesHandler.myStartElement(element, attrs);
     521         1968 :             break;
     522              :     }
     523       367229 : }
     524              : 
     525              : 
     526              : void
     527       367083 : NIImporter_SUMO::myEndElement(int element) {
     528       367083 :     switch (element) {
     529        65395 :         case SUMO_TAG_EDGE:
     530        65395 :             if (myCurrentEdge != nullptr) {
     531        65391 :                 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        65344 :                     myEdges[myCurrentEdge->id] = myCurrentEdge;
     539              :                 }
     540        65391 :                 myCurrentEdge = nullptr;
     541              :                 myLastParameterised.pop_back();
     542              :             }
     543              :             break;
     544        79695 :         case SUMO_TAG_LANE:
     545        79695 :             if (myCurrentEdge != nullptr && myCurrentLane != nullptr) {
     546        79679 :                 myCurrentEdge->maxSpeed = MAX2(myCurrentEdge->maxSpeed, myCurrentLane->maxSpeed);
     547        79679 :                 myCurrentEdge->lanes.push_back(myCurrentLane);
     548              :                 myLastParameterised.pop_back();
     549              :             }
     550        79695 :             myCurrentLane = nullptr;
     551        79695 :             break;
     552          749 :         case SUMO_TAG_TLLOGIC:
     553          749 :             if (myCurrentTL == nullptr) {
     554            0 :                 WRITE_ERROR(TL("Unmatched closing tag for tl-logic."));
     555              :             } else {
     556          749 :                 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          749 :                 myCurrentTL = nullptr;
     561              :                 myLastParameterised.pop_back();
     562              :             }
     563              :             break;
     564        22702 :         case SUMO_TAG_JUNCTION:
     565        22702 :             if (myCurrentJunction.node != nullptr) {
     566              :                 myLastParameterised.pop_back();
     567              :             }
     568              :             break;
     569       102314 :         case SUMO_TAG_CONNECTION:
     570              :             // !!! this just avoids a crash but is not a real check that it was a connection
     571       102314 :             if (!myLastParameterised.empty()) {
     572              :                 myLastParameterised.pop_back();
     573              :             }
     574              :             break;
     575              :         default:
     576              :             break;
     577              :     }
     578       367083 : }
     579              : 
     580              : 
     581              : void
     582        65455 : NIImporter_SUMO::addEdge(const SUMOSAXAttributes& attrs) {
     583              :     // get the id, report an error if not given or empty...
     584        65455 :     bool ok = true;
     585        65455 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     586        65455 :     if (!ok) {
     587              :         return;
     588              :     }
     589        65451 :     myCurrentEdge = new EdgeAttrs();
     590        65451 :     myLastParameterised.push_back(myCurrentEdge);
     591        65451 :     myCurrentEdge->builtEdge = nullptr;
     592        65451 :     myCurrentEdge->id = id;
     593              :     // get the function
     594        65451 :     myCurrentEdge->func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
     595        65451 :     if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
     596              :         // add the crossing but don't do anything else
     597          150 :         Crossing c(id);
     598          150 :         c.crossingEdges = attrs.get<std::vector<std::string> >(SUMO_ATTR_CROSSING_EDGES, nullptr, ok);
     599          300 :         myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(id)].push_back(c);
     600              :         return;
     601        65451 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL || myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
     602        30207 :         myHaveSeenInternalEdge = true;
     603        30207 :         return; // skip internal edges
     604              :     }
     605              :     // get the origin and the destination node
     606        35094 :     myCurrentEdge->fromNode = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id.c_str(), ok, "");
     607        70188 :     myCurrentEdge->toNode = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
     608        35094 :     myCurrentEdge->priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1);
     609        35094 :     myCurrentEdge->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     610        35094 :     myCurrentEdge->routingType = attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, id.c_str(), ok, "");
     611        35094 :     myCurrentEdge->shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
     612        35094 :     NBNetBuilder::transformCoordinates(myCurrentEdge->shape, true, myLocation);
     613        35094 :     myCurrentEdge->length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
     614        35094 :     myCurrentEdge->maxSpeed = 0;
     615        70188 :     myCurrentEdge->streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     616        35094 :     myCurrentEdge->distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
     617        35094 :     myCurrentEdge->bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
     618        53881 :     if (myCurrentEdge->streetName != "" && OptionsCont::getOptions().isDefault("output.street-names")) {
     619          166 :         OptionsCont::getOptions().set("output.street-names", "true");
     620              :     }
     621              : 
     622        70188 :     std::string lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, id.c_str(), ok, myDefaultSpreadType);
     623              :     if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
     624        35094 :         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        79695 : NIImporter_SUMO::addLane(const SUMOSAXAttributes& attrs) {
     633        79695 :     bool ok = true;
     634        79695 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     635        79695 :     if (!ok) {
     636              :         return;
     637              :     }
     638        79687 :     if (myCurrentEdge == nullptr) {
     639           24 :         WRITE_ERRORF(TL("Found lane '%' not within edge element."), id);
     640            8 :         return;
     641              :     }
     642       159358 :     const std::string expectedID = myCurrentEdge->id + "_" + toString(myCurrentEdge->lanes.size());
     643        79679 :     if (id != expectedID) {
     644          108 :         WRITE_WARNINGF(TL("Renaming lane '%' to '%'."), id, expectedID);
     645              :     }
     646        79679 :     myCurrentLane = new LaneAttrs();
     647        79679 :     myLastParameterised.push_back(myCurrentLane);
     648        79679 :     myCurrentLane->customShape = attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, nullptr, ok, false);
     649        79679 :     myCurrentLane->shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
     650        79679 :     myCurrentLane->width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_WIDTH);
     651        79679 :     myCurrentLane->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     652        79679 :     if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
     653              :         // save the width and the lane id of the crossing but don't do anything else
     654          450 :         std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(myCurrentEdge->id)];
     655              :         assert(crossings.size() > 0);
     656          150 :         crossings.back().width = attrs.get<double>(SUMO_ATTR_WIDTH, id.c_str(), ok);
     657              :         myLastParameterised.pop_back();
     658          150 :         myLastParameterised.push_back(&crossings.back());
     659          150 :         if (myCurrentLane->customShape) {
     660              :             crossings.back().customShape = myCurrentLane->shape;
     661           11 :             NBNetBuilder::transformCoordinates(crossings.back().customShape, true, myLocation);
     662              :         }
     663        79529 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
     664              :         // save custom shape if needed but don't do anything else
     665          348 :         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          348 :         return;
     673        79181 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL) {
     674              :         return; // skip internal edges
     675              :     }
     676        91480 :     if (attrs.hasAttribute("maxspeed")) {
     677              :         // !!! deprecated
     678            0 :         myCurrentLane->maxSpeed = attrs.getFloat("maxspeed");
     679              :     } else {
     680        45740 :         myCurrentLane->maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
     681              :     }
     682        45740 :     myCurrentLane->friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, NBEdge::UNSPECIFIED_FRICTION, false); //sets 1 on empty
     683              :     try {
     684        91480 :         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        45740 :     myCurrentLane->disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
     690        45740 :     myCurrentLane->endOffset = attrs.getOpt<double>(SUMO_ATTR_ENDOFFSET, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_OFFSET);
     691        45740 :     myCurrentLane->accelRamp = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
     692        45740 :     myCurrentLane->changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
     693        45740 :     myCurrentLane->changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
     694        45740 :     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        45740 :     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        22703 : NIImporter_SUMO::addJunction(const SUMOSAXAttributes& attrs) {
     728              :     // get the id, report an error if not given or empty...
     729        22703 :     myCurrentJunction.node = nullptr;
     730              :     myCurrentJunction.response.clear();
     731        22703 :     bool ok = true;
     732        22703 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     733        22703 :     if (!ok) {
     734              :         return;
     735              :     }
     736        22697 :     if (id[0] == ':') { // internal node
     737              :         return;
     738              :     }
     739        20050 :     SumoXMLNodeType type = attrs.getOpt<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok, SumoXMLNodeType::UNKNOWN);
     740        20050 :     if (ok) {
     741        20044 :         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        17554 :         } 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        20050 :     Position pos = readPosition(attrs, id, ok);
     751        20050 :     NBNetBuilder::transformCoordinate(pos, true, myLocation);
     752        20050 :     NBNode* node = new NBNode(id, pos, type);
     753        20049 :     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        20027 :         myLastParameterised.push_back(node);
     760              :     }
     761        20027 :     myCurrentJunction.node = node;
     762              :     // set optional radius
     763        20027 :     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        20027 :     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        20027 :     if (type == SumoXMLNodeType::RAIL_SIGNAL || type == SumoXMLNodeType::RAIL_CROSSING) {
     773              :         // both types of nodes come without a tlLogic
     774              :         myRailSignals.insert(id);
     775              :     }
     776        20027 :     node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, id.c_str(), ok, node->getRightOfWay()));
     777        20027 :     node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, id.c_str(), ok, node->getFringeType()));
     778        20027 :     node->setRoundaboutType(attrs.getOpt<RoundaboutType>(SUMO_ATTR_ROUNDABOUT, id.c_str(), ok, node->getRoundaboutType()));
     779        20027 :     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        67864 : NIImporter_SUMO::addRequest(const SUMOSAXAttributes& attrs) {
     787        67864 :     if (myCurrentJunction.node != nullptr) {
     788        67794 :         bool ok = true;
     789       135588 :         myCurrentJunction.response.push_back(attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok));
     790              :     }
     791        67864 : }
     792              : 
     793              : 
     794              : void
     795       102314 : NIImporter_SUMO::addConnection(const SUMOSAXAttributes& attrs) {
     796       102314 :     bool ok = true;
     797       102314 :     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       102292 :     EdgeAttrs* from = myEdges[fromID];
     803       102292 :     if (from->func == SumoXMLEdgeFunc::INTERNAL) {
     804              :         // internal junction connection
     805              :         return;
     806              :     }
     807              : 
     808        68733 :     Connection conn;
     809        68733 :     conn.toEdgeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
     810        68733 :     int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
     811        68733 :     conn.toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
     812        68733 :     conn.tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
     813        68733 :     conn.mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, false);
     814        68733 :     conn.keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
     815        68733 :     conn.indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
     816        68733 :     conn.edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
     817        68733 :     double contPos = NBEdge::UNSPECIFIED_CONTPOS;
     818       137466 :     if (OptionsCont::getOptions().isSet("default.connection.cont-pos")) {
     819       137466 :         contPos = OptionsCont::getOptions().getFloat("default.connection.cont-pos");
     820              :     }
     821        68733 :     conn.contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, contPos);
     822        68733 :     conn.visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE);
     823        68733 :     std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "", false);
     824        68733 :     std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "", false);
     825        68733 :     if (allow == "" && disallow == "") {
     826        68731 :         conn.permissions = SVC_UNSPECIFIED;
     827              :     } else {
     828            2 :         conn.permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
     829              :     }
     830        68733 :     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        68732 :         conn.changeLeft = SVC_UNSPECIFIED;
     834              :     }
     835        68733 :     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        68733 :         conn.changeRight = SVC_UNSPECIFIED;
     839              :     }
     840        68733 :     if (myChangeLefthand) {
     841              :         std::swap(conn.changeLeft, conn.changeRight);
     842              :     }
     843        68733 :     conn.speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, NBEdge::UNSPECIFIED_SPEED);
     844        68733 :     conn.friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, NBEdge::UNSPECIFIED_FRICTION);
     845        68733 :     conn.customLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
     846       137466 :     conn.customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
     847        68733 :     NBNetBuilder::transformCoordinates(conn.customShape, false, myLocation);
     848        68733 :     conn.uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED, false);
     849        68733 :     if (conn.tlID != "") {
     850         8182 :         conn.tlLinkIndex = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
     851         8182 :         conn.tlLinkIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
     852              :     } else {
     853        60551 :         conn.tlLinkIndex = NBConnection::InvalidTlIndex;
     854              :     }
     855        68733 :     if ((int)from->lanes.size() <= fromLaneIdx) {
     856           24 :         WRITE_ERRORF(TL("Invalid lane index '%' for connection from '%'."), toString(fromLaneIdx), fromID);
     857              :         return;
     858              :     }
     859        68725 :     from->lanes[fromLaneIdx]->connections.push_back(conn);
     860        68725 :     myLastParameterised.push_back(&from->lanes[fromLaneIdx]->connections.back());
     861              : 
     862              :     // determine crossing priority and tlIndex
     863        68725 :     if (myPedestrianCrossings.size() > 0) {
     864         1682 :         if (from->func == SumoXMLEdgeFunc::WALKINGAREA && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::CROSSING) {
     865              :             // connection from walkingArea to crossing
     866          453 :             std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)];
     867          732 :             for (std::vector<Crossing>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
     868          581 :                 if (conn.toEdgeID == (*it).edgeID) {
     869          151 :                     if (conn.tlID != "") {
     870          105 :                         (*it).priority = true;
     871          105 :                         (*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         1531 :         } else if (from->func == SumoXMLEdgeFunc::CROSSING && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::WALKINGAREA) {
     879              :             // connection from crossing to walkingArea (set optional linkIndex2)
     880         1026 :             for (Crossing& c : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
     881          576 :                 if (fromID == c.edgeID) {
     882          150 :                     c.customTLIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
     883              :                 }
     884              :             }
     885              :         }
     886              :     }
     887              :     // determine walking area reference edges
     888        68725 :     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        68733 : }
     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          749 : NIImporter_SUMO::initTrafficLightLogic(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
     945          749 :     if (currentTL) {
     946            0 :         WRITE_ERRORF(TL("Definition of tl-logic '%' was not finished."), currentTL->getID());
     947            0 :         return nullptr;
     948              :     }
     949          749 :     bool ok = true;
     950          749 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     951          749 :     SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
     952          749 :     std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
     953          749 :     std::string typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
     954              :     TrafficLightType type;
     955              :     if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
     956          749 :         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          749 :     if (ok) {
     962          749 :         return new NBLoadedSUMOTLDef(id, programID, offset, type);
     963              :     } else {
     964              :         return nullptr;
     965              :     }
     966              : }
     967              : 
     968              : 
     969              : void
     970         4773 : NIImporter_SUMO::addPhase(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
     971         4773 :     if (!currentTL) {
     972            0 :         WRITE_ERROR(TL("found phase without tl-logic"));
     973            0 :         return;
     974              :     }
     975              :     const std::string& id = currentTL->getID();
     976         4773 :     bool ok = true;
     977         4773 :     std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, id.c_str(), ok);
     978         4773 :     SUMOTime duration = TIME2STEPS(attrs.get<double>(SUMO_ATTR_DURATION, id.c_str(), ok));
     979         4773 :     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         4774 :     std::vector<int> nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
     985         4773 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, nullptr, ok);
     986              :     // Specific from actuated
     987         4773 :     const SUMOTime minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     988         4773 :     const SUMOTime maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     989         4773 :     const SUMOTime earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     990         4773 :     const SUMOTime latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     991              :     // specific von NEMA
     992         4773 :     const SUMOTime vehExt = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     993         4773 :     const SUMOTime yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     994         4773 :     const SUMOTime red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     995         4773 :     if (ok) {
     996         4773 :         currentTL->addPhase(duration, state, minDuration, maxDuration, earliestEnd, latestEnd, vehExt, yellow, red, nextPhases, name);
     997              :     }
     998         4773 : }
     999              : 
    1000              : 
    1001              : GeoConvHelper*
    1002         1300 : NIImporter_SUMO::loadLocation(const SUMOSAXAttributes& attrs, bool setLoaded) {
    1003              :     // @todo refactor parsing of location since its duplicated in NLHandler and PCNetProjectionLoader
    1004         1300 :     bool ok = true;
    1005              :     GeoConvHelper* result = nullptr;
    1006         1300 :     const Position offset = attrs.get<Position>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
    1007         1300 :     const Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
    1008         1300 :     const Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
    1009         1300 :     const std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
    1010         1300 :     if (ok) {
    1011         1300 :         result = new GeoConvHelper(proj, offset, origBoundary, convBoundary);
    1012         1300 :         result->resolveAbstractProjection();
    1013         1300 :         if (setLoaded) {
    1014         1274 :             GeoConvHelper::setLoaded(*result);
    1015              :         }
    1016              :     }
    1017         1300 :     return result;
    1018              : }
    1019              : 
    1020              : 
    1021              : Position
    1022        20050 : NIImporter_SUMO::readPosition(const SUMOSAXAttributes& attrs, const std::string& id, bool& ok) {
    1023        20050 :     const double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
    1024        20050 :     const double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
    1025        20050 :     const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0.);
    1026        20050 :     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