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

Generated by: LCOV version 2.0-1