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

Generated by: LCOV version 2.0-1