LCOV - code coverage report
Current view: top level - src/netimport - NIImporter_SUMO.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 93.8 % 625 586
Test Date: 2024-12-21 15:45:41 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         1892 : NIImporter_SUMO::loadNetwork(OptionsCont& oc, NBNetBuilder& nb) {
      57         1892 :     NIImporter_SUMO importer(nb);
      58         1892 :     importer._loadNetwork(oc);
      59         1892 : }
      60              : 
      61              : 
      62              : // ---------------------------------------------------------------------------
      63              : // loader methods
      64              : // ---------------------------------------------------------------------------
      65         1892 : NIImporter_SUMO::NIImporter_SUMO(NBNetBuilder& nb)
      66              :     : SUMOSAXHandler("sumo-network"),
      67         1892 :       myNetBuilder(nb),
      68         1892 :       myNodeCont(nb.getNodeCont()),
      69         1892 :       myTLLCont(nb.getTLLogicCont()),
      70         1892 :       myTypesHandler(nb.getTypeCont()),
      71         1892 :       myCurrentEdge(nullptr),
      72         1892 :       myCurrentLane(nullptr),
      73         1892 :       myCurrentTL(nullptr),
      74         1892 :       myLocation(nullptr),
      75              :       myNetworkVersion(0, 0),
      76         1892 :       myHaveSeenInternalEdge(false),
      77         1892 :       myAmLefthand(false),
      78         1892 :       myChangeLefthand(false),
      79         1892 :       myCornerDetail(0),
      80         1892 :       myLinkDetail(-1),
      81         1892 :       myRectLaneCut(false),
      82         1892 :       myWalkingAreas(false),
      83         1892 :       myLimitTurnSpeed(-1),
      84         1892 :       myCheckLaneFoesAll(false),
      85         1892 :       myCheckLaneFoesRoundabout(true),
      86         1892 :       myTlsIgnoreInternalJunctionJam(false),
      87         1892 :       myDefaultSpreadType(toString(LaneSpreadFunction::RIGHT)),
      88         1892 :       myGeomAvoidOverlap(true),
      89         1892 :       myJunctionsHigherSpeed(false),
      90         1892 :       myInternalJunctionsVehicleWidth(OptionsCont::getOptions().getFloat("internal-junctions.vehicle-width")),
      91         1892 :       myJunctionsMinimalShape(OptionsCont::getOptions().getBool("junctions.minimal-shape")),
      92         5676 :       myJunctionsEndpointShape(OptionsCont::getOptions().getBool("junctions.endpoint-shape")) {
      93         1892 : }
      94              : 
      95              : 
      96         1892 : NIImporter_SUMO::~NIImporter_SUMO() {
      97        46286 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
      98        44394 :         EdgeAttrs* ed = (*i).second;
      99        99009 :         for (std::vector<LaneAttrs*>::const_iterator j = ed->lanes.begin(); j != ed->lanes.end(); ++j) {
     100        54615 :             delete *j;
     101              :         }
     102              :         delete ed;
     103              :     }
     104         1892 :     delete myLocation;
     105         5676 : }
     106              : 
     107              : 
     108              : void
     109         1892 : NIImporter_SUMO::_loadNetwork(OptionsCont& oc) {
     110              :     // check whether the option is set (properly)
     111         3784 :     if (!oc.isUsableFileList("sumo-net-file")) {
     112         1258 :         return;
     113              :     }
     114         1268 :     const std::vector<std::string> discardableParams = oc.getStringVector("discard-params");
     115              :     myDiscardableParams.insert(discardableParams.begin(), discardableParams.end());
     116              :     // parse file(s)
     117         1268 :     const std::vector<std::string> files = oc.getStringVector("sumo-net-file");
     118         1276 :     for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
     119         1284 :         if (!FileHelpers::isReadable(*file)) {
     120            0 :             WRITE_ERRORF(TL("Could not open sumo-net-file '%'."), *file);
     121              :             return;
     122              :         }
     123          642 :         setFileName(*file);
     124         1926 :         const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing sumo-net from '" + *file + "'");
     125          642 :         XMLSubSys::runParser(*this, *file, true);
     126          642 :         PROGRESS_TIME_MESSAGE(before);
     127              :     }
     128              :     // build edges
     129         1268 :     const double maxSegmentLength = oc.getFloat("geometry.max-segment-length");
     130        45028 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     131        44394 :         EdgeAttrs* ed = (*i).second;
     132              :         // skip internal edges
     133        44394 :         if (ed->func == SumoXMLEdgeFunc::INTERNAL || ed->func == SumoXMLEdgeFunc::CROSSING || ed->func == SumoXMLEdgeFunc::WALKINGAREA) {
     134        21145 :             continue;
     135              :         }
     136              :         // get and check the nodes
     137        23249 :         NBNode* from = myNodeCont.retrieve(ed->fromNode);
     138        23249 :         NBNode* to = myNodeCont.retrieve(ed->toNode);
     139        23249 :         if (from == nullptr) {
     140          216 :             WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), ed->id, ed->fromNode);
     141           72 :             continue;
     142              :         }
     143        23177 :         if (to == nullptr) {
     144          102 :             WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), ed->id, ed->toNode);
     145           34 :             continue;
     146              :         }
     147        23143 :         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        23143 :         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        23143 :         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        69429 :                                ed->shape, ed->lsf, ed->streetName, "", true); // always use tryIgnoreNodePositions to keep original shape
     163        23143 :         e->setLoadedLength(ed->length);
     164        23143 :         e->updateParameters(ed->getParametersMap());
     165        23143 :         e->setDistance(ed->distance);
     166        23143 :         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        23143 :         ed->builtEdge = myNetBuilder.getEdgeCont().retrieve(ed->id);
     172        23143 :         if (ed->builtEdge != nullptr) {
     173        22973 :             ed->builtEdge->setEdgeStopOffset(-1, ed->edgeStopOffset);
     174        22973 :             ed->builtEdge->setBidi(ed->bidi != "");
     175              :         }
     176              :     }
     177              :     // assign further lane attributes (edges are built)
     178              :     EdgeVector toRemove;
     179         1268 :     const bool dismissVclasses = oc.getBool("dismiss-vclasses");
     180        45028 :     for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     181        44394 :         EdgeAttrs* ed = (*i).second;
     182        44394 :         NBEdge* nbe = ed->builtEdge;
     183        44394 :         if (nbe == nullptr) { // inner edge or removed by explicit list, vclass, ...
     184        21421 :             continue;
     185              :         }
     186              :         const SumoXMLNodeType toType = nbe->getToNode()->getType();
     187        53602 :         for (int fromLaneIndex = 0; fromLaneIndex < (int) ed->lanes.size(); ++fromLaneIndex) {
     188        30629 :             LaneAttrs* lane = ed->lanes[fromLaneIndex];
     189              :             // connections
     190              :             const std::vector<Connection>& connections = lane->connections;
     191        76828 :             for (const Connection& c : connections) {
     192        46199 :                 if (myEdges.count(c.toEdgeID) == 0) {
     193           66 :                     WRITE_ERRORF(TL("Unknown edge '%' given in connection."), c.toEdgeID);
     194           22 :                     continue;
     195              :                 }
     196        46177 :                 NBEdge* toEdge = myEdges[c.toEdgeID]->builtEdge;
     197        46177 :                 if (toEdge == nullptr) { // removed by explicit list, vclass, ...
     198          411 :                     continue;
     199              :                 }
     200        45766 :                 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        45745 :                 bool uncontrolled = c.uncontrolled;
     206              : 
     207        86293 :                 if ((NBNode::isTrafficLight(toType) || toType == SumoXMLNodeType::RAIL_SIGNAL)
     208        46412 :                         && c.tlLinkIndex == NBConnection::InvalidTlIndex) {
     209              :                     uncontrolled = true;
     210              :                 }
     211        91490 :                 nbe->addLane2LaneConnection(
     212        45745 :                     fromLaneIndex, toEdge, c.toLaneIdx, NBEdge::Lane2LaneInfoType::VALIDATED,
     213        45745 :                     true, c.mayDefinitelyPass, c.keepClear ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE,
     214        45745 :                     c.contPos, c.visibility, c.speed, c.friction, c.customLength, c.customShape, uncontrolled, c.permissions, c.indirectLeft, c.edgeType, c.changeLeft, c.changeRight);
     215        45745 :                 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        45745 :                 if (c.tlID != "" && myRailSignals.count(c.tlID) == 0) {
     220         5183 :                     const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(c.tlID);
     221         5183 :                     if (programs.size() > 0) {
     222              :                         std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
     223        10384 :                         for (it = programs.begin(); it != programs.end(); it++) {
     224         5201 :                             NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
     225         5201 :                             if (tlDef) {
     226         5201 :                                 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        30629 :             if (!dismissVclasses) {
     238        30623 :                 nbe->setPermissions(parseVehicleClasses(lane->allow, lane->disallow, myNetworkVersion), fromLaneIndex);
     239              :             }
     240        61258 :             nbe->setPermittedChanging(fromLaneIndex, parseVehicleClasses(lane->changeLeft, ""), parseVehicleClasses(lane->changeRight, ""));
     241              :             // width, offset
     242        30629 :             nbe->setLaneWidth(fromLaneIndex, lane->width);
     243        30629 :             nbe->setEndOffset(fromLaneIndex, lane->endOffset);
     244        30629 :             nbe->setSpeed(fromLaneIndex, lane->maxSpeed);
     245        30629 :             nbe->setFriction(fromLaneIndex, lane->friction);
     246        30629 :             nbe->setAcceleration(fromLaneIndex, lane->accelRamp);
     247        30629 :             nbe->getLaneStruct(fromLaneIndex).oppositeID = lane->oppositeID;
     248        30629 :             nbe->getLaneStruct(fromLaneIndex).type = lane->type;
     249        30629 :             nbe->getLaneStruct(fromLaneIndex).updateParameters(lane->getParametersMap());
     250        30629 :             if (lane->customShape) {
     251            5 :                 nbe->setLaneShape(fromLaneIndex, lane->shape);
     252              :             }
     253              :             // stop offset for lane
     254              :             bool stopOffsetSet = false;
     255        30629 :             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        30603 :                 nbe->setEdgeStopOffset(fromLaneIndex, nbe->getEdgeStopOffset());
     262              :             }
     263              :         }
     264        22973 :         nbe->declareConnectionsAsLoaded();
     265        22973 :         if (!nbe->hasLaneSpecificWidth() && nbe->getLanes()[0].width != NBEdge::UNSPECIFIED_WIDTH) {
     266          326 :             nbe->setLaneWidth(-1, nbe->getLaneWidth(0));
     267              :         }
     268        22973 :         if (!nbe->hasLaneSpecificEndOffset() && nbe->getEndOffset(0) != NBEdge::UNSPECIFIED_OFFSET) {
     269            0 :             nbe->setEndOffset(-1, nbe->getEndOffset(0));
     270              :         }
     271        22973 :         if (!nbe->hasLaneSpecificStopOffsets() && nbe->getEdgeStopOffset().isDefined()) {
     272            4 :             nbe->setEdgeStopOffset(-1, nbe->getEdgeStopOffset());
     273              :         }
     274              :         // check again after permissions are set
     275        22973 :         if (myNetBuilder.getEdgeCont().ignoreFilterMatch(nbe)) {
     276           11 :             myNetBuilder.getEdgeCont().ignore(nbe->getID());
     277           11 :             toRemove.push_back(nbe);
     278              :         }
     279              :     }
     280          645 :     for (EdgeVector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) {
     281           11 :         myNetBuilder.getEdgeCont().erase(myNetBuilder.getDistrictCont(), *i);
     282              :     }
     283              :     // insert loaded prohibitions
     284          654 :     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         1024 :     if (!myHaveSeenInternalEdge && oc.isWriteable("no-internal-links")) {
     305          740 :         oc.set("no-internal-links", "true");
     306              :     }
     307         1268 :     if (oc.isWriteable("lefthand")) {
     308         1262 :         oc.set("lefthand", toString(myAmLefthand));
     309              :     }
     310         1268 :     if (oc.isWriteable("junctions.corner-detail")) {
     311         1244 :         oc.set("junctions.corner-detail", toString(myCornerDetail));
     312              :     }
     313         1268 :     if (oc.isWriteable("junctions.internal-link-detail") && myLinkDetail > 0) {
     314            0 :         oc.set("junctions.internal-link-detail", toString(myLinkDetail));
     315              :     }
     316         1268 :     if (oc.isWriteable("rectangular-lane-cut")) {
     317         1236 :         oc.set("rectangular-lane-cut", toString(myRectLaneCut));
     318              :     }
     319         1268 :     if (oc.isWriteable("walkingareas")) {
     320         1268 :         oc.set("walkingareas", toString(myWalkingAreas));
     321              :     }
     322         1268 :     if (oc.isWriteable("junctions.limit-turn-speed")) {
     323         1266 :         oc.set("junctions.limit-turn-speed", toString(myLimitTurnSpeed));
     324              :     }
     325         1901 :     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         1902 :     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         1901 :     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         2536 :     if (oc.isWriteable("default.spreadtype") && oc.getString("default.spreadtype") != myDefaultSpreadType) {
     335            0 :         oc.set("default.spreadtype", myDefaultSpreadType);
     336              :     }
     337         1876 :     if (oc.isWriteable("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap") != myGeomAvoidOverlap) {
     338            4 :         oc.set("geometry.avoid-overlap", toString(myGeomAvoidOverlap));
     339              :     }
     340         1902 :     if (oc.isWriteable("junctions.higher-speed") && oc.getBool("junctions.higher-speed") != myJunctionsHigherSpeed) {
     341            0 :         oc.set("junctions.higher-speed", toString(myJunctionsHigherSpeed));
     342              :     }
     343         1901 :     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         1901 :     if (oc.isWriteable("junctions.minimal-shape") && oc.getBool("junctions.minimal-shape") != myJunctionsMinimalShape) {
     347            2 :         oc.set("junctions.minimal-shape", toString(myJunctionsMinimalShape));
     348              :     }
     349         1901 :     if (oc.isWriteable("junctions.endpoint-shape") && oc.getBool("junctions.endpoint-shape") != myJunctionsEndpointShape) {
     350            2 :         oc.set("junctions.endpoint-shape", toString(myJunctionsEndpointShape));
     351              :     }
     352          634 :     if (!deprecatedVehicleClassesSeen.empty()) {
     353            2 :         WRITE_WARNINGF(TL("Deprecated vehicle class(es) '%' in input network."), toString(deprecatedVehicleClassesSeen));
     354              :         deprecatedVehicleClassesSeen.clear();
     355              :     }
     356         1268 :     if (!oc.getBool("no-internal-links")) {
     357              :         // add loaded crossings
     358          299 :         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          243 :         myNetBuilder.setHaveNetworkCrossings(myPedestrianCrossings.size() > 0);
     376              :         // add walking area custom shapes
     377          246 :         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          678 :     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          634 : }
     421              : 
     422              : 
     423              : void
     424       252568 : 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       252568 :     switch (element) {
     441          642 :         case SUMO_TAG_NET: {
     442              :             bool ok;
     443          642 :             myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
     444          642 :             myAmLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
     445          642 :             myCornerDetail = attrs.getOpt<int>(SUMO_ATTR_CORNERDETAIL, nullptr, ok, 0);
     446          642 :             myLinkDetail = attrs.getOpt<int>(SUMO_ATTR_LINKDETAIL, nullptr, ok, -1);
     447          642 :             myRectLaneCut = attrs.getOpt<bool>(SUMO_ATTR_RECTANGULAR_LANE_CUT, nullptr, ok, false);
     448          642 :             myWalkingAreas = attrs.getOpt<bool>(SUMO_ATTR_WALKINGAREAS, nullptr, ok, false);
     449          642 :             myLimitTurnSpeed = attrs.getOpt<double>(SUMO_ATTR_LIMIT_TURN_SPEED, nullptr, ok, -1);
     450          642 :             myCheckLaneFoesAll = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ALL, nullptr, ok, false);
     451          642 :             myCheckLaneFoesRoundabout = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT, nullptr, ok, true);
     452          642 :             myTlsIgnoreInternalJunctionJam = attrs.getOpt<bool>(SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM, nullptr, ok, false);
     453          642 :             myDefaultSpreadType = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, nullptr, ok, myDefaultSpreadType);
     454          642 :             myGeomAvoidOverlap = attrs.getOpt<bool>(SUMO_ATTR_AVOID_OVERLAP, nullptr, ok, myGeomAvoidOverlap);
     455          642 :             myJunctionsHigherSpeed = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, myJunctionsHigherSpeed);
     456          642 :             myInternalJunctionsVehicleWidth = attrs.getOpt<double>(SUMO_ATTR_INTERNAL_JUNCTIONS_VEHICLE_WIDTH, nullptr, ok, myInternalJunctionsVehicleWidth);
     457          642 :             myJunctionsMinimalShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE, nullptr, ok, myJunctionsMinimalShape);
     458          642 :             myJunctionsEndpointShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE, nullptr, ok, myJunctionsEndpointShape);
     459              :             // derived
     460          642 :             const OptionsCont& oc = OptionsCont::getOptions();
     461          645 :             myChangeLefthand = !oc.isDefault("lefthand") && (oc.getBool("lefthand") != myAmLefthand);
     462              : 
     463              :             break;
     464              :         }
     465        44505 :         case SUMO_TAG_EDGE:
     466        44505 :             addEdge(attrs);
     467        44505 :             break;
     468        54722 :         case SUMO_TAG_LANE:
     469        54722 :             addLane(attrs);
     470        54722 :             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        15982 :         case SUMO_TAG_JUNCTION:
     480        15982 :             addJunction(attrs);
     481        15982 :             break;
     482        46462 :         case SUMO_TAG_REQUEST:
     483        46462 :             addRequest(attrs);
     484        46462 :             break;
     485        70465 :         case SUMO_TAG_CONNECTION:
     486        70465 :             addConnection(attrs);
     487        70465 :             break;
     488          527 :         case SUMO_TAG_TLLOGIC:
     489          527 :             myCurrentTL = initTrafficLightLogic(attrs, myCurrentTL);
     490          527 :             if (myCurrentTL) {
     491          527 :                 myLastParameterised.push_back(myCurrentTL);
     492              :             }
     493              :             break;
     494         3459 :         case SUMO_TAG_PHASE:
     495         3459 :             addPhase(attrs, myCurrentTL);
     496         3459 :             break;
     497          642 :         case SUMO_TAG_LOCATION:
     498          642 :             delete myLocation;
     499          642 :             myLocation = loadLocation(attrs);
     500          642 :             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       252568 : }
     523              : 
     524              : 
     525              : void
     526       252423 : NIImporter_SUMO::myEndElement(int element) {
     527       252423 :     switch (element) {
     528        44445 :         case SUMO_TAG_EDGE:
     529        44445 :             if (myCurrentEdge != nullptr) {
     530        44441 :                 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        44394 :                     myEdges[myCurrentEdge->id] = myCurrentEdge;
     538              :                 }
     539        44441 :                 myCurrentEdge = nullptr;
     540              :                 myLastParameterised.pop_back();
     541              :             }
     542              :             break;
     543        54722 :         case SUMO_TAG_LANE:
     544        54722 :             if (myCurrentEdge != nullptr && myCurrentLane != nullptr) {
     545        54706 :                 myCurrentEdge->maxSpeed = MAX2(myCurrentEdge->maxSpeed, myCurrentLane->maxSpeed);
     546        54706 :                 myCurrentEdge->lanes.push_back(myCurrentLane);
     547              :                 myLastParameterised.pop_back();
     548              :             }
     549        54722 :             myCurrentLane = nullptr;
     550        54722 :             break;
     551          527 :         case SUMO_TAG_TLLOGIC:
     552          527 :             if (myCurrentTL == nullptr) {
     553            0 :                 WRITE_ERROR(TL("Unmatched closing tag for tl-logic."));
     554              :             } else {
     555          527 :                 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          527 :                 myCurrentTL = nullptr;
     560              :                 myLastParameterised.pop_back();
     561              :             }
     562              :             break;
     563        15982 :         case SUMO_TAG_JUNCTION:
     564        15982 :             if (myCurrentJunction.node != nullptr) {
     565              :                 myLastParameterised.pop_back();
     566              :             }
     567              :             break;
     568        70465 :         case SUMO_TAG_CONNECTION:
     569              :             // !!! this just avoids a crash but is not a real check that it was a connection
     570        70465 :             if (!myLastParameterised.empty()) {
     571              :                 myLastParameterised.pop_back();
     572              :             }
     573              :             break;
     574              :         default:
     575              :             break;
     576              :     }
     577       252423 : }
     578              : 
     579              : 
     580              : void
     581        44505 : NIImporter_SUMO::addEdge(const SUMOSAXAttributes& attrs) {
     582              :     // get the id, report an error if not given or empty...
     583        44505 :     bool ok = true;
     584        44505 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     585        44505 :     if (!ok) {
     586              :         return;
     587              :     }
     588        44501 :     myCurrentEdge = new EdgeAttrs();
     589        44501 :     myLastParameterised.push_back(myCurrentEdge);
     590        44501 :     myCurrentEdge->builtEdge = nullptr;
     591        44501 :     myCurrentEdge->id = id;
     592              :     // get the function
     593        44501 :     myCurrentEdge->func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
     594        44501 :     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        44501 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL || myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
     601        21015 :         myHaveSeenInternalEdge = true;
     602        21015 :         return; // skip internal edges
     603              :     }
     604              :     // get the type
     605        23341 :     myCurrentEdge->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     606              :     // get the origin and the destination node
     607        23341 :     myCurrentEdge->fromNode = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id.c_str(), ok, "");
     608        46682 :     myCurrentEdge->toNode = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
     609        23341 :     myCurrentEdge->priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1);
     610        23341 :     myCurrentEdge->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     611        23341 :     myCurrentEdge->shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
     612        23341 :     NBNetBuilder::transformCoordinates(myCurrentEdge->shape, true, myLocation);
     613        23341 :     myCurrentEdge->length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
     614        23341 :     myCurrentEdge->maxSpeed = 0;
     615        46682 :     myCurrentEdge->streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     616        23341 :     myCurrentEdge->distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
     617        23341 :     myCurrentEdge->bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
     618        35237 :     if (myCurrentEdge->streetName != "" && OptionsCont::getOptions().isDefault("output.street-names")) {
     619          110 :         OptionsCont::getOptions().set("output.street-names", "true");
     620              :     }
     621              : 
     622        46682 :     std::string lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, id.c_str(), ok, myDefaultSpreadType);
     623              :     if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
     624        23341 :         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        54722 : NIImporter_SUMO::addLane(const SUMOSAXAttributes& attrs) {
     633        54722 :     bool ok = true;
     634        54722 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     635        54722 :     if (!ok) {
     636              :         return;
     637              :     }
     638        54714 :     if (myCurrentEdge == nullptr) {
     639           24 :         WRITE_ERRORF(TL("Found lane '%' not within edge element."), id);
     640            8 :         return;
     641              :     }
     642       109412 :     const std::string expectedID = myCurrentEdge->id + "_" + toString(myCurrentEdge->lanes.size());
     643        54706 :     if (id != expectedID) {
     644          108 :         WRITE_WARNINGF(TL("Renaming lane '%' to '%'."), id, expectedID);
     645              :     }
     646        54706 :     myCurrentLane = new LaneAttrs();
     647        54706 :     myLastParameterised.push_back(myCurrentLane);
     648        54706 :     myCurrentLane->customShape = attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, nullptr, ok, false);
     649        54706 :     myCurrentLane->shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
     650        54706 :     myCurrentLane->width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_WIDTH);
     651        54706 :     myCurrentLane->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     652        54706 :     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        54561 :     } 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        54228 :     } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL) {
     674              :         return; // skip internal edges
     675              :     }
     676        62496 :     if (attrs.hasAttribute("maxspeed")) {
     677              :         // !!! deprecated
     678            0 :         myCurrentLane->maxSpeed = attrs.getFloat("maxspeed");
     679              :     } else {
     680        31248 :         myCurrentLane->maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
     681              :     }
     682        31248 :     myCurrentLane->friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, NBEdge::UNSPECIFIED_FRICTION, false); //sets 1 on empty
     683              :     try {
     684        62496 :         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        31248 :     myCurrentLane->disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
     690        31248 :     myCurrentLane->endOffset = attrs.getOpt<double>(SUMO_ATTR_ENDOFFSET, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_OFFSET);
     691        31248 :     myCurrentLane->accelRamp = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
     692        31248 :     myCurrentLane->changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
     693        31248 :     myCurrentLane->changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
     694        31248 :     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        31248 :     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        15982 : NIImporter_SUMO::addJunction(const SUMOSAXAttributes& attrs) {
     728              :     // get the id, report an error if not given or empty...
     729        15982 :     myCurrentJunction.node = nullptr;
     730              :     myCurrentJunction.intLanes.clear();
     731              :     myCurrentJunction.response.clear();
     732        15982 :     bool ok = true;
     733        15982 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     734        15982 :     if (!ok) {
     735              :         return;
     736              :     }
     737        15976 :     if (id[0] == ':') { // internal node
     738              :         return;
     739              :     }
     740        13832 :     SumoXMLNodeType type = attrs.getOpt<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok, SumoXMLNodeType::UNKNOWN);
     741        13832 :     if (ok) {
     742        13826 :         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        11927 :         } 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        13832 :     Position pos = readPosition(attrs, id, ok);
     752        13832 :     NBNetBuilder::transformCoordinate(pos, true, myLocation);
     753        13832 :     NBNode* node = new NBNode(id, pos, type);
     754        13832 :     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        13810 :         myLastParameterised.push_back(node);
     761              :     }
     762        13810 :     myCurrentJunction.node = node;
     763        13810 :     myCurrentJunction.intLanes = attrs.get<std::vector<std::string> >(SUMO_ATTR_INTLANES, nullptr, ok, false);
     764              :     // set optional radius
     765        13810 :     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        13810 :     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        13810 :     if (type == SumoXMLNodeType::RAIL_SIGNAL || type == SumoXMLNodeType::RAIL_CROSSING) {
     775              :         // both types of nodes come without a tlLogic
     776              :         myRailSignals.insert(id);
     777              :     }
     778        13810 :     node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, id.c_str(), ok, node->getRightOfWay()));
     779        13810 :     node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, id.c_str(), ok, node->getFringeType()));
     780        13810 :     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        46462 : NIImporter_SUMO::addRequest(const SUMOSAXAttributes& attrs) {
     788        46462 :     if (myCurrentJunction.node != nullptr) {
     789        46392 :         bool ok = true;
     790        92784 :         myCurrentJunction.response.push_back(attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok));
     791              :     }
     792        46462 : }
     793              : 
     794              : 
     795              : void
     796        70465 : NIImporter_SUMO::addConnection(const SUMOSAXAttributes& attrs) {
     797        70465 :     bool ok = true;
     798        70465 :     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        70443 :     EdgeAttrs* from = myEdges[fromID];
     804        70443 :     if (from->func == SumoXMLEdgeFunc::INTERNAL) {
     805              :         // internal junction connection
     806              :         return;
     807              :     }
     808              : 
     809        47318 :     Connection conn;
     810        47318 :     conn.toEdgeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
     811        47318 :     int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
     812        47318 :     conn.toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
     813        47318 :     conn.tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
     814        47318 :     conn.mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, false);
     815        47318 :     conn.keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
     816        47318 :     conn.indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
     817        47318 :     conn.edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
     818        47318 :     double contPos = NBEdge::UNSPECIFIED_CONTPOS;
     819        94636 :     if (OptionsCont::getOptions().isSet("default.connection.cont-pos")) {
     820        94636 :         contPos = OptionsCont::getOptions().getFloat("default.connection.cont-pos");
     821              :     }
     822        47318 :     conn.contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, contPos);
     823        47318 :     conn.visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE);
     824        47318 :     std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "", false);
     825        47318 :     std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "", false);
     826        47318 :     if (allow == "" && disallow == "") {
     827        47317 :         conn.permissions = SVC_UNSPECIFIED;
     828              :     } else {
     829            1 :         conn.permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
     830              :     }
     831        47318 :     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        47317 :         conn.changeLeft = SVC_UNSPECIFIED;
     835              :     }
     836        47318 :     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        47318 :         conn.changeRight = SVC_UNSPECIFIED;
     840              :     }
     841        47318 :     if (myChangeLefthand) {
     842              :         std::swap(conn.changeLeft, conn.changeRight);
     843              :     }
     844        47318 :     conn.speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, NBEdge::UNSPECIFIED_SPEED);
     845        47318 :     conn.friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, NBEdge::UNSPECIFIED_FRICTION);
     846        47318 :     conn.customLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
     847        94636 :     conn.customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
     848        47318 :     NBNetBuilder::transformCoordinates(conn.customShape, false, myLocation);
     849        47318 :     conn.uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED, false);
     850        47318 :     if (conn.tlID != "") {
     851         6076 :         conn.tlLinkIndex = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
     852         6076 :         conn.tlLinkIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
     853              :     } else {
     854        41242 :         conn.tlLinkIndex = NBConnection::InvalidTlIndex;
     855              :     }
     856        47318 :     if ((int)from->lanes.size() <= fromLaneIdx) {
     857           24 :         WRITE_ERRORF(TL("Invalid lane index '%' for connection from '%'."), toString(fromLaneIdx), fromID);
     858              :         return;
     859              :     }
     860        47310 :     from->lanes[fromLaneIdx]->connections.push_back(conn);
     861        47310 :     myLastParameterised.push_back(&from->lanes[fromLaneIdx]->connections.back());
     862              : 
     863              :     // determine crossing priority and tlIndex
     864        47310 :     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        47310 :     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        47318 : }
     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          527 : NIImporter_SUMO::initTrafficLightLogic(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
     946          527 :     if (currentTL) {
     947            0 :         WRITE_ERRORF(TL("Definition of tl-logic '%' was not finished."), currentTL->getID());
     948            0 :         return nullptr;
     949              :     }
     950          527 :     bool ok = true;
     951          527 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     952          527 :     SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
     953          527 :     std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
     954          527 :     std::string typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
     955              :     TrafficLightType type;
     956              :     if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
     957          527 :         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          527 :     if (ok) {
     963          527 :         return new NBLoadedSUMOTLDef(id, programID, offset, type);
     964              :     } else {
     965              :         return nullptr;
     966              :     }
     967              : }
     968              : 
     969              : 
     970              : void
     971         3695 : NIImporter_SUMO::addPhase(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
     972         3695 :     if (!currentTL) {
     973            0 :         WRITE_ERROR(TL("found phase without tl-logic"));
     974            0 :         return;
     975              :     }
     976              :     const std::string& id = currentTL->getID();
     977         3695 :     bool ok = true;
     978         3695 :     std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, id.c_str(), ok);
     979         3695 :     SUMOTime duration = TIME2STEPS(attrs.get<double>(SUMO_ATTR_DURATION, id.c_str(), ok));
     980         3695 :     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         3696 :     std::vector<int> nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
     986         3695 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, nullptr, ok);
     987              :     // Specific from actuated
     988         3695 :     const SUMOTime minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     989         3695 :     const SUMOTime maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     990         3695 :     const SUMOTime earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     991         3695 :     const SUMOTime latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     992              :     // specific von NEMA
     993         3695 :     const SUMOTime vehExt = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     994         3695 :     const SUMOTime yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     995         3695 :     const SUMOTime red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
     996         3695 :     if (ok) {
     997         3695 :         currentTL->addPhase(duration, state, minDuration, maxDuration, earliestEnd, latestEnd, vehExt, yellow, red, nextPhases, name);
     998              :     }
     999         3695 : }
    1000              : 
    1001              : 
    1002              : GeoConvHelper*
    1003         1188 : NIImporter_SUMO::loadLocation(const SUMOSAXAttributes& attrs, bool setLoaded) {
    1004              :     // @todo refactor parsing of location since its duplicated in NLHandler and PCNetProjectionLoader
    1005         1188 :     bool ok = true;
    1006              :     GeoConvHelper* result = nullptr;
    1007         1188 :     const Position offset = attrs.get<Position>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
    1008         1188 :     const Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
    1009         1188 :     const Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
    1010         1188 :     const std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
    1011         1188 :     if (ok) {
    1012         1188 :         result = new GeoConvHelper(proj, offset, origBoundary, convBoundary);
    1013         1188 :         result->resolveAbstractProjection();
    1014         1188 :         if (setLoaded) {
    1015         1168 :             GeoConvHelper::setLoaded(*result);
    1016              :         }
    1017              :     }
    1018         1188 :     return result;
    1019         1188 : }
    1020              : 
    1021              : 
    1022              : Position
    1023        13832 : NIImporter_SUMO::readPosition(const SUMOSAXAttributes& attrs, const std::string& id, bool& ok) {
    1024        13832 :     const double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
    1025        13832 :     const double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
    1026        13832 :     const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0.);
    1027        13832 :     return Position(x, y, z);
    1028              : }
    1029              : 
    1030              : 
    1031              : void
    1032           40 : NIImporter_SUMO::parseProhibitionConnection(const std::string& attr, std::string& from, std::string& to, bool& ok) {
    1033              :     // split from/to
    1034              :     const std::string::size_type div = attr.find("->");
    1035           40 :     if (div == std::string::npos) {
    1036            0 :         WRITE_ERRORF(TL("Missing connection divider in prohibition attribute '%'"), attr);
    1037            0 :         ok = false;
    1038              :     }
    1039           40 :     from = attr.substr(0, div);
    1040           80 :     to = attr.substr(div + 2);
    1041              :     // check whether the edges are known
    1042              :     if (myEdges.count(from) == 0) {
    1043            0 :         WRITE_ERRORF(TL("Unknown edge prohibition '%'"), from);
    1044            0 :         ok = false;
    1045              :     }
    1046              :     if (myEdges.count(to) == 0) {
    1047            0 :         WRITE_ERRORF(TL("Unknown edge prohibition '%'"), to);
    1048            0 :         ok = false;
    1049              :     }
    1050           40 : }
    1051              : 
    1052              : 
    1053              : void
    1054           44 : NIImporter_SUMO::addRoundabout(const SUMOSAXAttributes& attrs) {
    1055           44 :     bool ok = true;
    1056           44 :     const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
    1057           44 :     if (ok) {
    1058           44 :         myRoundabouts.push_back(edgeIDs);
    1059              :     }
    1060           44 : }
    1061              : 
    1062              : 
    1063              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1