LCOV - code coverage report
Current view: top level - src/netload - NLHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.9 % 1111 988
Test Date: 2026-07-05 15:55:58 Functions: 97.8 % 45 44

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

Generated by: LCOV version 2.0-1