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

Generated by: LCOV version 2.0-1