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

Generated by: LCOV version 2.0-1