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

Generated by: LCOV version 2.0-1