LCOV - code coverage report
Current view: top level - src/netload - NLHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.5 % 1105 978
Test Date: 2024-11-20 15:55:46 Functions: 97.8 % 45 44

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

Generated by: LCOV version 2.0-1