LCOV - code coverage report
Current view: top level - src/netload - NLHandler.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 963 1094 88.0 %
Date: 2024-05-04 15:27:10 Functions: 43 44 97.7 %

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

Generated by: LCOV version 1.14