LCOV - code coverage report
Current view: top level - src/netload - NLHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.6 % 1110 983
Test Date: 2026-06-15 15:46:12 Functions: 97.8 % 45 44

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    NLHandler.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Clemens Honomichl
      18              : /// @author  Sascha Krieg
      19              : /// @author  Michael Behrisch
      20              : /// @author  Felix Brack
      21              : /// @date    Mon, 9 Jul 2001
      22              : ///
      23              : // The XML-Handler for network loading
      24              : /****************************************************************************/
      25              : #include <config.h>
      26              : 
      27              : #include <string>
      28              : #include "NLHandler.h"
      29              : #include "NLEdgeControlBuilder.h"
      30              : #include "NLJunctionControlBuilder.h"
      31              : #include "NLDetectorBuilder.h"
      32              : #include "NLTriggerBuilder.h"
      33              : #include <utils/xml/SUMOXMLDefinitions.h>
      34              : #include <utils/xml/SUMOSAXHandler.h>
      35              : #include <utils/common/MsgHandler.h>
      36              : #include <utils/common/SUMOTime.h>
      37              : #include <utils/common/StringUtils.h>
      38              : #include <utils/common/StringTokenizer.h>
      39              : #include <utils/common/RGBColor.h>
      40              : #include <utils/geom/GeomConvHelper.h>
      41              : #include <microsim/MSGlobals.h>
      42              : #include <microsim/MSLane.h>
      43              : #include <microsim/MSJunction.h>
      44              : #include <microsim/MSJunctionLogic.h>
      45              : #include <microsim/MSStoppingPlace.h>
      46              : #include <microsim/traffic_lights/MSTrafficLightLogic.h>
      47              : #include <microsim/traffic_lights/MSRailSignal.h>
      48              : #include <microsim/traffic_lights/MSRailSignalControl.h>
      49              : #include <microsim/traffic_lights/MSRailSignalConstraint.h>
      50              : #include <mesosim/MESegment.h>
      51              : #include <utils/iodevices/OutputDevice.h>
      52              : #include <utils/common/UtilExceptions.h>
      53              : #include <utils/geom/GeoConvHelper.h>
      54              : #include <utils/shapes/ShapeContainer.h>
      55              : #include <utils/shapes/Shape.h>
      56              : 
      57              : 
      58              : // ===========================================================================
      59              : // method definitions
      60              : // ===========================================================================
      61        42489 : NLHandler::NLHandler(const std::string& file, MSNet& net,
      62              :                      NLDetectorBuilder& detBuilder,
      63              :                      NLTriggerBuilder& triggerBuilder,
      64              :                      NLEdgeControlBuilder& edgeBuilder,
      65        42489 :                      NLJunctionControlBuilder& junctionBuilder) :
      66              :     MSRouteHandler(file, true),
      67        42489 :     myNet(net), myActionBuilder(net),
      68        42489 :     myCurrentIsInternalToSkip(false),
      69        42489 :     myDetectorBuilder(detBuilder), myTriggerBuilder(triggerBuilder),
      70        42489 :     myEdgeControlBuilder(edgeBuilder), myJunctionControlBuilder(junctionBuilder),
      71        42489 :     myAmParsingTLLogicOrJunction(false), myCurrentIsBroken(false),
      72        42489 :     myHaveWarnedAboutInvalidTLType(false),
      73        42489 :     myHaveSeenInternalEdge(false),
      74        42489 :     myHaveJunctionHigherSpeeds(false),
      75        42489 :     myHaveSeenDefaultLength(false),
      76        42489 :     myHaveSeenNeighs(false),
      77        42489 :     myHaveSeenAdditionalSpeedRestrictions(false),
      78        42489 :     myHaveSeenTLSParams(false),
      79              :     myNetworkVersion(0, 0),
      80        42489 :     myNetIsLoaded(false) {
      81        42489 : }
      82              : 
      83              : 
      84        42489 : NLHandler::~NLHandler() {}
      85              : 
      86              : 
      87              : void
      88     10299458 : NLHandler::myStartElement(int element,
      89              :                           const SUMOSAXAttributes& attrs) {
      90              :     try {
      91     10299458 :         switch (element) {
      92        42478 :             case SUMO_TAG_NET: {
      93              :                 bool ok;
      94        42478 :                 MSGlobals::gLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
      95        42478 :                 myHaveJunctionHigherSpeeds = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, false);
      96        84956 :                 myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
      97              :                 break;
      98              :             }
      99      1772587 :             case SUMO_TAG_EDGE:
     100      1772587 :                 beginEdgeParsing(attrs);
     101              :                 break;
     102      2133708 :             case SUMO_TAG_LANE:
     103      2133708 :                 addLane(attrs);
     104              :                 break;
     105         8847 :             case SUMO_TAG_NEIGH:
     106         8847 :                 if (!myCurrentIsInternalToSkip) {
     107        17694 :                     myEdgeControlBuilder.addNeigh(attrs.getString(SUMO_ATTR_LANE));
     108              :                 }
     109         8847 :                 myHaveSeenNeighs = true;
     110         8847 :                 break;
     111       564066 :             case SUMO_TAG_JUNCTION:
     112       564066 :                 openJunction(attrs);
     113       564066 :                 initJunctionLogic(attrs);
     114              :                 break;
     115       601590 :             case SUMO_TAG_PHASE:
     116       601590 :                 addPhase(attrs);
     117              :                 break;
     118          192 :             case SUMO_TAG_CONDITION:
     119          192 :                 addCondition(attrs);
     120              :                 break;
     121          120 :             case SUMO_TAG_ASSIGNMENT:
     122          120 :                 addAssignment(attrs);
     123              :                 break;
     124            8 :             case SUMO_TAG_FUNCTION:
     125            8 :                 addFunction(attrs);
     126              :                 break;
     127      2937810 :             case SUMO_TAG_CONNECTION:
     128      2937810 :                 addConnection(attrs);
     129              :                 break;
     130            6 :             case SUMO_TAG_CONFLICT:
     131            6 :                 addConflict(attrs);
     132              :                 break;
     133       118617 :             case SUMO_TAG_TLLOGIC:
     134       118617 :                 initTrafficLightLogic(attrs);
     135              :                 break;
     136      1615794 :             case SUMO_TAG_REQUEST:
     137      1615794 :                 addRequest(attrs);
     138              :                 break;
     139          340 :             case SUMO_TAG_WAUT:
     140          340 :                 openWAUT(attrs);
     141              :                 break;
     142          696 :             case SUMO_TAG_WAUT_SWITCH:
     143          696 :                 addWAUTSwitch(attrs);
     144              :                 break;
     145          344 :             case SUMO_TAG_WAUT_JUNCTION:
     146          344 :                 addWAUTJunction(attrs);
     147              :                 break;
     148        24681 :             case SUMO_TAG_E1DETECTOR:
     149              :             case SUMO_TAG_INDUCTION_LOOP:
     150        24681 :                 addE1Detector(attrs);
     151              :                 break;
     152         3166 :             case SUMO_TAG_E2DETECTOR:
     153              :             case SUMO_TAG_LANE_AREA_DETECTOR:
     154         3166 :                 addE2Detector(attrs);
     155              :                 break;
     156         1445 :             case SUMO_TAG_E3DETECTOR:
     157              :             case SUMO_TAG_ENTRY_EXIT_DETECTOR:
     158         1445 :                 beginE3Detector(attrs);
     159              :                 break;
     160         1791 :             case SUMO_TAG_DET_ENTRY:
     161         1791 :                 addE3Entry(attrs);
     162              :                 break;
     163         1755 :             case SUMO_TAG_DET_EXIT:
     164         1755 :                 addE3Exit(attrs);
     165              :                 break;
     166         1130 :             case SUMO_TAG_INSTANT_INDUCTION_LOOP:
     167         1130 :                 addInstantE1Detector(attrs);
     168              :                 break;
     169          500 :             case SUMO_TAG_VSS:
     170          500 :                 myTriggerBuilder.parseAndBuildLaneSpeedTrigger(myNet, attrs, getFileName());
     171              :                 break;
     172          919 :             case SUMO_TAG_CALIBRATOR:
     173          919 :                 myTriggerBuilder.parseAndBuildCalibrator(myNet, attrs, getFileName());
     174              :                 break;
     175         4079 :             case SUMO_TAG_REROUTER:
     176         4079 :                 myTriggerBuilder.parseAndBuildRerouter(myNet, attrs);
     177              :                 break;
     178        51188 :             case SUMO_TAG_BUS_STOP:
     179              :             case SUMO_TAG_TRAIN_STOP:
     180              :             case SUMO_TAG_CONTAINER_STOP:
     181        51188 :                 myTriggerBuilder.parseAndBuildStoppingPlace(myNet, attrs, (SumoXMLTag)element);
     182        51177 :                 myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
     183        51177 :                 break;
     184          655 :             case SUMO_TAG_PARKING_SPACE:
     185          655 :                 myTriggerBuilder.parseAndAddLotEntry(attrs);
     186              :                 break;
     187        17306 :             case SUMO_TAG_PARKING_AREA:
     188        17306 :                 myTriggerBuilder.parseAndBeginParkingArea(myNet, attrs);
     189        17305 :                 myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
     190        17305 :                 break;
     191         2264 :             case SUMO_TAG_ACCESS:
     192         2264 :                 myTriggerBuilder.addAccess(myNet, attrs);
     193              :                 break;
     194        15266 :             case SUMO_TAG_CHARGING_STATION:
     195        15266 :                 myTriggerBuilder.parseAndBuildChargingStation(myNet, attrs);
     196        15265 :                 myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
     197        15265 :                 break;
     198           58 :             case SUMO_TAG_OVERHEAD_WIRE_SEGMENT:
     199           58 :                 myTriggerBuilder.parseAndBuildOverheadWireSegment(myNet, attrs);
     200              :                 break;
     201            8 :             case SUMO_TAG_OVERHEAD_WIRE_SECTION:
     202            8 :                 myTriggerBuilder.parseAndBuildOverheadWireSection(myNet, attrs);
     203              :                 break;
     204           17 :             case SUMO_TAG_TRACTION_SUBSTATION:
     205           17 :                 myTriggerBuilder.parseAndBuildTractionSubstation(myNet, attrs);
     206              :                 break;
     207           11 :             case SUMO_TAG_OVERHEAD_WIRE_CLAMP:
     208           11 :                 myTriggerBuilder.parseAndBuildOverheadWireClamp(myNet, attrs);
     209              :                 break;
     210          144 :             case SUMO_TAG_VTYPEPROBE:
     211          144 :                 addVTypeProbeDetector(attrs);
     212              :                 break;
     213          478 :             case SUMO_TAG_ROUTEPROBE:
     214          478 :                 addRouteProbeDetector(attrs);
     215              :                 break;
     216        12214 :             case SUMO_TAG_MEANDATA_EDGE:
     217        12214 :                 addEdgeLaneMeanData(attrs, SUMO_TAG_MEANDATA_EDGE);
     218              :                 break;
     219        11113 :             case SUMO_TAG_MEANDATA_LANE:
     220        11113 :                 addEdgeLaneMeanData(attrs, SUMO_TAG_MEANDATA_LANE);
     221              :                 break;
     222         1579 :             case SUMO_TAG_TIMEDEVENT:
     223         1579 :                 myActionBuilder.addAction(attrs, getFileName());
     224              :                 break;
     225          505 :             case SUMO_TAG_VAPORIZER:
     226          505 :                 myTriggerBuilder.buildVaporizer(attrs);
     227              :                 break;
     228        42500 :             case SUMO_TAG_LOCATION:
     229        42500 :                 setLocation(attrs);
     230              :                 break;
     231        14604 :             case SUMO_TAG_TAZ:
     232        14604 :                 addDistrict(attrs);
     233              :                 break;
     234        26306 :             case SUMO_TAG_TAZSOURCE:
     235        26306 :                 addDistrictEdge(attrs, true);
     236              :                 break;
     237        26313 :             case SUMO_TAG_TAZSINK:
     238        26313 :                 addDistrictEdge(attrs, false);
     239              :                 break;
     240          387 :             case SUMO_TAG_ROUNDABOUT:
     241          387 :                 addRoundabout(attrs);
     242              :                 break;
     243        18766 :             case SUMO_TAG_TYPE: {
     244        18766 :                 bool ok = true;
     245        37532 :                 myCurrentTypeID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     246              :                 break;
     247              :             }
     248          197 :             case SUMO_TAG_RESTRICTION: {
     249          197 :                 bool ok = true;
     250          394 :                 const SUMOVehicleClass svc = getVehicleClassID(attrs.get<std::string>(SUMO_ATTR_VCLASS, myCurrentTypeID.c_str(), ok));
     251          197 :                 const double speed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentTypeID.c_str(), ok);
     252          197 :                 if (ok) {
     253          197 :                     myNet.addRestriction(myCurrentTypeID, svc, speed);
     254              :                 }
     255          197 :                 if (myNetIsLoaded) {
     256          189 :                     myHaveSeenAdditionalSpeedRestrictions = true;
     257              :                 }
     258              :                 break;
     259              :             }
     260          104 :             case SUMO_TAG_PREFERENCE: {
     261          104 :                 bool ok = true;
     262          104 :                 const std::string routingType = attrs.get<std::string>(SUMO_ATTR_ROUTINGTYPE, nullptr, ok);
     263          104 :                 const double prio = attrs.get<double>(SUMO_ATTR_PRIORITY, routingType.c_str(), ok);
     264          104 :                 if (prio <= 0) {
     265            0 :                     throw InvalidArgument("In preference for routingType '" + routingType + "', priority must be positve");
     266              :                 }
     267          104 :                 if (attrs.hasAttribute(SUMO_ATTR_VCLASSES)) {
     268           14 :                     StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_VCLASSES, routingType.c_str(), ok));
     269           42 :                     for (std::string className : st.getVector()) {
     270           28 :                         myNet.addPreference(routingType, getVehicleClassID(className), prio);
     271           14 :                     }
     272          104 :                 } else if (!attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
     273              :                     // general preferenze applying to all types and vClasses
     274          152 :                     myNet.addPreference(routingType, "", prio);
     275              :                 }
     276          104 :                 if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
     277           14 :                     StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_VTYPES, routingType.c_str(), ok));
     278           28 :                     for (std::string typeName : st.getVector()) {
     279           42 :                         myNet.addPreference(routingType, typeName, prio);
     280           14 :                     }
     281           14 :                 }
     282              :                 break;
     283              :             }
     284         2178 :             case SUMO_TAG_STOPOFFSET: {
     285         2178 :                 bool ok = true;
     286         2178 :                 const StopOffset stopOffset(attrs, ok);
     287         2178 :                 if (!ok) {
     288            0 :                     WRITE_ERROR(myEdgeControlBuilder.reportCurrentEdgeOrLane());
     289              :                 } else {
     290         2178 :                     myEdgeControlBuilder.addStopOffsets(stopOffset);
     291              :                 }
     292              :                 break;
     293              :             }
     294          495 :             case SUMO_TAG_RAILSIGNAL_CONSTRAINTS: {
     295          495 :                 bool ok = true;
     296          495 :                 const std::string signalID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     297          495 :                 if (!MSNet::getInstance()->getTLSControl().knows(signalID)) {
     298            0 :                     throw InvalidArgument("Rail signal '" + signalID + "' in railSignalConstraints is not known");
     299              :                 }
     300          495 :                 myConstrainedSignal = dynamic_cast<MSRailSignal*>(MSNet::getInstance()->getTLSControl().get(signalID).getDefault());
     301          495 :                 if (myConstrainedSignal == nullptr) {
     302            0 :                     throw InvalidArgument("Traffic light '" + signalID + "' is not a rail signal");
     303              :                 }
     304              :                 break;
     305              :             }
     306          653 :             case SUMO_TAG_PREDECESSOR: // intended fall-through
     307              :             case SUMO_TAG_FOE_INSERTION: // intended fall-through
     308              :             case SUMO_TAG_INSERTION_PREDECESSOR: // intended fall-through
     309              :             case SUMO_TAG_INSERTION_ORDER: // intended fall-through
     310              :             case SUMO_TAG_BIDI_PREDECESSOR:
     311          653 :                 myLastParameterised.push_back(addPredecessorConstraint(element, attrs, myConstrainedSignal));
     312          653 :                 break;
     313           15 :             case SUMO_TAG_DEADLOCK:
     314           15 :                 addDeadlock(attrs);
     315              :                 break;
     316              :             default:
     317              :                 break;
     318              :         }
     319           85 :     } catch (InvalidArgument& e) {
     320           85 :         myCurrentIsBroken = true;
     321           85 :         WRITE_ERROR(e.what());
     322           85 :     }
     323     10299444 :     MSRouteHandler::myStartElement(element, attrs);
     324     10299360 :     if (element == SUMO_TAG_PARAM && !myCurrentIsBroken) {
     325        43989 :         addParam(attrs);
     326              :     }
     327     10299360 : }
     328              : 
     329              : 
     330              : void
     331     10294672 : NLHandler::myEndElement(int element) {
     332     10294672 :     switch (element) {
     333      1772527 :         case SUMO_TAG_EDGE:
     334      1772527 :             closeEdge();
     335      1772527 :             break;
     336      2133708 :         case SUMO_TAG_LANE:
     337      2133708 :             myEdgeControlBuilder.closeLane();
     338      2133708 :             if (!myCurrentIsInternalToSkip && !myCurrentIsBroken) {
     339              :                 myLastParameterised.pop_back();
     340              :             }
     341              :             break;
     342       564066 :         case SUMO_TAG_JUNCTION:
     343       564066 :             if (!myCurrentIsBroken) {
     344              :                 try {
     345       563931 :                     myJunctionControlBuilder.closeJunction(getFileName());
     346            6 :                 } catch (InvalidArgument& e) {
     347            6 :                     WRITE_ERROR(e.what());
     348            6 :                 }
     349              :             }
     350       564066 :             myAmParsingTLLogicOrJunction = false;
     351       564066 :             break;
     352       118617 :         case SUMO_TAG_TLLOGIC:
     353       118617 :             if (!myCurrentIsBroken) {
     354              :                 try {
     355       118613 :                     myJunctionControlBuilder.closeTrafficLightLogic(getFileName());
     356            8 :                 } catch (InvalidArgument& e) {
     357           40 :                     for (MSPhaseDefinition* const phase : myJunctionControlBuilder.getLoadedPhases()) {
     358           32 :                         delete phase;
     359              :                     }
     360            8 :                     WRITE_ERROR(e.what());
     361            8 :                 }
     362              :             }
     363       118611 :             myAmParsingTLLogicOrJunction = false;
     364       118611 :             break;
     365            8 :         case SUMO_TAG_FUNCTION:
     366            8 :             closeFunction();
     367            8 :             break;
     368          340 :         case SUMO_TAG_WAUT:
     369          340 :             closeWAUT();
     370          340 :             break;
     371          495 :         case SUMO_TAG_RAILSIGNAL_CONSTRAINTS:
     372          495 :             myConstrainedSignal = nullptr;
     373          495 :             break;
     374        28967 :         case SUMO_TAG_E1DETECTOR:
     375              :         case SUMO_TAG_INDUCTION_LOOP:
     376              :         case SUMO_TAG_INSTANT_INDUCTION_LOOP:
     377              :         case SUMO_TAG_E2DETECTOR:
     378              :         case SUMO_TAG_LANE_AREA_DETECTOR:
     379        28967 :             if (!myCurrentIsBroken) {
     380              :                 myLastParameterised.pop_back();
     381              :             }
     382              :             break;
     383         1445 :         case SUMO_TAG_E3DETECTOR:
     384              :         case SUMO_TAG_ENTRY_EXIT_DETECTOR:
     385         1445 :             endE3Detector();
     386         1437 :             if (!myCurrentIsBroken) {
     387              :                 myLastParameterised.pop_back();
     388              :             }
     389              :             break;
     390        17306 :         case SUMO_TAG_PARKING_AREA:
     391        17306 :             myTriggerBuilder.updateParkingAreaDefaultCapacity();
     392        17306 :             myTriggerBuilder.endParkingArea();
     393              :             myLastParameterised.pop_back();
     394              :             break;
     395        66454 :         case SUMO_TAG_BUS_STOP:
     396              :         case SUMO_TAG_TRAIN_STOP:
     397              :         case SUMO_TAG_CONTAINER_STOP:
     398              :         case SUMO_TAG_CHARGING_STATION:
     399        66454 :             myTriggerBuilder.endStoppingPlace();
     400              :             myLastParameterised.pop_back();
     401              :             break;
     402          653 :         case SUMO_TAG_PREDECESSOR: // intended fall-through
     403              :         case SUMO_TAG_FOE_INSERTION: // intended fall-through
     404              :         case SUMO_TAG_INSERTION_PREDECESSOR: // intended fall-through
     405              :         case SUMO_TAG_INSERTION_ORDER: // intended fall-through
     406              :         case SUMO_TAG_BIDI_PREDECESSOR:
     407              :             myLastParameterised.pop_back();
     408              :             break;
     409        42393 :         case SUMO_TAG_NET:
     410              :             // build junction graph
     411      1814603 :             for (JunctionGraph::iterator it = myJunctionGraph.begin(); it != myJunctionGraph.end(); ++it) {
     412      1772360 :                 MSEdge* edge = MSEdge::dictionary(it->first);
     413      3544720 :                 MSJunction* from = myJunctionControlBuilder.retrieve(it->second.first);
     414      3544720 :                 MSJunction* to = myJunctionControlBuilder.retrieve(it->second.second);
     415      1772360 :                 if (from == nullptr) {
     416           96 :                     WRITE_ERRORF(TL("Unknown from-node '%' for edge '%'."), it->second.first, it->first);
     417           32 :                     return;
     418              :                 }
     419      1772328 :                 if (to == nullptr) {
     420          354 :                     WRITE_ERRORF(TL("Unknown to-node '%' for edge '%'."), it->second.second, it->first);
     421          118 :                     return;
     422              :                 }
     423      1772210 :                 if (edge != nullptr) {
     424      1772206 :                     edge->setJunctions(from, to);
     425      1772206 :                     from->addOutgoing(edge);
     426      1772206 :                     to->addIncoming(edge);
     427              :                 }
     428              :             }
     429        42243 :             myNetIsLoaded = true;
     430        42243 :             break;
     431              :         default:
     432              :             break;
     433              :     }
     434     10294495 :     MSRouteHandler::myEndElement(element);
     435              : }
     436              : 
     437              : 
     438              : 
     439              : // ---- the root/edge - element
     440              : void
     441      1772587 : NLHandler::beginEdgeParsing(const SUMOSAXAttributes& attrs) {
     442      1772587 :     bool ok = true;
     443      1772587 :     myCurrentIsBroken = false;
     444              :     // get the id, report an error if not given or empty...
     445      1772587 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     446      1772587 :     if (!ok) {
     447            4 :         myCurrentIsBroken = true;
     448            4 :         return;
     449              :     }
     450              :     // parse the function
     451      1772583 :     const SumoXMLEdgeFunc func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
     452      1772583 :     if (!ok) {
     453            2 :         myCurrentIsBroken = true;
     454            2 :         return;
     455              :     }
     456              :     // omit internal edges if not wished
     457      1772581 :     if (id[0] == ':') {
     458      1096109 :         myHaveSeenInternalEdge = true;
     459      1096109 :         if (!MSGlobals::gUsingInternalLanes && (func == SumoXMLEdgeFunc::CROSSING || func == SumoXMLEdgeFunc::WALKINGAREA)) {
     460            0 :             myCurrentIsInternalToSkip = true;
     461            0 :             return;
     462              :         }
     463      2192218 :         std::string junctionID = SUMOXMLDefinitions::getJunctionIDFromInternalEdge(id);
     464      2192218 :         myJunctionGraph[id] = std::make_pair(junctionID, junctionID);
     465              :     } else {
     466       676472 :         myHaveSeenDefaultLength |= !attrs.hasAttribute(SUMO_ATTR_LENGTH);
     467       676472 :         myJunctionGraph[id] = std::make_pair(
     468      1352944 :                                   attrs.get<std::string>(SUMO_ATTR_FROM, id.c_str(), ok),
     469      1352944 :                                   attrs.get<std::string>(SUMO_ATTR_TO, id.c_str(), ok));
     470              :     }
     471      1772581 :     if (!ok) {
     472           16 :         myCurrentIsBroken = true;
     473           16 :         return;
     474              :     }
     475      1772565 :     myCurrentIsInternalToSkip = false;
     476              :     // get the street name
     477      1772565 :     const std::string streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     478              :     // get the edge type
     479      1772565 :     const std::string edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     480      3545130 :     const std::string routingType = attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, id.c_str(), ok, "");
     481              :     // get the edge priority (only for visualization)
     482      1772565 :     const int priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1); // default taken from netbuild/NBFrame option 'default.priority'
     483              :     // get the bidi-edge
     484      3545130 :     const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
     485              :     // get the kilometrage/mileage (for visualization and output)
     486      1772565 :     const double distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
     487              : 
     488      1772565 :     if (!ok) {
     489            0 :         myCurrentIsBroken = true;
     490              :         return;
     491              :     }
     492              :     //
     493              :     try {
     494      1772565 :         myEdgeControlBuilder.beginEdgeParsing(id, func, streetName, edgeType, routingType, priority, bidi, distance);
     495            4 :     } catch (InvalidArgument& e) {
     496            4 :         WRITE_ERROR(e.what());
     497            4 :         myCurrentIsBroken = true;
     498            4 :     }
     499              : 
     500      1772565 :     if (func == SumoXMLEdgeFunc::CROSSING) {
     501              :         //get the crossingEdges attribute (to implement the other side of the road pushbutton)
     502        38838 :         const std::string crossingEdges = attrs.getOpt<std::string>(SUMO_ATTR_CROSSING_EDGES, id.c_str(), ok, "");
     503        19419 :         if (!crossingEdges.empty()) {
     504              :             std::vector<std::string> crossingEdgesVector;
     505        19419 :             StringTokenizer edges(crossingEdges);
     506        57296 :             while (edges.hasNext()) {
     507        75754 :                 crossingEdgesVector.push_back(edges.next());
     508              :             }
     509        19419 :             myEdgeControlBuilder.addCrossingEdges(crossingEdgesVector);
     510        19419 :         }
     511              :     }
     512      1772565 :     myLastEdgeParameters.clearParameter();
     513      1772565 :     myLastParameterised.push_back(&myLastEdgeParameters);
     514              : }
     515              : 
     516              : 
     517              : void
     518      1772527 : NLHandler::closeEdge() {
     519              :     myLastParameterised.clear();
     520              :     // omit internal edges if not wished and broken edges
     521      1772527 :     if (myCurrentIsInternalToSkip || myCurrentIsBroken) {
     522              :         return;
     523              :     }
     524              :     try {
     525      1772427 :         MSEdge* e = myEdgeControlBuilder.closeEdge();
     526      1772427 :         MSEdge::dictionary(e->getID(), e);
     527      1772427 :         e->updateParameters(myLastEdgeParameters.getParametersMap());
     528            0 :     } catch (InvalidArgument& e) {
     529            0 :         WRITE_ERROR(e.what());
     530            0 :     }
     531              : }
     532              : 
     533              : 
     534              : //             ---- the root/edge/lanes/lane - element
     535              : void
     536      2133708 : NLHandler::addLane(const SUMOSAXAttributes& attrs) {
     537              :     // omit internal edges if not wished and broken edges
     538      2133708 :     if (myCurrentIsInternalToSkip || myCurrentIsBroken) {
     539          132 :         return;
     540              :     }
     541      2133622 :     bool ok = true;
     542              :     // get the id, report an error if not given or empty...
     543      2133622 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     544      2133622 :     if (!ok) {
     545            8 :         myCurrentIsBroken = true;
     546            8 :         return;
     547              :     }
     548      2133614 :     const double maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
     549      2133614 :     const double friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, (double)(1.), false);
     550      2133614 :     const double length = attrs.get<double>(SUMO_ATTR_LENGTH, id.c_str(), ok);
     551              :     // sanity check values that could lead to crashing later on
     552      2133614 :     if (std::isnan(length)) {
     553           18 :         WRITE_ERRORF(TL("Attribute length of lane '%' is invalid (%)"), id, length);
     554            6 :         myCurrentIsBroken = true;
     555            6 :         return;
     556              :     }
     557      2133608 :     if (std::isnan(maxSpeed)) {
     558            0 :         WRITE_ERRORF(TL("Attribute maxSpeed of lane '%' is invalid (%)"), id, maxSpeed);
     559            0 :         myCurrentIsBroken = true;
     560            0 :         return;
     561              :     }
     562      2133608 :     const std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, id.c_str(), ok, "", false);
     563      2133608 :     const std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
     564      2133608 :     const std::string changeLeftS = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
     565      4267216 :     const std::string changeRightS = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
     566      2133608 :     const double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, SUMO_const_laneWidth);
     567      2133608 :     const PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
     568      2133608 :     const PositionVector outlineShape = attrs.getOpt<PositionVector>(SUMO_ATTR_OUTLINESHAPE, id.c_str(), ok, PositionVector());
     569      2133608 :     const int index = attrs.get<int>(SUMO_ATTR_INDEX, id.c_str(), ok);
     570      2133608 :     const bool isRampAccel = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
     571      4267216 :     const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     572      2133608 :     if (shape.size() < 2 && myEdgeControlBuilder.getCurrentEdgeFunction() != SumoXMLEdgeFunc::WALKINGAREA) {
     573           96 :         WRITE_ERRORF(TL("Shape of lane '%' is broken.\n Can not build according edge."), id);
     574           32 :         myCurrentIsBroken = true;
     575              :         return;
     576              :     }
     577      2133576 :     const SVCPermissions permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
     578      2133576 :     SVCPermissions changeLeft = parseVehicleClasses(changeLeftS, "", myNetworkVersion);
     579      2133576 :     SVCPermissions changeRight = parseVehicleClasses(changeRightS, "", myNetworkVersion);
     580      2133576 :     if (MSGlobals::gLefthand) {
     581              :         // internally, changeLeft always checks for the higher lane index
     582              :         // even though the higher lane index is to the right in a left-hand network
     583              :         std::swap(changeLeft, changeRight);
     584              :     }
     585      2133576 :     if (permissions != SVCAll || changeLeft != SVCAll || changeRight != SVCAll) {
     586       939298 :         myNet.setPermissionsFound();
     587              :     }
     588      2133576 :     myCurrentIsBroken |= !ok;
     589      2133576 :     if (!myCurrentIsBroken) {
     590              :         try {
     591      2133556 :             MSLane* lane = myEdgeControlBuilder.addLane(id, maxSpeed, friction, length, shape, width, permissions, changeLeft, changeRight, index, isRampAccel, type, outlineShape);
     592              :             // insert the lane into the lane-dictionary, checking
     593      2133556 :             if (!MSLane::dictionary(id, lane)) {
     594            8 :                 delete lane;
     595           24 :                 WRITE_ERRORF(TL("Another lane with the id '%' exists."), id);
     596            8 :                 myCurrentIsBroken = true;
     597            8 :                 myLastParameterised.push_back(nullptr);
     598              :             } else {
     599      2133548 :                 myLastParameterised.push_back(lane);
     600              :             }
     601            0 :         } catch (InvalidArgument& e) {
     602            0 :             WRITE_ERROR(e.what());
     603            0 :         }
     604              :     }
     605      2133608 : }
     606              : 
     607              : 
     608              : // ---- the root/junction - element
     609              : void
     610       564066 : NLHandler::openJunction(const SUMOSAXAttributes& attrs) {
     611       564066 :     myCurrentIsBroken = false;
     612       564066 :     bool ok = true;
     613              :     // get the id, report an error if not given or empty...
     614       564066 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     615       564066 :     if (!ok) {
     616            6 :         myCurrentIsBroken = true;
     617              :         return;
     618              :     }
     619       564060 :     PositionVector shape;
     620       564060 :     if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
     621              :         // inner junctions have no shape
     622       743910 :         shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
     623       371955 :         if (shape.size() > 2) {
     624       329870 :             shape.closePolygon();
     625              :         }
     626              :     }
     627       564060 :     double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
     628       564060 :     double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
     629       564060 :     double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0);
     630       564060 :     const SumoXMLNodeType type = attrs.get<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok);
     631       564060 :     std::string key = attrs.getOpt<std::string>(SUMO_ATTR_KEY, id.c_str(), ok, "");
     632      1128120 :     std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     633              :     // incoming lanes
     634              :     std::vector<MSLane*> incomingLanes;
     635      1128120 :     parseLanes(id, attrs.getStringSecure(SUMO_ATTR_INCLANES, ""), incomingLanes, ok);
     636              :     // internal lanes
     637              :     std::vector<MSLane*> internalLanes;
     638       564060 :     if (MSGlobals::gUsingInternalLanes) {
     639      1126316 :         parseLanes(id, attrs.getStringSecure(SUMO_ATTR_INTLANES, ""), internalLanes, ok);
     640              :     }
     641       564060 :     if (!ok) {
     642          129 :         myCurrentIsBroken = true;
     643              :     } else {
     644              :         try {
     645       563931 :             myJunctionControlBuilder.openJunction(id, key, type, Position(x, y, z), shape, incomingLanes, internalLanes, name);
     646            0 :         } catch (InvalidArgument& e) {
     647            0 :             WRITE_ERROR(e.what() + std::string("\n Can not build according junction."));
     648            0 :             myCurrentIsBroken = true;
     649            0 :         }
     650              :     }
     651      1128120 : }
     652              : 
     653              : 
     654              : void
     655      1127218 : NLHandler::parseLanes(const std::string& junctionID,
     656              :                       const std::string& def, std::vector<MSLane*>& into, bool& ok) {
     657      3381654 :     StringTokenizer st(def, " ");
     658      4360863 :     while (ok && st.hasNext()) {
     659      3233645 :         std::string laneID = st.next();
     660      3233645 :         MSLane* lane = MSLane::dictionary(laneID);
     661      3233645 :         if (!MSGlobals::gUsingInternalLanes && laneID[0] == ':') {
     662           73 :             continue;
     663              :         }
     664      3233572 :         if (lane == nullptr) {
     665          360 :             WRITE_ERRORF(TL("An unknown lane ('%') was tried to be set as incoming to junction '%'."), laneID, junctionID);
     666          120 :             ok = false;
     667          120 :             continue;
     668              :         }
     669      3233452 :         into.push_back(lane);
     670              :     }
     671      1127218 : }
     672              : // ----
     673              : 
     674              : void
     675        43989 : NLHandler::addParam(const SUMOSAXAttributes& attrs) {
     676        43989 :     bool ok = true;
     677        43989 :     const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
     678              :     // circumventing empty string test
     679        43989 :     const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
     680        43989 :     if (myLastParameterised.size() > 0 && myLastParameterised.back() != nullptr) {
     681        29838 :         myLastParameterised.back()->setParameter(key, val);
     682              :     }
     683              :     // set
     684        43989 :     if (ok && myAmParsingTLLogicOrJunction) {
     685              :         assert(key != "");
     686        10922 :         myJunctionControlBuilder.addParam(key, val);
     687        10922 :         if (myNetIsLoaded) {
     688         6827 :             myHaveSeenTLSParams = true;
     689              :         }
     690              :     }
     691        43989 : }
     692              : 
     693              : 
     694              : void
     695          340 : NLHandler::openWAUT(const SUMOSAXAttributes& attrs) {
     696          340 :     myCurrentIsBroken = false;
     697          340 :     bool ok = true;
     698              :     // get the id, report an error if not given or empty...
     699          340 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     700          340 :     if (!ok) {
     701            4 :         myCurrentIsBroken = true;
     702              :         return;
     703              :     }
     704          336 :     SUMOTime refTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_REF_TIME, id.c_str(), ok, 0);
     705          336 :     SUMOTime period = attrs.getOptSUMOTimeReporting(SUMO_ATTR_PERIOD, id.c_str(), ok, 0);
     706          336 :     std::string startProg = attrs.get<std::string>(SUMO_ATTR_START_PROG, id.c_str(), ok);
     707          336 :     if (!ok) {
     708            4 :         myCurrentIsBroken = true;
     709              :     }
     710          336 :     if (!myCurrentIsBroken) {
     711          332 :         myCurrentWAUTID = id;
     712              :         try {
     713          332 :             myJunctionControlBuilder.getTLLogicControlToUse().addWAUT(refTime, id, startProg, period);
     714            0 :         } catch (InvalidArgument& e) {
     715            0 :             WRITE_ERROR(e.what());
     716            0 :             myCurrentIsBroken = true;
     717            0 :         }
     718              :     }
     719              : }
     720              : 
     721              : 
     722              : void
     723          696 : NLHandler::addWAUTSwitch(const SUMOSAXAttributes& attrs) {
     724          696 :     bool ok = true;
     725          696 :     SUMOTime t = attrs.getSUMOTimeReporting(SUMO_ATTR_TIME, myCurrentWAUTID.c_str(), ok);
     726          696 :     std::string to = attrs.get<std::string>(SUMO_ATTR_TO, myCurrentWAUTID.c_str(), ok);
     727          696 :     if (!ok) {
     728            8 :         myCurrentIsBroken = true;
     729              :     }
     730          696 :     if (!myCurrentIsBroken) {
     731              :         try {
     732          680 :             myJunctionControlBuilder.getTLLogicControlToUse().addWAUTSwitch(myCurrentWAUTID, t, to);
     733            0 :         } catch (InvalidArgument& e) {
     734            0 :             WRITE_ERROR(e.what());
     735            0 :             myCurrentIsBroken = true;
     736            0 :         }
     737              :     }
     738          696 : }
     739              : 
     740              : 
     741              : void
     742          344 : NLHandler::addWAUTJunction(const SUMOSAXAttributes& attrs) {
     743          344 :     bool ok = true;
     744          344 :     std::string wautID = attrs.get<std::string>(SUMO_ATTR_WAUT_ID, nullptr, ok);
     745          344 :     std::string junctionID = attrs.get<std::string>(SUMO_ATTR_JUNCTION_ID, nullptr, ok);
     746          344 :     std::string procedure = attrs.getOpt<std::string>(SUMO_ATTR_PROCEDURE, nullptr, ok, "");
     747          344 :     bool synchron = attrs.getOpt<bool>(SUMO_ATTR_SYNCHRON, nullptr, ok, false);
     748          344 :     if (!ok) {
     749            0 :         myCurrentIsBroken = true;
     750              :     }
     751              :     try {
     752          344 :         if (!myCurrentIsBroken) {
     753          328 :             myJunctionControlBuilder.getTLLogicControlToUse().addWAUTJunction(wautID, junctionID, procedure, synchron);
     754              :         }
     755           12 :     } catch (InvalidArgument& e) {
     756           12 :         WRITE_ERROR(e.what());
     757           12 :         myCurrentIsBroken = true;
     758           12 :     }
     759          344 : }
     760              : 
     761              : 
     762              : void
     763      1615794 : NLHandler::addRequest(const SUMOSAXAttributes& attrs) {
     764      1615794 :     if (myCurrentIsBroken) {
     765          130 :         return;
     766              :     }
     767      1615664 :     bool ok = true;
     768      1615664 :     int request = attrs.get<int>(SUMO_ATTR_INDEX, nullptr, ok);
     769              :     bool cont = false;
     770      1615664 :     cont = attrs.getOpt<bool>(SUMO_ATTR_CONT, nullptr, ok, false);
     771      1615664 :     std::string response = attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok);
     772      1615664 :     std::string foes = attrs.get<std::string>(SUMO_ATTR_FOES, nullptr, ok);
     773      1615664 :     if (!ok) {
     774              :         return;
     775              :     }
     776              :     // store received information
     777      1615664 :     if (request >= 0 && response.length() > 0) {
     778              :         try {
     779      1615664 :             myJunctionControlBuilder.addLogicItem(request, response, foes, cont);
     780            0 :         } catch (InvalidArgument& e) {
     781            0 :             WRITE_ERROR(e.what());
     782            0 :         }
     783              :     }
     784              : }
     785              : 
     786              : 
     787              : void
     788       564066 : NLHandler::initJunctionLogic(const SUMOSAXAttributes& attrs) {
     789       564066 :     if (myCurrentIsBroken) {
     790          135 :         return;
     791              :     }
     792       563931 :     myAmParsingTLLogicOrJunction = true;
     793       563931 :     bool ok = true;
     794              :     // we either a have a junction or a legacy network with ROWLogic
     795       563931 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     796       563931 :     if (ok) {
     797       563931 :         myJunctionControlBuilder.initJunctionLogic(id);
     798              :     }
     799              : }
     800              : 
     801              : 
     802              : void
     803       118617 : NLHandler::initTrafficLightLogic(const SUMOSAXAttributes& attrs) {
     804       118617 :     myCurrentIsBroken = false;
     805       118617 :     myAmParsingTLLogicOrJunction = true;
     806       118617 :     bool ok = true;
     807       118617 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     808       118617 :     std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
     809       118617 :     TrafficLightType type = TrafficLightType::STATIC;
     810              :     std::string typeS;
     811       118617 :     if (myJunctionControlBuilder.getTLLogicControlToUse().get(id, programID) == nullptr) {
     812              :         // SUMO_ATTR_TYPE is not needed when only modifying the offset of an
     813              :         // existing program
     814       118458 :         typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok);
     815       118458 :         if (!ok) {
     816            4 :             myCurrentIsBroken = true;
     817            4 :             return;
     818              :         }
     819              :         if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
     820       118454 :             type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
     821              :         } else {
     822            0 :             WRITE_ERRORF(TL("Traffic light '%' has unknown type '%'."), id, typeS);
     823              :         }
     824       118454 :         if (MSGlobals::gUseMesoSim && type != TrafficLightType::STATIC
     825              :                 && type != TrafficLightType::RAIL_SIGNAL
     826              :                 && type != TrafficLightType::RAIL_CROSSING
     827              :                 && type != TrafficLightType::OFF) {
     828           34 :             if (!myHaveWarnedAboutInvalidTLType) {
     829           68 :                 WRITE_WARNINGF(TL("Traffic light type '%' cannot be used in mesoscopic simulation. Using '%' as fallback."), toString(type), toString(TrafficLightType::STATIC));
     830           34 :                 myHaveWarnedAboutInvalidTLType = true;
     831              :             }
     832           34 :             type = TrafficLightType::STATIC;
     833              :         }
     834              :     }
     835       118613 :     SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
     836       118613 :     if (offset == SUMOTime_MAX) {
     837            8 :         offset = string2time(OptionsCont::getOptions().getString("begin"));
     838              :     }
     839       118613 :     if (!ok) {
     840            0 :         myCurrentIsBroken = true;
     841            0 :         return;
     842              :     }
     843       118613 :     myJunctionControlBuilder.initTrafficLightLogic(id, programID, type, offset);
     844              : }
     845              : 
     846              : 
     847              : void
     848       601590 : NLHandler::addPhase(const SUMOSAXAttributes& attrs) {
     849              :     // try to get the phase definition
     850       601590 :     bool ok = true;
     851       601590 :     const std::string& id = myJunctionControlBuilder.getActiveKey();
     852              :     const SUMOTime tDefault = MSPhaseDefinition::UNSPECIFIED_DURATION;
     853              : 
     854       601590 :     const SUMOTime duration = attrs.getSUMOTimeReporting(SUMO_ATTR_DURATION, myJunctionControlBuilder.getActiveKey().c_str(), ok);
     855       601590 :     const std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok);
     856       601590 :     if (duration == 0) {
     857            8 :         WRITE_ERROR("Duration of phase " + toString(myJunctionControlBuilder.getLoadedPhases().size())
     858              :                     + " for tlLogic '" + myJunctionControlBuilder.getActiveKey()
     859              :                     + "' program '" + myJunctionControlBuilder.getActiveSubKey() + "' is zero.");
     860            4 :         return;
     861              :     }
     862       601586 :     if (!ok) {
     863              :         return;
     864              :     }
     865      1203172 :     MSPhaseDefinition* phase = new MSPhaseDefinition(duration, state);
     866              : 
     867              :     // if the traffic light is an actuated traffic light, try to get
     868              :     //  the minimum and maximum durations
     869       601586 :     phase->minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, duration);
     870              :     // if minDur is set but not maxDur, assume high maxDur (avoid using the absolute max in case we do some arithmetic later)
     871       601586 :     SUMOTime defaultMaxDur = attrs.hasAttribute(SUMO_ATTR_MINDURATION) ? std::numeric_limits<int>::max() : duration;
     872       601586 :     phase->maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, defaultMaxDur);
     873       601586 :     phase->earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, tDefault);
     874       601586 :     phase->latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, tDefault);
     875       601586 :     phase->nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
     876      1203172 :     phase->earlyTarget = attrs.getOpt<std::string>(SUMO_ATTR_EARLY_TARGET, id.c_str(), ok);
     877      1203172 :     phase->finalTarget = attrs.getOpt<std::string>(SUMO_ATTR_FINAL_TARGET, id.c_str(), ok);
     878      1203172 :     phase->name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok);
     879              : 
     880       601586 :     phase->vehext = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, tDefault);
     881       601586 :     phase->yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, tDefault);
     882       601586 :     phase->red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, tDefault);
     883              : 
     884       601586 :     if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
     885              :         //SOTL attributes
     886              :         //If the type attribute is not present, the parsed phase is of type "undefined" (MSPhaseDefinition constructor),
     887              :         //in this way SOTL traffic light logic can recognize the phase as unsuitable or decides other
     888              :         //behaviors. See SOTL traffic light logic implementations.
     889              :         std::string phaseTypeString;
     890              :         try {
     891         1536 :             phaseTypeString = attrs.get<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, false);
     892            0 :         } catch (EmptyData&) {
     893            0 :             MsgHandler::getWarningInstance()->inform("Empty type definition. Assuming phase type as SUMOSOTL_TagAttrDefinitions::SOTL_ATTL_TYPE_TRANSIENT");
     894            0 :             phase->myTransientNotDecisional = false;
     895            0 :         }
     896          768 :         if (phaseTypeString.find("decisional") != std::string::npos) {
     897          256 :             phase->myTransientNotDecisional = false;
     898          512 :         } else if (phaseTypeString.find("transient") != std::string::npos) {
     899          512 :             phase->myTransientNotDecisional = true;
     900              :         } else {
     901            0 :             MsgHandler::getWarningInstance()->inform("SOTL_ATTL_TYPE_DECISIONAL nor SOTL_ATTL_TYPE_TRANSIENT. Assuming phase type as SUMOSOTL_TagAttrDefinitions::SOTL_ATTL_TYPE_TRANSIENT");
     902            0 :             phase->myTransientNotDecisional = false;
     903              :         }
     904          768 :         phase->myCommit = (phaseTypeString.find("commit") != std::string::npos);
     905              : 
     906          768 :         if (phaseTypeString.find("target") != std::string::npos) {
     907          256 :             std::string delimiter(" ,;");
     908              :             //Phase declared as target, getting targetLanes attribute
     909              :             try {
     910          512 :                 phase->myTargetLaneSet = StringTokenizer(attrs.getStringSecure(SUMO_ATTR_TARGETLANE, ""), " ,;", true).getVector();
     911            0 :             } catch (EmptyData&) {
     912            0 :                 MsgHandler::getErrorInstance()->inform("Missing targetLane definition for the target phase.");
     913            0 :                 delete phase;
     914              :                 return;
     915            0 :             }
     916              :         }
     917              :     }
     918              : 
     919       601586 :     if (phase->maxDuration < phase->minDuration) {
     920            0 :         WRITE_WARNINGF(TL("maxDur % should not be smaller than minDir % in phase of tlLogic %"), phase->maxDuration, phase->minDuration, id);
     921            0 :         phase->maxDuration = phase->duration;
     922              :     }
     923              : 
     924       601586 :     phase->myLastSwitch = string2time(OptionsCont::getOptions().getString("begin")) - 1; // SUMOTime-option
     925       601586 :     myJunctionControlBuilder.addPhase(phase);
     926              : }
     927              : 
     928              : 
     929              : void
     930          192 : NLHandler::addCondition(const SUMOSAXAttributes& attrs) {
     931          192 :     bool ok = true;
     932          192 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     933          192 :     const std::string value = attrs.get<std::string>(SUMO_ATTR_VALUE, id.c_str(), ok);
     934          192 :     if (!myJunctionControlBuilder.addCondition(id, value)) {
     935            0 :         WRITE_ERRORF(TL("Duplicate condition '%' in tlLogic '%'"), id, myJunctionControlBuilder.getActiveKey());
     936              :     }
     937          192 : }
     938              : 
     939              : 
     940              : void
     941          120 : NLHandler::addAssignment(const SUMOSAXAttributes& attrs) {
     942          120 :     bool ok = true;
     943          120 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     944          120 :     const std::string check = attrs.get<std::string>(SUMO_ATTR_CHECK, nullptr, ok);
     945          120 :     const std::string value = attrs.get<std::string>(SUMO_ATTR_VALUE, id.c_str(), ok);
     946          120 :     myJunctionControlBuilder.addAssignment(id, check, value);
     947          120 : }
     948              : 
     949              : 
     950              : void
     951            8 : NLHandler::addFunction(const SUMOSAXAttributes& attrs) {
     952            8 :     bool ok = true;
     953            8 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     954            8 :     const int nArgs = attrs.get<int>(SUMO_ATTR_NARGS, nullptr, ok);
     955            8 :     myJunctionControlBuilder.addFunction(id, nArgs);
     956            8 : }
     957              : 
     958              : void
     959            8 : NLHandler::closeFunction() {
     960            8 :     myJunctionControlBuilder.closeFunction();
     961            8 : }
     962              : 
     963              : void
     964        24681 : NLHandler::addE1Detector(const SUMOSAXAttributes& attrs) {
     965        24681 :     myCurrentIsBroken = false;
     966        24681 :     bool ok = true;
     967              :     // get the id, report an error if not given or empty...
     968        24681 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     969        24681 :     if (!ok) {
     970           12 :         myCurrentIsBroken = true;
     971           12 :         return;
     972              :     }
     973        24669 :     const SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
     974        24669 :     const double position = attrs.get<double>(SUMO_ATTR_POSITION, id.c_str(), ok);
     975        24669 :     const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, 0);
     976        24669 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     977        24675 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     978        24675 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
     979        49344 :     const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
     980        24669 :     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
     981        24669 :     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
     982        24675 :     const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
     983        24669 :     const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
     984        24669 :     if (!ok) {
     985           60 :         myCurrentIsBroken = true;
     986              :         return;
     987              :     }
     988              :     try {
     989        49218 :         Parameterised* det = myDetectorBuilder.buildInductLoop(id, lane, position, length, period,
     990        49218 :                              FileHelpers::checkForRelativity(file, getFileName()),
     991        24579 :                              friendlyPos, name, vTypes, nextEdges, detectPersons);
     992        24579 :         myLastParameterised.push_back(det);
     993           30 :     } catch (InvalidArgument& e) {
     994           18 :         myCurrentIsBroken = true;
     995           18 :         WRITE_ERROR(e.what());
     996           24 :     } catch (IOError& e) {
     997            6 :         myCurrentIsBroken = true;
     998            6 :         WRITE_ERROR(e.what());
     999            6 :     }
    1000              : }
    1001              : 
    1002              : 
    1003              : void
    1004         1130 : NLHandler::addInstantE1Detector(const SUMOSAXAttributes& attrs) {
    1005         1130 :     myCurrentIsBroken = false;
    1006         1130 :     bool ok = true;
    1007              :     // get the id, report an error if not given or empty...
    1008         1130 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1009         1130 :     if (!ok) {
    1010            8 :         myCurrentIsBroken = true;
    1011            8 :         return;
    1012              :     }
    1013         1122 :     const double position = attrs.get<double>(SUMO_ATTR_POSITION, id.c_str(), ok);
    1014         1122 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
    1015         1122 :     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
    1016         1122 :     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
    1017         1122 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
    1018         1122 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
    1019         1122 :     const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
    1020         1122 :     const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
    1021         1122 :     const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
    1022         1122 :     if (!ok) {
    1023           28 :         myCurrentIsBroken = true;
    1024              :         return;
    1025              :     }
    1026              :     try {
    1027         2200 :         Parameterised* det = myDetectorBuilder.buildInstantInductLoop(id, lane, position, FileHelpers::checkForRelativity(file, getFileName()), friendlyPos, name, vTypes, nextEdges, detectPersons);
    1028         1082 :         myLastParameterised.push_back(det);
    1029           12 :     } catch (InvalidArgument& e) {
    1030            8 :         WRITE_ERROR(e.what());
    1031           12 :     } catch (IOError& e) {
    1032            4 :         WRITE_ERROR(e.what());
    1033            4 :     }
    1034         1094 :     myCurrentIsBroken = true;
    1035              : }
    1036              : 
    1037              : 
    1038              : void
    1039          144 : NLHandler::addVTypeProbeDetector(const SUMOSAXAttributes& attrs) {
    1040          144 :     WRITE_WARNING(TL("VTypeProbes are deprecated. Use fcd-output devices (assigned to the vType) instead."));
    1041          144 :     bool ok = true;
    1042          144 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1043          144 :     SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
    1044          288 :     std::string type = attrs.getStringSecure(SUMO_ATTR_TYPE, "");
    1045          144 :     std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
    1046          144 :     if (!ok) {
    1047              :         return;
    1048              :     }
    1049              :     try {
    1050          198 :         myDetectorBuilder.buildVTypeProbe(id, type, period, FileHelpers::checkForRelativity(file, getFileName()));
    1051           18 :     } catch (InvalidArgument& e) {
    1052           12 :         WRITE_ERROR(e.what());
    1053           18 :     } catch (IOError& e) {
    1054            6 :         WRITE_ERROR(e.what());
    1055            6 :     }
    1056              : }
    1057              : 
    1058              : 
    1059              : void
    1060          478 : NLHandler::addRouteProbeDetector(const SUMOSAXAttributes& attrs) {
    1061          478 :     bool ok = true;
    1062          478 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1063          478 :     SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
    1064          478 :     SUMOTime begin = attrs.getOptSUMOTimeReporting(SUMO_ATTR_BEGIN, id.c_str(), ok, -1);
    1065          478 :     std::string edge = attrs.get<std::string>(SUMO_ATTR_EDGE, id.c_str(), ok);
    1066          478 :     std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
    1067          478 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
    1068          478 :     if (!ok) {
    1069              :         return;
    1070              :     }
    1071              :     try {
    1072          478 :         myDetectorBuilder.buildRouteProbe(id, edge, period, begin,
    1073          956 :                                           FileHelpers::checkForRelativity(file, getFileName()), vTypes);
    1074            0 :     } catch (InvalidArgument& e) {
    1075            0 :         WRITE_ERROR(e.what());
    1076            0 :     } catch (IOError& e) {
    1077            0 :         WRITE_ERROR(e.what());
    1078            0 :     }
    1079              : }
    1080              : 
    1081              : 
    1082              : 
    1083              : void
    1084         3166 : NLHandler::addE2Detector(const SUMOSAXAttributes& attrs) {
    1085         3166 :     myCurrentIsBroken = false;
    1086              :     // check whether this is a detector connected to a tls and optionally to a link
    1087         3166 :     bool ok = true;
    1088         3166 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1089         3197 :     const std::string lsaid = attrs.getOpt<std::string>(SUMO_ATTR_TLID, id.c_str(), ok, "");
    1090         6363 :     const std::string toLane = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
    1091         3166 :     const SUMOTime haltingTimeThreshold = attrs.getOptSUMOTimeReporting(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, TIME2STEPS(1));
    1092         3166 :     const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, 5.0f / 3.6f);
    1093         3166 :     const double jamDistThreshold = attrs.getOpt<double>(SUMO_ATTR_JAM_DIST_THRESHOLD, id.c_str(), ok, 10.0f);
    1094         3166 :     double position = attrs.getOpt<double>(SUMO_ATTR_POSITION, id.c_str(), ok, std::numeric_limits<double>::max());
    1095         3166 :     const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, std::numeric_limits<double>::max());
    1096         3166 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
    1097         3166 :     const bool showDetector = attrs.getOpt<bool>(SUMO_ATTR_SHOW_DETECTOR, id.c_str(), ok, true);
    1098         6363 :     const std::string contStr = attrs.getOpt<std::string>(SUMO_ATTR_CONT, id.c_str(), ok, "");
    1099         3166 :     if (contStr != "") {
    1100            0 :         WRITE_WARNINGF(TL("Ignoring deprecated argument 'cont' for E2 detector '%'"), id);
    1101              :     }
    1102         6363 :     std::string lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, id.c_str(), ok, "");
    1103         3166 :     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
    1104         3197 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
    1105         3197 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
    1106         6363 :     const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
    1107              : 
    1108         3166 :     double endPosition = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, std::numeric_limits<double>::max());
    1109         3197 :     const std::string lanes = attrs.getOpt<std::string>(SUMO_ATTR_LANES, id.c_str(), ok, ""); // lanes has priority to lane
    1110         3197 :     const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
    1111         3166 :     const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
    1112         3166 :     if (!ok) {
    1113           36 :         myCurrentIsBroken = true;
    1114           36 :         return;
    1115              :     }
    1116              : 
    1117              :     bool lanesGiven = lanes != "";
    1118              :     bool laneGiven = lane != "";
    1119         3130 :     if (!(lanesGiven || laneGiven)) {
    1120              :         // in absence of any lane-specification assume specification by id
    1121           39 :         WRITE_WARNING(TL("Trying to specify detector's lane by the given id since the argument 'lane' is missing."))
    1122              :         lane = id;
    1123              :         laneGiven = true;
    1124              :     }
    1125         3130 :     bool lengthGiven = length != std::numeric_limits<double>::max();
    1126              :     bool posGiven = position != std::numeric_limits<double>::max();
    1127         3130 :     bool endPosGiven = endPosition != std::numeric_limits<double>::max();
    1128              :     bool lsaGiven = lsaid != "";
    1129              :     bool toLaneGiven = toLane != "";
    1130              : 
    1131         3130 :     MSLane* clane = nullptr;
    1132              :     std::vector<MSLane*> clanes;
    1133         3130 :     if (lanesGiven) {
    1134              :         // If lanes is given, endPos and startPos are required. lane, and length are ignored
    1135          252 :         std::string seps = " ,\t\n";
    1136          504 :         StringTokenizer st = StringTokenizer(lanes, seps, true);
    1137              : //        std::cout << "Parsing lanes..." << std::endl;
    1138          852 :         while (st.hasNext()) {
    1139          600 :             std::string nextLaneID = st.next();
    1140              : //            std::cout << "Next: " << nextLaneID << std::endl;
    1141          600 :             if (nextLaneID.find_first_of(seps) != nextLaneID.npos) {
    1142              :                 continue;
    1143              :             }
    1144          600 :             clane = myDetectorBuilder.getLaneChecking(nextLaneID, SUMO_TAG_E2DETECTOR, id);
    1145          600 :             clanes.push_back(clane);
    1146              :         }
    1147          252 :         if (clanes.size() == 0) {
    1148            0 :             throw InvalidArgument("Malformed argument 'lanes' for E2Detector '" + id + "'.\nSpecify 'lanes' as a sequence of lane-IDs separated by whitespace or comma (',')");
    1149              :         }
    1150          252 :         if (laneGiven) {
    1151            0 :             WRITE_WARNING("Ignoring argument 'lane' for E2Detector '" + id + "' since argument 'lanes' was given.\n"
    1152              :                           "Usage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]");
    1153              :         }
    1154          252 :         if (lengthGiven) {
    1155            0 :             WRITE_WARNING("Ignoring argument 'length' for E2Detector '" + id + "' since argument 'lanes' was given.\n"
    1156              :                           "Usage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]");
    1157              :         }
    1158          252 :         if (!posGiven) {
    1159              :             // assuming start pos == lane start
    1160              :             position = 0;
    1161            3 :             WRITE_WARNINGF(TL("Missing argument 'pos' for E2Detector '%'. Assuming detector start == lane start of lane '%'."), id, clanes[0]->getID());
    1162              :         }
    1163          252 :         if (!endPosGiven) {
    1164              :             // assuming end pos == lane end
    1165            1 :             endPosition = clanes[clanes.size() - 1]->getLength();
    1166            3 :             WRITE_WARNINGF(TL("Missing argument 'endPos' for E2Detector '%'. Assuming detector end == lane end of lane '%'."), id, clanes[clanes.size() - 1]->getID());
    1167              :         }
    1168              : 
    1169          252 :     } else {
    1170         2878 :         if (!laneGiven) {
    1171            0 :             std::stringstream ss;
    1172              :             ss << "Missing argument 'lane' for E2Detector '" << id << "'."
    1173            0 :                << "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
    1174            0 :             throw InvalidArgument(ss.str());
    1175            0 :         }
    1176         2878 :         clane = myDetectorBuilder.getLaneChecking(lane, SUMO_TAG_E2DETECTOR, id);
    1177              : 
    1178         2870 :         if (posGiven) {
    1179              :             // start pos is given
    1180         2753 :             if (endPosGiven && lengthGiven) {
    1181            0 :                 std::stringstream ss;
    1182              :                 ss << "Ignoring argument 'endPos' for E2Detector '" << id << "' since argument 'pos' was given."
    1183            0 :                    << "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
    1184            0 :                 WRITE_WARNING(ss.str());
    1185              :                 endPosition = std::numeric_limits<double>::max();
    1186            0 :             }
    1187         2753 :             if (!lengthGiven && !endPosGiven) {
    1188            4 :                 std::stringstream ss;
    1189            4 :                 ss << "Missing arguments 'length'/'endPos' for E2Detector '" << id << "'. Assuming detector end == lane end of lane '" << lane << "'.";
    1190            8 :                 WRITE_WARNING(ss.str());
    1191              :                 endPosition = clane->getLength();
    1192            4 :             }
    1193          117 :         } else if (endPosGiven) {
    1194              :             // endPos is given, pos is not given
    1195          112 :             if (!lengthGiven) {
    1196            0 :                 std::stringstream ss;
    1197            0 :                 ss << "Missing arguments 'length'/'pos' for E2Detector '" << id << "'. Assuming detector start == lane start of lane '" << lane << "'.";
    1198            0 :                 WRITE_WARNING(ss.str());
    1199            0 :             }
    1200              :         } else {
    1201            5 :             std::stringstream ss;
    1202            5 :             if (lengthGiven && fabs(length - clane->getLength()) > NUMERICAL_EPS) {
    1203              :                 ss << "Incomplete positional specification for E2Detector '" << id << "'."
    1204            5 :                    << "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
    1205           15 :                 throw InvalidArgument(ss.str());
    1206              :             }
    1207              :             endPosition = clane->getLength();
    1208              :             position = 0;
    1209            0 :             ss << "Missing arguments 'pos'/'endPos' for E2Detector '" << id << "'. Assuming that the detector covers the whole lane '" << lane << "'.";
    1210            0 :             WRITE_WARNING(ss.str());
    1211            5 :         }
    1212              :     }
    1213              : 
    1214              :     // Period
    1215              : 
    1216              :     SUMOTime period;
    1217         3117 :     if (!lsaGiven) {
    1218         2560 :         period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
    1219         2560 :         if (!ok) {
    1220            8 :             myCurrentIsBroken = true;
    1221              :             return;
    1222              :         }
    1223              :     } else {
    1224          557 :         period = attrs.getPeriod(id.c_str(), ok, false);
    1225              :     }
    1226              : 
    1227              :     // TLS
    1228              :     MSTLLogicControl::TLSLogicVariants* tlls = nullptr;
    1229         3109 :     if (lsaGiven) {
    1230          557 :         tlls = &myJunctionControlBuilder.getTLLogic(lsaid);
    1231          557 :         if (tlls->getActive() == nullptr) {
    1232            0 :             throw InvalidArgument("The detector '" + id + "' refers to an unknown lsa '" + lsaid + "'.");
    1233              :         }
    1234          557 :         if (period != -1) {
    1235           75 :             WRITE_WARNINGF(TL("Ignoring argument 'period' for E2Detector '%' since argument 'tl' was given."), id);
    1236              :             period = -1;
    1237              :         }
    1238              :     }
    1239              : 
    1240              :     // Link
    1241              :     MSLane* cToLane = nullptr;
    1242         3109 :     if (toLaneGiven) {
    1243            8 :         cToLane = myDetectorBuilder.getLaneChecking(toLane, SUMO_TAG_E2DETECTOR, id);
    1244              :     }
    1245              : 
    1246              :     // File
    1247              :     std::string filename;
    1248              :     try {
    1249         6218 :         filename = FileHelpers::checkForRelativity(file, getFileName());
    1250            0 :     } catch (IOError& e) {
    1251            0 :         WRITE_ERROR(e.what());
    1252            0 :     }
    1253              : 
    1254              :     Parameterised* det;
    1255              :     // Build detector
    1256         3109 :     if (lanesGiven) {
    1257              :         // specification by a lane sequence
    1258          510 :         det = myDetectorBuilder.buildE2Detector(id, clanes, position, endPosition, filename, period,
    1259              :                                                 haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold,
    1260              :                                                 name, vTypes, nextEdges, detectPersons, friendlyPos, showDetector,
    1261              :                                                 tlls, cToLane);
    1262              :     } else {
    1263              :         // specification by start or end lane
    1264         5732 :         det = myDetectorBuilder.buildE2Detector(id, clane, position, endPosition, length, filename, period,
    1265              :                                                 haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold,
    1266              :                                                 name, vTypes, nextEdges, detectPersons, friendlyPos, showDetector,
    1267              :                                                 tlls, cToLane);
    1268              :     }
    1269         3091 :     myLastParameterised.push_back(det);
    1270         3130 : }
    1271              : 
    1272              : 
    1273              : void
    1274         1445 : NLHandler::beginE3Detector(const SUMOSAXAttributes& attrs) {
    1275         1445 :     myCurrentIsBroken = false;
    1276         1445 :     bool ok = true;
    1277         1445 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1278         1445 :     const SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
    1279         1445 :     const SUMOTime haltingTimeThreshold = attrs.getOptSUMOTimeReporting(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, TIME2STEPS(1));
    1280         1445 :     const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, 5.0f / 3.6f);
    1281         1445 :     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
    1282         1445 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
    1283         1445 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
    1284         2890 :     const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
    1285         1445 :     const bool openEntry = attrs.getOpt<bool>(SUMO_ATTR_OPEN_ENTRY, id.c_str(), ok, false);
    1286         1445 :     const bool expectArrival = attrs.getOpt<bool>(SUMO_ATTR_EXPECT_ARRIVAL, id.c_str(), ok, false);
    1287         1445 :     const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
    1288         1445 :     const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
    1289         1445 :     if (!ok) {
    1290           24 :         myCurrentIsBroken = true;
    1291              :         return;
    1292              :     }
    1293              :     try {
    1294         2842 :         Parameterised* det = myDetectorBuilder.beginE3Detector(id,
    1295         2842 :                              FileHelpers::checkForRelativity(file, getFileName()),
    1296         1417 :                              period, haltingSpeedThreshold, haltingTimeThreshold, name, vTypes, nextEdges, detectPersons, openEntry, expectArrival);
    1297         1417 :         myLastParameterised.push_back(det);
    1298            4 :     } catch (InvalidArgument& e) {
    1299            4 :         myCurrentIsBroken = true;
    1300            4 :         WRITE_ERROR(e.what());
    1301            4 :     } catch (IOError& e) {
    1302            0 :         myCurrentIsBroken = true;
    1303            0 :         WRITE_ERROR(e.what());
    1304            0 :     }
    1305              : }
    1306              : 
    1307              : 
    1308              : void
    1309         1791 : NLHandler::addE3Entry(const SUMOSAXAttributes& attrs) {
    1310         1791 :     bool ok = true;
    1311         1791 :     const double position = attrs.get<double>(SUMO_ATTR_POSITION, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
    1312         1791 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, myDetectorBuilder.getCurrentE3ID().c_str(), ok, false);
    1313         1791 :     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
    1314         1791 :     if (!ok) {
    1315              :         return;
    1316              :     }
    1317         1771 :     myDetectorBuilder.addE3Entry(lane, position, friendlyPos);
    1318              : }
    1319              : 
    1320              : 
    1321              : void
    1322         1755 : NLHandler::addE3Exit(const SUMOSAXAttributes& attrs) {
    1323         1755 :     bool ok = true;
    1324         1755 :     const double position = attrs.get<double>(SUMO_ATTR_POSITION, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
    1325         1755 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, myDetectorBuilder.getCurrentE3ID().c_str(), ok, false);
    1326         1755 :     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
    1327         1755 :     if (!ok) {
    1328              :         return;
    1329              :     }
    1330         1735 :     myDetectorBuilder.addE3Exit(lane, position, friendlyPos);
    1331              : }
    1332              : 
    1333              : 
    1334              : void
    1335        23327 : NLHandler::addEdgeLaneMeanData(const SUMOSAXAttributes& attrs, int objecttype) {
    1336        23327 :     bool ok = true;
    1337        23327 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1338        23327 :     const double maxTravelTime = attrs.getOpt<double>(SUMO_ATTR_MAX_TRAVELTIME, id.c_str(), ok, 100000);
    1339        23327 :     const double minSamples = attrs.getOpt<double>(SUMO_ATTR_MIN_SAMPLES, id.c_str(), ok, 0);
    1340        23327 :     const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, POSITION_EPS);
    1341        46658 :     const std::string excludeEmpty = attrs.getOpt<std::string>(SUMO_ATTR_EXCLUDE_EMPTY, id.c_str(), ok, "false");
    1342        23327 :     const bool withInternal = attrs.getOpt<bool>(SUMO_ATTR_WITH_INTERNAL, id.c_str(), ok, false);
    1343        23327 :     const bool trackVehicles = attrs.getOpt<bool>(SUMO_ATTR_TRACK_VEHICLES, id.c_str(), ok, false);
    1344        46658 :     const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
    1345        23327 :     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
    1346        23331 :     const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "performance");
    1347        23331 :     std::string vtypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
    1348        46658 :     const std::string writeAttributes = attrs.getOpt<std::string>(SUMO_ATTR_WRITE_ATTRIBUTES, id.c_str(), ok, "");
    1349        23327 :     const SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, -1);
    1350        23327 :     const SUMOTime begin = attrs.getOptSUMOTimeReporting(SUMO_ATTR_BEGIN, id.c_str(), ok, string2time(OptionsCont::getOptions().getString("begin")));
    1351        46654 :     const SUMOTime end = attrs.getOptSUMOTimeReporting(SUMO_ATTR_END, id.c_str(), ok, string2time(OptionsCont::getOptions().getString("end")));
    1352        23331 :     std::vector<std::string> edgeIDs = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_EDGES, id.c_str(), ok);
    1353        23327 :     const std::string edgesFile = attrs.getOpt<std::string>(SUMO_ATTR_EDGESFILE, id.c_str(), ok, "");
    1354        23331 :     const std::string aggregateStr = attrs.getOpt<std::string>(SUMO_ATTR_AGGREGATE, id.c_str(), ok, "false");
    1355              :     AggregateType aggregate = AggregateType::NO;
    1356        23327 :     if (aggregateStr == "taz") {
    1357              :         aggregate = AggregateType::TAZ;
    1358        23321 :     } else if (StringUtils::toBool(aggregateStr)) {
    1359              :         aggregate = AggregateType::YES;
    1360              :     }
    1361        23327 :     if (!ok) {
    1362              :         return;
    1363              :     }
    1364              :     int detectPersons = 0;
    1365        46624 :     for (std::string mode : StringTokenizer(detectPersonsString).getVector()) {
    1366              :         if (SUMOXMLDefinitions::PersonModeValues.hasString(mode)) {
    1367           38 :             detectPersons |= (int)SUMOXMLDefinitions::PersonModeValues.get(mode);
    1368              :         } else {
    1369            0 :             WRITE_ERRORF(TL("Invalid person mode '%' in edgeData definition '%'"), mode, id);
    1370              :             return;
    1371              :         }
    1372        23291 :     }
    1373        23291 :     if (edgesFile != "") {
    1374           10 :         std::ifstream strm(edgesFile.c_str());
    1375           10 :         if (!strm.good()) {
    1376           12 :             throw ProcessError("Could not load names of edges for edgeData definition '" + id + "' from '" + edgesFile + "'.");
    1377              :         }
    1378           24 :         while (strm.good()) {
    1379              :             std::string name;
    1380           18 :             strm >> name;
    1381              :             // maybe we're loading an edge-selection
    1382           36 :             if (StringUtils::startsWith(name, "edge:")) {
    1383           12 :                 edgeIDs.push_back(name.substr(5));
    1384           12 :             } else if (name != "") {
    1385            6 :                 edgeIDs.push_back(name);
    1386              :             }
    1387              :         }
    1388           10 :     }
    1389              :     std::vector<MSEdge*> edges;
    1390        23400 :     for (const std::string& edgeID : edgeIDs) {
    1391          113 :         MSEdge* edge = MSEdge::dictionary(edgeID);
    1392          113 :         if (edge == nullptr) {
    1393            0 :             WRITE_ERRORF(TL("Unknown edge '%' in edgeData definition '%'"), edgeID, id);
    1394            0 :             return;
    1395              :         }
    1396          113 :         edges.push_back(edge);
    1397              :     }
    1398        23287 :     bool useLanes = objecttype == SUMO_TAG_MEANDATA_LANE;
    1399        23657 :     if (useLanes && MSGlobals::gUseMesoSim && !OptionsCont::getOptions().getBool("meso-lane-queue")) {
    1400         1098 :         WRITE_WARNINGF(TL("LaneData '%' requested for mesoscopic simulation but --meso-lane-queue is not active. Falling back to edgeData."), id);
    1401              :         useLanes = false;
    1402              :     }
    1403              :     try {
    1404        23359 :         myDetectorBuilder.createEdgeLaneMeanData(id, period, begin, end,
    1405              :                 type, useLanes, excludeEmpty, withInternal, trackVehicles, detectPersons,
    1406              :                 maxTravelTime, minSamples, haltingSpeedThreshold, vtypes, writeAttributes, edges, aggregate,
    1407        46502 :                 FileHelpers::checkForRelativity(file, getFileName()));
    1408           72 :     } catch (InvalidArgument& e) {
    1409           72 :         WRITE_ERROR(e.what());
    1410           72 :     } catch (IOError& e) {
    1411            0 :         WRITE_ERROR(e.what());
    1412            0 :     }
    1413        46614 : }
    1414              : 
    1415              : 
    1416              : void
    1417      2937810 : NLHandler::addConnection(const SUMOSAXAttributes& attrs) {
    1418      2937810 :     bool ok = true;
    1419      2937810 :     const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
    1420      2937810 :     const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
    1421      2937810 :     if (!MSGlobals::gUsingInternalLanes && (fromID[0] == ':' || toID[0] == ':')) {
    1422         2732 :         std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
    1423         1366 :         if (tlID != "") {
    1424            0 :             int tlLinkIdx = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
    1425            0 :             myJunctionControlBuilder.getTLLogic(tlID).ignoreLinkIndex(tlLinkIdx);
    1426              :         }
    1427              :         return;
    1428              :     }
    1429              : 
    1430      2936444 :     myCurrentLink = nullptr;
    1431              :     try {
    1432      2936444 :         const int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
    1433      2936444 :         const int toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
    1434      2936444 :         LinkDirection dir = parseLinkDir(attrs.get<std::string>(SUMO_ATTR_DIR, nullptr, ok));
    1435      2936444 :         LinkState state = parseLinkState(attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok));
    1436      5872278 :         const double foeVisibilityDistance = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, state == LINKSTATE_ZIPPER ? 100 : 4.5);
    1437      2936444 :         const bool keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
    1438      2936444 :         const bool indirect = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
    1439      2936444 :         std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
    1440      2936448 :         std::string viaID = attrs.getOpt<std::string>(SUMO_ATTR_VIA, nullptr, ok, "");
    1441              : 
    1442      2936444 :         MSEdge* from = MSEdge::dictionaryHint(fromID, myPreviousEdgeIdx);
    1443      2936444 :         if (from == nullptr) {
    1444          342 :             WRITE_ERRORF(TL("Unknown from-edge '%' in connection."), fromID);
    1445          114 :             return;
    1446              :         }
    1447      2936330 :         myPreviousEdgeIdx = from->getNumericalID();
    1448      2936330 :         MSEdge* to = MSEdge::dictionary(toID);
    1449      2936330 :         if (to == nullptr) {
    1450          324 :             WRITE_ERRORF(TL("Unknown to-edge '%' in connection."), toID);
    1451          108 :             return;
    1452              :         }
    1453      2936222 :         if (fromLaneIdx < 0 || fromLaneIdx >= (int)from->getLanes().size() ||
    1454      5872440 :                 toLaneIdx < 0 || toLaneIdx >= (int)to->getLanes().size()) {
    1455           24 :             WRITE_ERRORF(TL("Invalid lane index in connection from '%' to '%'."), from->getID(), to->getID());
    1456            8 :             return;
    1457              :         }
    1458      2936214 :         MSLane* fromLane = from->getLanes()[fromLaneIdx];
    1459      2936214 :         MSLane* toLane = to->getLanes()[toLaneIdx];
    1460              :         assert(fromLane);
    1461              :         assert(toLane);
    1462              : 
    1463              :         MSTrafficLightLogic* logic = nullptr;
    1464      2936214 :         int tlLinkIdx = -1;
    1465      2936214 :         if (tlID != "") {
    1466       883143 :             tlLinkIdx = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
    1467              :             // make sure that the index is in range
    1468       883143 :             logic = myJunctionControlBuilder.getTLLogic(tlID).getActive();
    1469       882818 :             if ((tlLinkIdx < 0 || tlLinkIdx >= (int)logic->getCurrentPhaseDef().getState().size())
    1470          327 :                     && logic->getLogicType() != TrafficLightType::RAIL_SIGNAL
    1471       883466 :                     && logic->getLogicType() != TrafficLightType::RAIL_CROSSING) {
    1472           24 :                 WRITE_ERROR("Invalid " + toString(SUMO_ATTR_TLLINKINDEX) + " '" + toString(tlLinkIdx) +
    1473              :                             "' in connection controlled by '" + tlID + "'");
    1474            6 :                 return;
    1475              :             }
    1476       883133 :             if (!ok) {
    1477              :                 return;
    1478              :             }
    1479              :         }
    1480              :         double length;
    1481              :         // build the link
    1482              :         MSLane* via = nullptr;
    1483      2936204 :         if (viaID != "" && MSGlobals::gUsingInternalLanes) {
    1484      1022021 :             via = MSLane::dictionary(viaID);
    1485      1022021 :             if (via == nullptr) {
    1486           16 :                 WRITE_ERROR("An unknown lane ('" + viaID +
    1487              :                             "') should be set as a via-lane for lane '" + toLane->getID() + "'.");
    1488            6 :                 return;
    1489              :             }
    1490              :             length = via->getLength();
    1491      1914183 :         } else if (toLane->isCrossing()) {
    1492              :             length = toLane->getLength();
    1493              :         } else {
    1494      1894768 :             length = fromLane->getShape()[-1].distanceTo(toLane->getShape()[0]);
    1495              :         }
    1496      2936198 :         myCurrentLink = new MSLink(fromLane, toLane, via, dir, state, length, foeVisibilityDistance, keepClear, logic, tlLinkIdx, indirect);
    1497      2936198 :         if (via != nullptr) {
    1498      1022015 :             via->addIncomingLane(fromLane, myCurrentLink);
    1499              :         } else {
    1500      1914183 :             toLane->addIncomingLane(fromLane, myCurrentLink);
    1501              :         }
    1502      2936198 :         toLane->addApproachingLane(fromLane, myNetworkVersion < MMVersion(0, 25));
    1503              : 
    1504              :         // if a traffic light is responsible for it, inform the traffic light
    1505              :         // check whether this link is controlled by a traffic light
    1506              :         // we can not reuse logic here because it might be an inactive one
    1507      2936198 :         if (tlID != "") {
    1508       883133 :             myJunctionControlBuilder.getTLLogic(tlID).addLink(myCurrentLink, fromLane, tlLinkIdx);
    1509              :         }
    1510              :         // add the link
    1511      2936198 :         fromLane->addLink(myCurrentLink);
    1512              : 
    1513            4 :     } catch (InvalidArgument& e) {
    1514            4 :         WRITE_ERROR(e.what());
    1515            4 :     }
    1516              : }
    1517              : 
    1518              : 
    1519              : void
    1520            6 : NLHandler::addConflict(const SUMOSAXAttributes& attrs) {
    1521            6 :     if (myCurrentLink == nullptr) {
    1522            0 :         throw InvalidArgument(toString(SUMO_TAG_CONFLICT) + " must occur within a " + toString(SUMO_TAG_CONNECTION) + " element");
    1523              :     }
    1524            6 :     if (!MSGlobals::gUsingInternalLanes) {
    1525            0 :         return;
    1526              :     }
    1527            6 :     bool ok = true;
    1528            6 :     const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
    1529            6 :     const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
    1530            6 :     const int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
    1531            6 :     const int toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
    1532            6 :     double startPos = attrs.get<double>(SUMO_ATTR_STARTPOS, nullptr, ok);
    1533            6 :     double endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, nullptr, ok);
    1534            6 :     MSEdge* from = MSEdge::dictionary(fromID);
    1535            6 :     if (from == nullptr) {
    1536            0 :         WRITE_ERRORF(TL("Unknown from-edge '%' in conflict."), fromID);
    1537            0 :         return;
    1538              :     }
    1539            6 :     MSEdge* to = MSEdge::dictionary(toID);
    1540            6 :     if (to == nullptr) {
    1541            0 :         WRITE_ERRORF(TL("Unknown to-edge '%' in conflict."), toID);
    1542            0 :         return;
    1543              :     }
    1544            6 :     if (fromLaneIdx < 0 || fromLaneIdx >= (int)from->getLanes().size() ||
    1545           12 :             toLaneIdx < 0 || toLaneIdx >= (int)to->getLanes().size()) {
    1546            0 :         WRITE_ERRORF(TL("Invalid lane index in conflict with '%' to '%'."), from->getID(), to->getID());
    1547            0 :         return;
    1548              :     }
    1549            6 :     MSLane* fromLane = from->getLanes()[fromLaneIdx];
    1550            6 :     MSLane* toLane = to->getLanes()[toLaneIdx];
    1551              :     assert(fromLane);
    1552              :     assert(toLane);
    1553            6 :     myCurrentLink->addCustomConflict(fromLane, toLane, startPos, endPos);
    1554              : }
    1555              : 
    1556              : 
    1557              : LinkDirection
    1558      2936444 : NLHandler::parseLinkDir(const std::string& dir) {
    1559              :     if (SUMOXMLDefinitions::LinkDirections.hasString(dir)) {
    1560      2936444 :         return SUMOXMLDefinitions::LinkDirections.get(dir);
    1561              :     } else {
    1562            0 :         throw InvalidArgument("Unrecognised link direction '" + dir + "'.");
    1563              :     }
    1564              : }
    1565              : 
    1566              : 
    1567              : LinkState
    1568      2936444 : NLHandler::parseLinkState(const std::string& state) {
    1569              :     if (SUMOXMLDefinitions::LinkStates.hasString(state)) {
    1570      2936444 :         return SUMOXMLDefinitions::LinkStates.get(state);
    1571              :     } else {
    1572            0 :         if (state == "t") { // legacy networks
    1573              :             // WRITE_WARNING(TL("Obsolete link state 't'. Use 'o' instead"));
    1574              :             return LINKSTATE_TL_OFF_BLINKING;
    1575              :         } else {
    1576            0 :             throw InvalidArgument("Unrecognised link state '" + state + "'.");
    1577              :         }
    1578              :     }
    1579              : }
    1580              : 
    1581              : 
    1582              : // ----------------------------------
    1583              : void
    1584        42500 : NLHandler::setLocation(const SUMOSAXAttributes& attrs) {
    1585        42500 :     if (myNetIsLoaded) {
    1586              :         //WRITE_WARNING(TL("POIs and Polygons should be loaded using option --po-files"))
    1587           22 :         return;
    1588              :     }
    1589        42478 :     bool ok = true;
    1590        42478 :     PositionVector s = attrs.get<PositionVector>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
    1591        42478 :     Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
    1592        42478 :     Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
    1593        42478 :     std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
    1594        42478 :     if (ok) {
    1595        42478 :         Position networkOffset = s[0];
    1596        42478 :         GeoConvHelper::init(proj, networkOffset, origBoundary, convBoundary);
    1597        84956 :         if (OptionsCont::getOptions().getBool("fcd-output.geo") && !GeoConvHelper::getFinal().usingGeoProjection()) {
    1598            0 :             WRITE_WARNING(TL("no valid geo projection loaded from network. fcd-output.geo will not work"));
    1599              :         }
    1600              :     }
    1601        42478 : }
    1602              : 
    1603              : 
    1604              : void
    1605        14604 : NLHandler::addDistrict(const SUMOSAXAttributes& attrs) {
    1606        14604 :     bool ok = true;
    1607        14604 :     myCurrentIsBroken = false;
    1608              :     // get the id, report an error if not given or empty...
    1609        14604 :     myCurrentDistrictID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
    1610        14604 :     if (!ok) {
    1611            0 :         myCurrentIsBroken = true;
    1612            0 :         return;
    1613              :     }
    1614              :     try {
    1615        14604 :         const std::string sinkID = myCurrentDistrictID + "-sink";
    1616        14604 :         const std::string sourceID = myCurrentDistrictID + "-source";
    1617              : 
    1618        14604 :         MSEdge* sink = MSEdge::dictionary(sinkID);
    1619        14604 :         if (sink == nullptr) {
    1620        29166 :             sink = myEdgeControlBuilder.buildEdge(sinkID, SumoXMLEdgeFunc::CONNECTOR, "", "", "", -1, 0);
    1621        14583 :             MSEdge::dictionary(sinkID, sink);
    1622        14583 :             sink->initialize(new std::vector<MSLane*>());
    1623              :         } else {
    1624           42 :             if (OptionsCont::getOptions().getBool("junction-taz")
    1625           21 :                     && myNet.getJunctionControl().get(myCurrentDistrictID) != nullptr) {
    1626              :                 // overwrite junction taz
    1627           42 :                 sink->resetTAZ(myNet.getJunctionControl().get(myCurrentDistrictID));
    1628           63 :                 WRITE_WARNINGF(TL("Replacing junction-taz '%' with loaded TAZ."), myCurrentDistrictID);
    1629              :             } else {
    1630            0 :                 throw InvalidArgument("Another edge with the id '" + sinkID + "' exists.");
    1631              :             }
    1632              :         }
    1633        14604 :         MSEdge* source = MSEdge::dictionary(sourceID);
    1634        14604 :         if (source == nullptr) {
    1635        29166 :             source = myEdgeControlBuilder.buildEdge(sourceID, SumoXMLEdgeFunc::CONNECTOR, "", "", "", -1, 0);
    1636        14583 :             MSEdge::dictionary(sourceID, source);
    1637        14583 :             source->initialize(new std::vector<MSLane*>());
    1638              :         } else {
    1639           42 :             if (OptionsCont::getOptions().getBool("junction-taz")
    1640           21 :                     && myNet.getJunctionControl().get(myCurrentDistrictID) != nullptr) {
    1641              :                 // overwrite junction taz
    1642           42 :                 source->resetTAZ(myNet.getJunctionControl().get(myCurrentDistrictID));
    1643              :             } else {
    1644            0 :                 throw InvalidArgument("Another edge with the id '" + sourceID + "' exists.");
    1645              :             }
    1646              :         }
    1647              :         sink->setOtherTazConnector(source);
    1648              :         source->setOtherTazConnector(sink);
    1649        14604 :         const std::vector<std::string>& desc = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_EDGES, myCurrentDistrictID.c_str(), ok);
    1650        17413 :         for (const std::string& eID : desc) {
    1651         2809 :             MSEdge* edge = MSEdge::dictionary(eID);
    1652              :             // check whether the edge exists
    1653         2809 :             if (edge == nullptr) {
    1654            0 :                 throw InvalidArgument("The edge '" + eID + "' within district '" + myCurrentDistrictID + "' is not known.");
    1655              :             }
    1656         2809 :             source->addSuccessor(edge);
    1657         2809 :             edge->addSuccessor(sink);
    1658              :         }
    1659        14604 :         source->setParameter("taz", myCurrentDistrictID);
    1660        14604 :         sink->setParameter("taz", myCurrentDistrictID);
    1661        14604 :         RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, myCurrentDistrictID.c_str(), ok, RGBColor::parseColor("1.0,.33,.33"));
    1662        14604 :         const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, myCurrentDistrictID.c_str(), ok, "");
    1663        29208 :         source->setParameter("tazColor", toString(color));
    1664        29208 :         sink->setParameter("tazColor", toString(color));
    1665              : 
    1666        14604 :         if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
    1667        13239 :             PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentDistrictID.c_str(), ok);
    1668        13239 :             const bool fill = attrs.getOpt<bool>(SUMO_ATTR_FILL, myCurrentDistrictID.c_str(), ok, false);
    1669        13239 :             if (shape.size() != 0) {
    1670        26478 :                 if (!myNet.getShapeContainer().addPolygon(myCurrentDistrictID, "taz", color, 0, 0, "", shape, false, fill, 1.0, false, name)) {
    1671            0 :                     WRITE_WARNINGF(TL("Skipping visualization of taz '%', polygon already exists."), myCurrentDistrictID);
    1672              :                 } else {
    1673        13239 :                     myLastParameterised.push_back(myNet.getShapeContainer().getPolygons().get(myCurrentDistrictID));
    1674        13239 :                     myCurrentIsBroken = false;
    1675              :                 }
    1676              :             }
    1677        13239 :         }
    1678        14604 :     } catch (InvalidArgument& e) {
    1679            0 :         WRITE_ERROR(e.what());
    1680            0 :         myCurrentIsBroken = true;
    1681            0 :     }
    1682              : }
    1683              : 
    1684              : 
    1685              : void
    1686        52619 : NLHandler::addDistrictEdge(const SUMOSAXAttributes& attrs, bool isSource) {
    1687        52619 :     if (myCurrentIsBroken) {
    1688              :         // earlier error
    1689            0 :         return;
    1690              :     }
    1691        52619 :     bool ok = true;
    1692        52619 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, myCurrentDistrictID.c_str(), ok);
    1693        52619 :     MSEdge* succ = MSEdge::dictionary(id);
    1694        52619 :     if (succ != nullptr) {
    1695              :         // connect edge
    1696        52619 :         if (isSource) {
    1697        52612 :             MSEdge::dictionary(myCurrentDistrictID + "-source")->addSuccessor(succ);
    1698              :         } else {
    1699        52626 :             succ->addSuccessor(MSEdge::dictionary(myCurrentDistrictID + "-sink"));
    1700              :         }
    1701              :     } else {
    1702            0 :         WRITE_ERRORF(TL("At district '%': succeeding edge '%' does not exist."), myCurrentDistrictID, id);
    1703              :     }
    1704              : }
    1705              : 
    1706              : 
    1707              : void
    1708          387 : NLHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
    1709          387 :     bool ok = true;
    1710          387 :     const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
    1711          387 :     if (ok) {
    1712         1955 :         for (const std::string& eID : edgeIDs) {
    1713         1568 :             MSEdge* edge = MSEdge::dictionary(eID);
    1714         1568 :             if (edge == nullptr) {
    1715            0 :                 WRITE_ERRORF(TL("Unknown edge '%' in roundabout"), eID);
    1716              :             } else {
    1717              :                 edge->markAsRoundabout();
    1718              :             }
    1719              :         }
    1720              :     }
    1721          387 : }
    1722              : 
    1723              : 
    1724              : void
    1725           15 : NLHandler::addDeadlock(const SUMOSAXAttributes& attrs) {
    1726           15 :     bool ok = true;
    1727           15 :     std::vector<std::string> signalIDs = attrs.get<std::vector<std::string>>(SUMO_ATTR_SIGNALS, nullptr, ok);
    1728              :     std::vector<const MSRailSignal*> signals;
    1729           78 :     for (const std::string& id : signalIDs) {
    1730           63 :         const MSTrafficLightLogic* tll = myJunctionControlBuilder.getTLLogicControlToUse().getActive(id);
    1731           63 :         const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(tll);
    1732           63 :         if (rs != nullptr) {
    1733           63 :             signals.push_back(rs);
    1734              :         } else {
    1735            0 :             throw InvalidArgument("Rail signal '" + toString(id) + "' in " + toString(SUMO_TAG_DEADLOCK) + " is not known");
    1736              :         }
    1737              :     }
    1738           15 :     MSRailSignalControl::getInstance().addDeadlockCheck(signals);
    1739           15 : }
    1740              : 
    1741              : // ----------------------------------
    1742              : void
    1743         1445 : NLHandler::endE3Detector() {
    1744              :     try {
    1745         1445 :         myDetectorBuilder.endE3Detector();
    1746            0 :     } catch (InvalidArgument& e) {
    1747            0 :         WRITE_ERROR(e.what());
    1748            0 :     }
    1749         1437 : }
    1750              : 
    1751              : 
    1752              : void
    1753          340 : NLHandler::closeWAUT() {
    1754          340 :     if (!myCurrentIsBroken) {
    1755              :         try {
    1756          324 :             myJunctionControlBuilder.getTLLogicControlToUse().closeWAUT(myCurrentWAUTID);
    1757            0 :         } catch (InvalidArgument& e) {
    1758            0 :             WRITE_ERROR(e.what());
    1759            0 :             myCurrentIsBroken = true;
    1760            0 :         }
    1761              :     }
    1762          340 :     myCurrentWAUTID = "";
    1763          340 : }
    1764              : 
    1765              : 
    1766              : Position
    1767          893 : NLShapeHandler::getLanePos(const std::string& poiID, const std::string& laneID, double lanePos, bool friendlyPos, double lanePosLat) {
    1768          893 :     MSLane* lane = MSLane::dictionary(laneID);
    1769          893 :     if (lane == nullptr) {
    1770            0 :         WRITE_ERRORF(TL("Lane '%' to place poi '%' on is not known."), laneID, poiID);
    1771            0 :         return Position::INVALID;
    1772              :     }
    1773          893 :     if (lanePos < 0) {
    1774            1 :         lanePos = lane->getLength() + lanePos;
    1775              :     }
    1776          893 :     if ((lanePos < 0) && friendlyPos) {
    1777            0 :         lanePos = 0;
    1778              :     }
    1779          893 :     if ((lanePos > lane->getLength()) && friendlyPos) {
    1780            0 :         lanePos = lane->getLength();
    1781              :     }
    1782          893 :     if (lanePos < 0 || lanePos > lane->getLength()) {
    1783            0 :         WRITE_WARNINGF(TL("lane position % for poi '%' is not valid."), toString(lanePos), poiID);
    1784              :     }
    1785          893 :     return lane->geometryPositionAtOffset(lanePos, -lanePosLat);
    1786              : }
    1787              : 
    1788              : 
    1789              : Parameterised*
    1790          678 : NLHandler::addPredecessorConstraint(int element, const SUMOSAXAttributes& attrs, MSRailSignal* rs) {
    1791          678 :     if (rs == nullptr) {
    1792            0 :         throw InvalidArgument("Rail signal '" + toString((SumoXMLTag)element) + "' constraint must occur within a railSignalConstraints element");
    1793              :     }
    1794          678 :     bool ok = true;
    1795          678 :     const std::string tripId = attrs.get<std::string>(SUMO_ATTR_TRIP_ID, nullptr, ok);
    1796          678 :     const std::string signalID = attrs.get<std::string>(SUMO_ATTR_TLID, nullptr, ok);
    1797          678 :     const std::string foesString = attrs.get<std::string>(SUMO_ATTR_FOES, nullptr, ok);
    1798         1356 :     const std::vector<std::string> foes = StringTokenizer(foesString).getVector();
    1799          678 :     const int limit = attrs.getOpt<int>(SUMO_ATTR_LIMIT, nullptr, ok, (int)foes.size());
    1800          678 :     const bool active = attrs.getOpt<bool>(SUMO_ATTR_ACTIVE, nullptr, ok, true);
    1801              : 
    1802          678 :     if (!MSNet::getInstance()->getTLSControl().knows(signalID)) {
    1803            0 :         throw InvalidArgument("Rail signal '" + signalID + "' in railSignalConstraints is not known");
    1804              :     }
    1805          678 :     MSRailSignal* signal = dynamic_cast<MSRailSignal*>(MSNet::getInstance()->getTLSControl().get(signalID).getDefault());
    1806          678 :     if (signal == nullptr) {
    1807            0 :         throw InvalidArgument("Traffic light '" + signalID + "' is not a rail signal");
    1808              :     }
    1809              :     MSRailSignalConstraint::ConstraintType type;
    1810          678 :     switch (element) {
    1811              :         case SUMO_TAG_PREDECESSOR:
    1812              :             type = MSRailSignalConstraint::ConstraintType::PREDECESSOR;
    1813              :             break;
    1814              :         case SUMO_TAG_INSERTION_PREDECESSOR:
    1815              :             type = MSRailSignalConstraint::ConstraintType::INSERTION_PREDECESSOR;
    1816              :             break;
    1817              :         case SUMO_TAG_FOE_INSERTION:
    1818              :             type = MSRailSignalConstraint::ConstraintType::FOE_INSERTION;
    1819              :             break;
    1820              :         case SUMO_TAG_INSERTION_ORDER:
    1821              :             type = MSRailSignalConstraint::ConstraintType::INSERTION_ORDER;
    1822              :             break;
    1823              :         case SUMO_TAG_BIDI_PREDECESSOR:
    1824              :             type = MSRailSignalConstraint::ConstraintType::BIDI_PREDECESSOR;
    1825              :             break;
    1826            0 :         default:
    1827            0 :             throw InvalidArgument("Unsupported rail signal constraint '" + toString((SumoXMLTag)element) + "'");
    1828              :     }
    1829              :     Parameterised* result = nullptr;
    1830          678 :     if (ok) {
    1831         1366 :         for (const std::string& foe : foes) {
    1832          688 :             MSRailSignalConstraint* c = new MSRailSignalConstraint_Predecessor(type, signal, foe, limit, active);
    1833          688 :             rs->addConstraint(tripId, c);
    1834              :             // XXX if there are multiple foes, only one constraint will receive the parameters
    1835              :             result = c;
    1836              :         }
    1837              :     }
    1838          678 :     return result;
    1839          678 : }
    1840              : 
    1841              : 
    1842              : int
    1843        30402 : NLHandler::parseDetectPersons(const std::string& detectPersonsString, const std::string& id, bool& ok) {
    1844              :     int detectPersons = 0;
    1845        61331 :     for (std::string mode : StringTokenizer(detectPersonsString).getVector()) {
    1846              :         if (SUMOXMLDefinitions::PersonModeValues.hasString(mode)) {
    1847          527 :             detectPersons |= (int)SUMOXMLDefinitions::PersonModeValues.get(mode);
    1848              :         } else {
    1849           18 :             WRITE_ERRORF(TL("Invalid person mode '%' in E1 detector definition '%'"), mode, id);
    1850            6 :             ok = false;
    1851              :             return 0;
    1852              :         }
    1853        30402 :     }
    1854        30396 :     return detectPersons;
    1855              : }
    1856              : 
    1857              : 
    1858              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1