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

Generated by: LCOV version 2.0-1