LCOV - code coverage report
Current view: top level - src/microsim - MSRouteHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 89.6 % 1134 1016
Test Date: 2024-11-22 15:46:21 Functions: 97.6 % 42 41

            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    MSRouteHandler.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Sascha Krieg
      18              : /// @author  Michael Behrisch
      19              : /// @author  Johannes Rummel
      20              : /// @date    Mon, 9 Jul 2001
      21              : ///
      22              : // Parser and container for routes during their loading
      23              : /****************************************************************************/
      24              : #include <config.h>
      25              : 
      26              : #include "MSRouteHandler.h"
      27              : #include <microsim/transportables/MSTransportableControl.h>
      28              : #include <microsim/transportables/MSPModel.h>
      29              : #include <microsim/transportables/MSStageDriving.h>
      30              : #include <microsim/transportables/MSStageTranship.h>
      31              : #include <microsim/transportables/MSStageTrip.h>
      32              : #include <microsim/transportables/MSStageWaiting.h>
      33              : #include <microsim/transportables/MSStageWalking.h>
      34              : #include <microsim/MSEdge.h>
      35              : #include <microsim/MSLane.h>
      36              : #include <microsim/MSInsertionControl.h>
      37              : #include <microsim/MSStoppingPlace.h>
      38              : #include <microsim/MSVehicleControl.h>
      39              : #include <microsim/MSEventControl.h>
      40              : #include <microsim/Command_RouteReplacement.h>
      41              : #include <utils/common/StringTokenizer.h>
      42              : #include <utils/common/StringUtils.h>
      43              : #include <utils/options/OptionsCont.h>
      44              : #include <utils/vehicle/SUMOVehicleParserHelper.h>
      45              : 
      46              : // ===========================================================================
      47              : // static members
      48              : // ===========================================================================
      49              : SumoRNG MSRouteHandler::myParsingRNG("routehandler");
      50              : 
      51              : 
      52              : // ===========================================================================
      53              : // method definitions
      54              : // ===========================================================================
      55        79719 : MSRouteHandler::MSRouteHandler(const std::string& file, bool addVehiclesDirectly) :
      56              :     SUMORouteHandler(file, addVehiclesDirectly ? "" : "routes", true),
      57       159438 :     MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
      58       159438 :                OptionsCont::getOptions().getBool("mapmatch.taz"),
      59        79719 :                OptionsCont::getOptions().getFloat("mapmatch.distance"),
      60              :                MsgHandler::getErrorInstance()),
      61        79719 :     myActiveRouteRepeat(0),
      62        79719 :     myActiveRoutePeriod(0),
      63        79719 :     myActiveRoutePermanent(false),
      64        79719 :     myActiveType(ObjectTypeEnum::UNDEFINED),
      65        79719 :     myHaveVia(false),
      66        79719 :     myActiveTransportablePlan(nullptr),
      67        79719 :     myAddVehiclesDirectly(addVehiclesDirectly),
      68        79719 :     myCurrentVTypeDistribution(nullptr),
      69        79719 :     myCurrentRouteDistribution(nullptr),
      70        79719 :     myAmLoadingState(false),
      71        79719 :     myScaleSuffix(OptionsCont::getOptions().getString("scale-suffix")),
      72        79719 :     myReplayRerouting(OptionsCont::getOptions().getBool("replay-rerouting")),
      73       354360 :     myStartTriggeredInFlow(false) {
      74        79719 :     myActiveRoute.reserve(100);
      75        79719 : }
      76              : 
      77              : 
      78       186168 : MSRouteHandler::~MSRouteHandler() {}
      79              : 
      80              : 
      81              : void
      82         1735 : MSRouteHandler::deleteActivePlanAndVehicleParameter() {
      83         1735 :     if (myActiveTransportablePlan != nullptr) {
      84          365 :         for (MSStage* const s : *myActiveTransportablePlan) {
      85          212 :             delete s;
      86              :         }
      87          153 :         delete myActiveTransportablePlan;
      88              :     }
      89         1735 :     delete myVehicleParameter;
      90         1735 :     resetActivePlanAndVehicleParameter();
      91         1735 : }
      92              : 
      93              : 
      94              : void
      95        44327 : MSRouteHandler::resetActivePlanAndVehicleParameter() {
      96        44327 :     myVehicleParameter = nullptr;
      97        44327 :     myActiveTransportablePlan = nullptr;
      98        44327 :     myActiveType = ObjectTypeEnum::UNDEFINED;
      99        44327 : }
     100              : 
     101              : 
     102              : void
     103        64684 : MSRouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs) {
     104        64684 :     const std::string element = toString(tag);
     105              :     myActiveRoute.clear();
     106        64684 :     bool useTaz = OptionsCont::getOptions().getBool("with-taz");
     107        64684 :     if (useTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
     108         1344 :         WRITE_WARNINGF(TL("Taz usage was requested but no taz present in % '%'!"), element, myVehicleParameter->id);
     109              :         useTaz = false;
     110              :     }
     111        64684 :     bool ok = true;
     112        64736 :     const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
     113              :     SUMOVehicleClass vClass = SVC_PASSENGER;
     114        64684 :     auto& vc = MSNet::getInstance()->getVehicleControl();
     115        64684 :     if (!vc.getVTypeDistribution(myVehicleParameter->vtypeid)) {
     116        63888 :         MSVehicleType* const type = vc.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     117        63888 :         if (type != nullptr) {
     118        63888 :             vClass = type->getParameter().vehicleClass;
     119              :         }
     120              :     }
     121              :     // from-attributes
     122        82822 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_FROM) && !attrs.hasAttribute(SUMO_ATTR_FROMXY) && !attrs.hasAttribute(SUMO_ATTR_FROMLONLAT))) &&
     123        33639 :             (attrs.hasAttribute(SUMO_ATTR_FROM_TAZ) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION))) {
     124         3561 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION);
     125         6198 :         const std::string tazType = useJunction ? "junction" : "taz";
     126         6198 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM_TAZ, myVehicleParameter->id.c_str(), ok);
     127         3593 :         const MSEdge* fromTaz = MSEdge::dictionary(tazID + "-source");
     128         3561 :         if (fromTaz == nullptr) {
     129           21 :             throw ProcessError("Source " + tazType + " '" + tazID + "' not known " + rid + "!"
     130           42 :                                + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
     131         3540 :         } else if (fromTaz->getNumSuccessors() == 0 && tag != SUMO_TAG_PERSON) {
     132           22 :             throw ProcessError("Source " + tazType + " '" + tazID + "' has no outgoing edges " + rid + "!");
     133              :         } else {
     134         3529 :             myActiveRoute.push_back(fromTaz);
     135              :         }
     136        61123 :     } else if (attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
     137          168 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMXY, myVehicleParameter->id.c_str(), ok), false, vClass, myActiveRoute, rid, true, ok);
     138          168 :         if (myMapMatchTAZ && ok) {
     139            7 :             myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
     140            7 :             myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
     141              :         }
     142        60955 :     } else if (attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
     143           28 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMLONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, myActiveRoute, rid, true, ok);
     144           28 :         if (myMapMatchTAZ && ok) {
     145            0 :             myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
     146            0 :             myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
     147              :         }
     148              :     } else {
     149       121906 :         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
     150        60927 :                                myActiveRoute, rid);
     151              :     }
     152        64652 :     if (!ok) {
     153           14 :         throw ProcessError();
     154              :     }
     155              : 
     156              :     // via-attributes
     157        64638 :     if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
     158        55114 :         myInsertStopEdgesAt = (int)myActiveRoute.size();
     159              :     }
     160              :     ConstMSEdgeVector viaEdges;
     161        64638 :     if (attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
     162           56 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIAXY, myVehicleParameter->id.c_str(), ok), false, vClass, viaEdges, rid, false, ok);
     163        64582 :     } else if (attrs.hasAttribute(SUMO_ATTR_VIALONLAT)) {
     164            7 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIALONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, viaEdges, rid, false, ok);
     165        64575 :     } else if (attrs.hasAttribute(SUMO_ATTR_VIAJUNCTIONS)) {
     166           42 :         for (std::string junctionID : attrs.get<std::vector<std::string> >(SUMO_ATTR_VIAJUNCTIONS, myVehicleParameter->id.c_str(), ok)) {
     167           28 :             const MSEdge* viaSink = MSEdge::dictionary(junctionID + "-sink");
     168           28 :             if (viaSink == nullptr) {
     169            0 :                 throw ProcessError("Junction-taz '" + junctionID + "' not found." + JUNCTION_TAZ_MISSING_HELP);
     170              :             } else {
     171           28 :                 viaEdges.push_back(viaSink);
     172              :             }
     173           14 :         }
     174              :     } else {
     175       129116 :         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok),
     176              :                                viaEdges, rid);
     177              :     }
     178        64632 :     if (!viaEdges.empty()) {
     179          612 :         myHaveVia = true;
     180              :     }
     181        67930 :     for (const MSEdge* e : viaEdges) {
     182         3298 :         myActiveRoute.push_back(e);
     183         3298 :         myVehicleParameter->via.push_back(e->getID());
     184              :     }
     185              : 
     186              :     // to-attributes
     187        84255 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_TO) && !attrs.hasAttribute(SUMO_ATTR_TOXY) && !attrs.hasAttribute(SUMO_ATTR_TOLONLAT))) &&
     188        36300 :             (attrs.hasAttribute(SUMO_ATTR_TO_TAZ) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION))) {
     189         4186 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION);
     190         7132 :         const std::string tazType = useJunction ? "junction" : "taz";
     191         7132 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO_TAZ, myVehicleParameter->id.c_str(), ok, true);
     192         4186 :         const MSEdge* toTaz = MSEdge::dictionary(tazID + "-sink");
     193         4186 :         if (toTaz == nullptr) {
     194            0 :             throw ProcessError("Sink " + tazType + " '" + tazID + "' not known " + rid + "!"
     195            0 :                                + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
     196         4186 :         } else if (toTaz->getNumPredecessors() == 0 && tag != SUMO_TAG_PERSON) {
     197            0 :             throw ProcessError("Sink " + tazType + " '" + tazID + "' has no incoming edges " + rid + "!");
     198              :         } else {
     199         4186 :             myActiveRoute.push_back(toTaz);
     200              :         }
     201        60446 :     } else if (attrs.hasAttribute(SUMO_ATTR_TOXY)) {
     202          168 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOXY, myVehicleParameter->id.c_str(), ok, true), false, vClass, myActiveRoute, rid, false, ok);
     203          168 :         if (myMapMatchTAZ && ok) {
     204            7 :             myVehicleParameter->toTaz = myActiveRoute.back()->getID();
     205            7 :             myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
     206              :         }
     207        60278 :     } else if (attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
     208           35 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOLONLAT, myVehicleParameter->id.c_str(), ok, true), true, vClass, myActiveRoute, rid, false, ok);
     209           35 :         if (myMapMatchTAZ && ok) {
     210            0 :             myVehicleParameter->toTaz = myActiveRoute.back()->getID();
     211            0 :             myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
     212              :         }
     213              :     } else {
     214       120486 :         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true),
     215        60243 :                                myActiveRoute, rid);
     216              :     }
     217        64632 :     myActiveRouteID = "!" + myVehicleParameter->id;
     218        64632 :     if (myVehicleParameter->routeid == "") {
     219              :         myVehicleParameter->routeid = myActiveRouteID;
     220              :     }
     221       129270 : }
     222              : 
     223              : 
     224              : void
     225     10753775 : MSRouteHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
     226              :     try {
     227        48911 :         if (myActiveTransportablePlan != nullptr && myActiveTransportablePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED
     228     10754641 :                 && element != SUMO_TAG_RIDE && element != SUMO_TAG_TRANSPORT && element != SUMO_TAG_PARAM) {
     229            0 :             const std::string mode = myActiveType == ObjectTypeEnum::PERSON ? "ride" : "transport";
     230            0 :             throw ProcessError("Triggered departure for " + myActiveTypeName + " '" + myVehicleParameter->id + "' requires starting with a " + mode + ".");
     231              :         }
     232     10753775 :         if (myVehicleParameter == nullptr) {
     233     10547162 :             myActiveTypeName = toString((SumoXMLTag)element);
     234     10547162 :             myHaveVia = false;
     235              :         }
     236     10753775 :         SUMORouteHandler::myStartElement(element, attrs);
     237     10753394 :         switch (element) {
     238         1649 :             case SUMO_TAG_PERSONFLOW:
     239         1649 :                 addTransportable(attrs, true);
     240              :                 break;
     241          289 :             case SUMO_TAG_CONTAINERFLOW:
     242          289 :                 addTransportable(attrs, false);
     243              :                 break;
     244        18805 :             case SUMO_TAG_FLOW:
     245        18805 :                 if (myVehicleParameter) {
     246        18805 :                     parseFromViaTo((SumoXMLTag)element, attrs);
     247              :                 }
     248              :                 break;
     249        37830 :             case SUMO_TAG_TRIP:
     250        37830 :                 parseFromViaTo((SumoXMLTag)element, attrs);
     251              :                 break;
     252              :             default:
     253              :                 break;
     254              :         }
     255          426 :     } catch (ProcessError&) {
     256          426 :         deleteActivePlanAndVehicleParameter();
     257          426 :         throw;
     258          426 :     }
     259     10753349 : }
     260              : 
     261              : 
     262              : void
     263          467 : MSRouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
     264          467 :     bool ok = true;
     265          467 :     myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     266          467 :     if (ok) {
     267          467 :         myCurrentVTypeDistribution = new RandomDistributor<MSVehicleType*>();
     268          467 :         if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
     269              :             std::vector<double> probs;
     270           94 :             if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
     271           79 :                 StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentVTypeDistributionID.c_str(), ok));
     272          249 :                 while (st.hasNext()) {
     273          340 :                     probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
     274              :                 }
     275           79 :             }
     276           94 :             const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
     277           94 :             StringTokenizer st(vTypes);
     278           94 :             int probIndex = 0;
     279          294 :             while (st.hasNext()) {
     280          200 :                 const std::string& vtypeID = st.next();
     281          200 :                 const RandomDistributor<MSVehicleType*>* const dist = MSNet::getInstance()->getVehicleControl().getVTypeDistribution(vtypeID);
     282          200 :                 if (dist != nullptr) {
     283           14 :                     const double distProb = ((int)probs.size() > probIndex ? probs[probIndex] : 1.) / dist->getOverallProb();
     284              :                     std::vector<double>::const_iterator probIt = dist->getProbs().begin();
     285           42 :                     for (MSVehicleType* const type : dist->getVals()) {
     286           28 :                         myCurrentVTypeDistribution->add(type, distProb * *probIt);
     287              :                         probIt++;
     288              :                     }
     289              :                 } else {
     290          186 :                     MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(vtypeID, &myParsingRNG);
     291          186 :                     if (type == nullptr) {
     292            0 :                         throw ProcessError("Unknown vtype '" + vtypeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
     293              :                     }
     294          186 :                     const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : type->getDefaultProbability());
     295          186 :                     myCurrentVTypeDistribution->add(type, prob);
     296              :                 }
     297          200 :                 probIndex++;
     298              :             }
     299           94 :             if (probs.size() > 0 && probIndex != (int)probs.size()) {
     300            0 :                 WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
     301              :                               " types in vTypeDistribution '" + myCurrentVTypeDistributionID + "'");
     302              :             }
     303          188 :         }
     304              :     }
     305          467 : }
     306              : 
     307              : 
     308              : void
     309          467 : MSRouteHandler::closeVehicleTypeDistribution() {
     310          467 :     if (myCurrentVTypeDistribution != nullptr) {
     311          467 :         if (MSGlobals::gStateLoaded && MSNet::getInstance()->getVehicleControl().hasVTypeDistribution(myCurrentVTypeDistributionID)) {
     312           52 :             delete myCurrentVTypeDistribution;
     313           26 :             return;
     314              :         }
     315          441 :         if (myCurrentVTypeDistribution->getOverallProb() == 0) {
     316            0 :             delete myCurrentVTypeDistribution;
     317            0 :             throw ProcessError(TLF("Vehicle type distribution '%' is empty.", myCurrentVTypeDistributionID));
     318              :         }
     319          441 :         if (!MSNet::getInstance()->getVehicleControl().addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
     320            0 :             delete myCurrentVTypeDistribution;
     321            0 :             throw ProcessError(TLF("Another vehicle type (or distribution) with the id '%' exists.", myCurrentVTypeDistributionID));
     322              :         }
     323          441 :         myCurrentVTypeDistribution = nullptr;
     324              :     }
     325              : }
     326              : 
     327              : 
     328              : void
     329       153269 : MSRouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
     330              :     myActiveRoute.clear();
     331       153269 :     myInsertStopEdgesAt = -1;
     332              :     // check whether the id is really necessary
     333              :     std::string rid;
     334       153269 :     if (myCurrentRouteDistribution != nullptr) {
     335         6760 :         myActiveRouteID = myCurrentRouteDistributionID + "#" + toString(myCurrentRouteDistribution->getProbs().size()); // !!! document this
     336        10140 :         rid =  "distribution '" + myCurrentRouteDistributionID + "'";
     337       149889 :     } else if (myVehicleParameter != nullptr) {
     338              :         // ok, a vehicle is wrapping the route,
     339              :         //  we may use this vehicle's id as default
     340       110242 :         myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
     341       110242 :         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     342           24 :             WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
     343              :         }
     344              :     } else {
     345        39647 :         bool ok = true;
     346        39647 :         myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok, false);
     347        39647 :         if (!ok) {
     348            0 :             return;
     349              :         }
     350       118941 :         rid = "'" + myActiveRouteID + "'";
     351              :     }
     352       153269 :     if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
     353       339588 :         rid =  "for vehicle '" + myVehicleParameter->id + "'";
     354              :     }
     355       153269 :     bool ok = true;
     356       153269 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     357       306330 :         MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
     358              :     }
     359       306430 :     myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
     360       153271 :     if (myActiveRouteRefID != "" && MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG) == nullptr) {
     361           42 :         throw ProcessError(TLF("Invalid reference to route '%' in route %.", myActiveRouteRefID, rid));
     362              :     }
     363       153201 :     myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
     364       153201 :     myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
     365       153201 :     myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
     366       153201 :     myActiveRouteReplacedAtTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_REPLACED_AT_TIME, myActiveRouteID.c_str(), ok, -1);
     367       153201 :     myActiveRouteReplacedIndex = attrs.getOpt<int>(SUMO_ATTR_REPLACED_ON_INDEX, myActiveRouteID.c_str(), ok, 0);
     368       153201 :     myActiveRoutePeriod = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CYCLETIME, myActiveRouteID.c_str(), ok,
     369              :                           // handle obsolete attribute name
     370              :                           attrs.getOptSUMOTimeReporting(SUMO_ATTR_PERIOD, myActiveRouteID.c_str(), ok, 0));
     371       153201 :     myActiveRoutePermanent = attrs.getOpt<bool>(SUMO_ATTR_STATE, myActiveRouteID.c_str(), ok, false);
     372       153201 :     if (attrs.hasAttribute(SUMO_ATTR_PERIOD)) {
     373            0 :         WRITE_WARNING(TL("Attribute 'period' is deprecated for route. Use 'cycleTime' instead."));
     374              :     }
     375       153201 :     myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
     376       153201 :     if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
     377           68 :         WRITE_WARNING(TLF("Invalid cost for route '%'.", myActiveRouteID));
     378              :     }
     379              : }
     380              : 
     381              : 
     382              : void
     383        10327 : MSRouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
     384              :     // Currently unused
     385        10327 : }
     386              : 
     387              : 
     388              : void
     389         8478 : MSRouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
     390              :     // Currently unused
     391         8478 : }
     392              : 
     393              : 
     394              : void
     395        37830 : MSRouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
     396              :     // Currently unsued
     397        37830 : }
     398              : 
     399              : 
     400              : void
     401       197438 : MSRouteHandler::closeRoute(const bool mayBeDisconnected) {
     402       197438 :     std::string type = "vehicle";
     403       197438 :     if (mayBeDisconnected) {
     404        44237 :         if (myVehicleParameter->repetitionNumber >= 0) {
     405              :             type = "flow";
     406              :         } else {
     407              :             type = "trip";
     408              :         }
     409              :     }
     410              : 
     411              :     try {
     412       197438 :         const bool mustReroute = myActiveRoute.size() == 0 && myActiveRouteStops.size() != 0;
     413              :         if (mustReroute) {
     414              :             // implicit route from stops
     415          105 :             for (const SUMOVehicleParameter::Stop& stop : myActiveRouteStops) {
     416           84 :                 myActiveRoute.push_back(MSEdge::dictionary(stop.edge));
     417              :             }
     418              :         }
     419       197438 :         if (myActiveRoute.size() == 0) {
     420          590 :             delete myActiveRouteColor;
     421          590 :             myActiveRouteColor = nullptr;
     422          590 :             if (myActiveRouteRefID != "" && myCurrentRouteDistribution != nullptr) {
     423           42 :                 ConstMSRoutePtr route = MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG);
     424           42 :                 if (route != nullptr) {
     425           84 :                     myCurrentRouteDistribution->add(route, myActiveRouteProbability);
     426              :                 }
     427           42 :                 myActiveRouteID = "";
     428              :                 myActiveRouteRefID = "";
     429              :                 return;
     430              :             }
     431          548 :             if (myVehicleParameter != nullptr) {
     432         1084 :                 throw ProcessError("The route for " + type + " '" + myVehicleParameter->id + "' has no edges.");
     433              :             } else {
     434           18 :                 throw ProcessError(TLF("Route '%' has no edges.", myActiveRouteID));
     435              :             }
     436              :         }
     437       196848 :         if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
     438          144 :             throw ProcessError("The routing information for " + type + " '" + myVehicleParameter->id + "' is insufficient.");
     439              :         }
     440       196776 :         if (myActiveRouteRepeat > 0) {
     441              :             // duplicate route
     442          818 :             ConstMSEdgeVector tmpEdges = myActiveRoute;
     443          818 :             auto tmpStops = myActiveRouteStops;
     444              : 
     445          818 :             if (MSGlobals::gCheckRoutes) {
     446              :                 SUMOVehicleClass vClass = SVC_IGNORING;
     447          818 :                 std::string errSuffix = ".";
     448          818 :                 if (myVehicleParameter != nullptr) {
     449          658 :                     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
     450          658 :                     MSVehicleType* vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     451          658 :                     if (vtype != nullptr) {
     452              :                         vClass = vtype->getVehicleClass();
     453         2640 :                         errSuffix = TLF(" for vehicle '%' with vClass %.", myVehicleParameter->id, getVehicleClassNames(vClass));
     454              :                     }
     455              :                 }
     456          818 :                 if (myActiveRoute.size() > 0 && !myActiveRoute.back()->isConnectedTo(*myActiveRoute.front(), vClass)) {
     457           16 :                     if (tmpStops.size() == 0 || tmpStops.back().jump < 0) {
     458           32 :                         throw ProcessError(TLF("Disconnected route '%' when repeating. Last edge '%' is not connected to first edge '%'%",
     459           16 :                                                myActiveRouteID, myActiveRoute.back()->getID(), myActiveRoute.front()->getID(), errSuffix));
     460              :                     }
     461              :                 }
     462              :             }
     463         8709 :             for (int i = 0; i < myActiveRouteRepeat; i++) {
     464         7907 :                 myActiveRoute.insert(myActiveRoute.begin(), tmpEdges.begin(), tmpEdges.end());
     465         8947 :                 for (SUMOVehicleParameter::Stop stop : tmpStops) {
     466         1048 :                     if (stop.until > 0) {
     467          544 :                         if (myActiveRoutePeriod <= 0) {
     468            8 :                             const std::string description = myVehicleParameter != nullptr
     469           16 :                                                             ?  "for " + type + " '" + myVehicleParameter->id + "'"
     470           24 :                                                             :  "'" + myActiveRouteID + "'";
     471           24 :                             throw ProcessError(TLF("Cannot repeat stops with 'until' in route % because no cycleTime is defined.", description));
     472              :                         }
     473          536 :                         stop.until += myActiveRoutePeriod * (i + 1);
     474              :                     }
     475         1040 :                     if (stop.arrival > 0) {
     476           25 :                         if (myActiveRoutePeriod <= 0) {
     477            0 :                             const std::string description = myVehicleParameter != nullptr
     478            0 :                                                             ?  "for " + type + " '" + myVehicleParameter->id + "'"
     479            0 :                                                             :  "'" + myActiveRouteID + "'";
     480            0 :                             throw ProcessError(TLF("Cannot repeat stops with 'arrival' in route % because no cycleTime is defined.", description));
     481              :                         }
     482           25 :                         stop.arrival += myActiveRoutePeriod * (i + 1);
     483              :                     }
     484         1040 :                     stop.index = STOP_INDEX_REPEAT;
     485         1040 :                     myActiveRouteStops.push_back(stop);
     486         1048 :                 }
     487              :             }
     488          802 :             if (myActiveRouteStops.size() > 0) {
     489              :                 // never jump on the last stop of a repeating route
     490           92 :                 myActiveRouteStops.back().jump = -1;
     491              :             }
     492          834 :         }
     493       196760 :         MSRoute* const route = new MSRoute(myActiveRouteID, myActiveRoute,
     494       393520 :                                            myAmLoadingState ? myActiveRoutePermanent : myVehicleParameter == nullptr,
     495       196760 :                                            myActiveRouteColor, myActiveRouteStops,
     496       196760 :                                            myActiveRouteReplacedAtTime, myActiveRouteReplacedIndex);
     497       196760 :         route->setPeriod(myActiveRoutePeriod);
     498       196760 :         route->setCosts(myCurrentCosts);
     499              :         route->setReroute(mustReroute);
     500              :         myActiveRoute.clear();
     501              :         ConstMSRoutePtr constRoute = std::shared_ptr<const MSRoute>(route);
     502       393520 :         if (!MSRoute::dictionary(myActiveRouteID, constRoute)) {
     503          142 :             if (!MSGlobals::gStateLoaded) {
     504           26 :                 if (myVehicleParameter != nullptr) {
     505           20 :                     if (MSNet::getInstance()->getVehicleControl().getVehicle(myVehicleParameter->id) == nullptr) {
     506           12 :                         throw ProcessError("Another route for " + type + " '" + myVehicleParameter->id + "' exists.");
     507              :                     } else {
     508           42 :                         throw ProcessError(TLF("A vehicle with id '%' already exists.", myVehicleParameter->id));
     509              :                     }
     510              :                 } else {
     511           18 :                     throw ProcessError(TLF("Another route (or distribution) with the id '%' exists.", myActiveRouteID));
     512              :                 }
     513              :             }
     514              :         } else {
     515       196618 :             if (myCurrentRouteDistribution != nullptr) {
     516         6666 :                 myCurrentRouteDistribution->add(constRoute, myActiveRouteProbability);
     517              :             }
     518              :         }
     519              :         myActiveRouteID = "";
     520       196734 :         myActiveRouteColor = nullptr;
     521              :         myActiveRouteStops.clear();
     522       196734 :         myActiveRouteRepeat = 0;
     523          662 :     } catch (ProcessError&) {
     524          662 :         deleteActivePlanAndVehicleParameter();
     525          662 :         throw;
     526          662 :     }
     527              : }
     528              : 
     529              : 
     530              : void
     531         1273 : MSRouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
     532              :     // check whether the id is really necessary
     533         1273 :     if (myVehicleParameter != nullptr) {
     534              :         // ok, a vehicle is wrapping the route,
     535              :         //  we may use this vehicle's id as default
     536         1094 :         myCurrentRouteDistributionID = "!" + myVehicleParameter->id; // !!! document this
     537              :         // we have to record this or we cannot remove the distribution later
     538         1094 :         myVehicleParameter->routeid = myCurrentRouteDistributionID;
     539              :     } else {
     540          179 :         bool ok = true;
     541          179 :         myCurrentRouteDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     542          179 :         if (!ok) {
     543            0 :             return;
     544              :         }
     545              :     }
     546         1273 :     myCurrentRouteDistribution = new RandomDistributor<ConstMSRoutePtr>();
     547              :     std::vector<double> probs;
     548         1273 :     if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
     549           15 :         bool ok = true;
     550           15 :         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentRouteDistributionID.c_str(), ok));
     551           45 :         while (st.hasNext()) {
     552           60 :             probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
     553              :         }
     554           15 :     }
     555         1273 :     if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
     556           22 :         bool ok = true;
     557           22 :         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, myCurrentRouteDistributionID.c_str(), ok));
     558           22 :         int probIndex = 0;
     559           66 :         while (st.hasNext()) {
     560           44 :             std::string routeID = st.next();
     561           44 :             ConstMSRoutePtr route = MSRoute::dictionary(routeID, &myParsingRNG);
     562           44 :             if (route == nullptr) {
     563            0 :                 throw ProcessError("Unknown route '" + routeID + "' in distribution '" + myCurrentRouteDistributionID + "'.");
     564              :             }
     565           44 :             const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : 1.0);
     566           44 :             myCurrentRouteDistribution->add(route, prob, false);
     567           44 :             probIndex++;
     568              :         }
     569           22 :         if (probs.size() > 0 && probIndex != (int)probs.size()) {
     570            0 :             WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
     571              :                           " routes in routeDistribution '" + myCurrentRouteDistributionID + "'");
     572              :         }
     573           22 :     }
     574         1273 : }
     575              : 
     576              : 
     577              : void
     578         1259 : MSRouteHandler::closeRouteDistribution() {
     579         1259 :     if (myCurrentRouteDistribution != nullptr) {
     580         1259 :         const bool haveSameID = MSRoute::dictionary(myCurrentRouteDistributionID, &myParsingRNG) != nullptr;
     581         1259 :         if (MSGlobals::gStateLoaded && haveSameID) {
     582            4 :             delete myCurrentRouteDistribution;
     583            2 :             myCurrentRouteDistribution = nullptr;
     584            2 :             return;
     585              :         }
     586         1247 :         if (haveSameID) {
     587            0 :             delete myCurrentRouteDistribution;
     588            0 :             throw ProcessError(TLF("Another route (or distribution) with the id '%' exists.", myCurrentRouteDistributionID));
     589              :         }
     590         1257 :         if (myCurrentRouteDistribution->getOverallProb() == 0) {
     591            0 :             delete myCurrentRouteDistribution;
     592            0 :             throw ProcessError(TLF("Route distribution '%' is empty.", myCurrentRouteDistributionID));
     593              :         }
     594         1257 :         MSRoute::dictionary(myCurrentRouteDistributionID, myCurrentRouteDistribution, myVehicleParameter == nullptr);
     595         1257 :         myCurrentRouteDistribution = nullptr;
     596              :     }
     597              : }
     598              : 
     599              : 
     600              : void
     601       296448 : MSRouteHandler::closeVehicle() {
     602              :     // get nested route
     603       296448 :     const std::string embeddedRouteID = "!" + myVehicleParameter->id;
     604              :     ConstMSRoutePtr route = nullptr;
     605       296448 :     if (myReplayRerouting) {
     606         2832 :         RandomDistributor<ConstMSRoutePtr>* rDist = MSRoute::distDictionary(embeddedRouteID);
     607         2832 :         if (rDist != nullptr && rDist->getVals().size() > 0) {
     608              :             route = rDist->getVals().front();
     609              :         }
     610              :     }
     611       296448 :     if (route == nullptr) {
     612       590860 :         route = MSRoute::dictionary(embeddedRouteID, &myParsingRNG);
     613              :     }
     614       296448 :     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
     615       296448 :     if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN) {
     616              :         // let's check whether this vehicle had to depart before the simulation starts
     617      1182638 :         if (!(myAddVehiclesDirectly || checkLastDepart()) || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
     618              :             return;
     619              :         }
     620              :     }
     621              : 
     622              :     // get the vehicle's type
     623              :     MSVehicleType* vtype = nullptr;
     624              : 
     625              :     try {
     626       295622 :         if (myVehicleParameter->vtypeid != "") {
     627       295622 :             vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     628       295622 :             if (vtype == nullptr) {
     629            0 :                 throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
     630              :             }
     631       295622 :             if (vtype->getVehicleClass() == SVC_PEDESTRIAN) {
     632           90 :                 WRITE_WARNINGF(TL("Vehicle type '%' with vClass=pedestrian should only be used for persons and not for vehicle '%'."), vtype->getID(), myVehicleParameter->id);
     633              :             }
     634              :         } else {
     635              :             // there should be one (at least the default one)
     636            0 :             vtype = vehControl.getVType(DEFAULT_VTYPE_ID, &myParsingRNG);
     637              :         }
     638       295622 :         if (myVehicleParameter->wasSet(VEHPARS_ROUTE_SET)) {
     639              :             // if the route id was given, prefer that one
     640       152713 :             if (route != nullptr && !myAmLoadingState) {
     641         1554 :                 WRITE_WARNINGF(TL("Ignoring child element 'route' for vehicle '%' because attribute 'route' is set."), myVehicleParameter->id);
     642              :             }
     643       305426 :             route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
     644              :         }
     645       295622 :         if (route == nullptr) {
     646              :             // nothing found? -> error
     647          340 :             if (myVehicleParameter->routeid != "") {
     648           28 :                 throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
     649              :             } else {
     650          978 :                 throw ProcessError(TLF("Vehicle '%' has no route.", myVehicleParameter->id));
     651              :             }
     652              :         }
     653       295282 :         myActiveRouteID = "";
     654              : 
     655          340 :     } catch (ProcessError&) {
     656          340 :         deleteActivePlanAndVehicleParameter();
     657          340 :         throw;
     658          340 :     }
     659       295282 :     if (route->mustReroute()) {
     660           14 :         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
     661           14 :         if (myVehicleParameter->stops.size() > 0) {
     662           14 :             route = addVehicleStopsToImplicitRoute(route, false);
     663              :         }
     664              :     }
     665       295282 :     if (myVehicleParameter->departEdgeProcedure != RouteIndexDefinition::DEFAULT) {
     666           89 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
     667          175 :                 myVehicleParameter->departEdgeProcedure == RouteIndexDefinition::GIVEN &&
     668           65 :                 myVehicleParameter->departEdge >= (int)route->getEdges().size()) {
     669           14 :             throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has invalid departEdge index "
     670           28 :                                + toString(myVehicleParameter->departEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
     671              :         }
     672              :     }
     673       295275 :     if (myVehicleParameter->arrivalEdgeProcedure != RouteIndexDefinition::DEFAULT) {
     674           44 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
     675           95 :                 myVehicleParameter->arrivalEdgeProcedure == RouteIndexDefinition::GIVEN &&
     676           37 :                 myVehicleParameter->arrivalEdge >= (int)route->getEdges().size()) {
     677           14 :             throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has invalid arrivalEdge index "
     678           28 :                                + toString(myVehicleParameter->arrivalEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
     679              :         }
     680              :     }
     681              : 
     682              :     // try to build the vehicle
     683              :     SUMOVehicle* vehicle = nullptr;
     684       295268 :     if (vehControl.getVehicle(myVehicleParameter->id) == nullptr) {
     685       295190 :         MSVehicleControl::VehicleDefinitionSource source = (myAmLoadingState) ? MSVehicleControl::VehicleDefinitionSource::STATE : MSVehicleControl::VehicleDefinitionSource::ROUTEFILE;
     686              :         try {
     687       295190 :             vehicle = vehControl.buildVehicle(myVehicleParameter, route, vtype, !MSGlobals::gCheckRoutes, source, !myAmLoadingState);
     688           64 :         } catch (const ProcessError& e) {
     689           64 :             myVehicleParameter = nullptr;
     690           64 :             if (!MSGlobals::gCheckRoutes) {
     691           30 :                 WRITE_WARNING(e.what());
     692              :                 vehControl.fixVehicleCounts();
     693              :                 return;
     694              :             } else {
     695           98 :                 throw e;
     696              :             }
     697           64 :         }
     698       295126 :         const SUMOTime origDepart = myVehicleParameter->depart;
     699              :         // maybe we do not want this vehicle to be inserted due to scaling
     700       295126 :         int quota = myAmLoadingState ? 1 : vehControl.getQuota(vehControl.getScale() * vtype->getParameter().scale);
     701       290715 :         if (quota > 0) {
     702       294188 :             registerLastDepart();
     703       294188 :             myVehicleParameter->depart += MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
     704       294188 :             vehControl.addVehicle(myVehicleParameter->id, vehicle);
     705       294188 :             if (myReplayRerouting) {
     706         2832 :                 RandomDistributor<ConstMSRoutePtr>* rDist = MSRoute::distDictionary(embeddedRouteID);
     707         2832 :                 if (rDist != nullptr) {
     708         2809 :                     for (int i = 0; i < (int)rDist->getVals().size() - 1; i++) {
     709         1791 :                         SUMOTime replacedAt = rDist->getVals()[i]->getReplacedTime();
     710         1791 :                         auto* cmd = new Command_RouteReplacement(vehicle->getID(), rDist->getVals()[i + 1]);
     711         1791 :                         if (i == 0 && replacedAt >= 0 && replacedAt == myVehicleParameter->depart) {
     712              :                             // routing in the insertion step happens *after* insertion
     713           12 :                             MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(cmd, replacedAt);
     714              :                         } else {
     715         1779 :                             MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(cmd, replacedAt);
     716              :                         }
     717              :                     }
     718              :                 }
     719              :             }
     720              :             int offset = 0;
     721       294360 :             for (int i = 1; i < quota; i++) {
     722          172 :                 if (vehicle->getParameter().departProcedure == DepartDefinition::GIVEN || vehicle->getParameter().departProcedure == DepartDefinition::BEGIN) {
     723          156 :                     MSNet::getInstance()->getInsertionControl().add(vehicle);
     724              :                 }
     725          172 :                 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*myVehicleParameter);
     726          344 :                 newPars->id = myVehicleParameter->id + myScaleSuffix + toString(i + offset);
     727          178 :                 while (vehControl.getVehicle(newPars->id) != nullptr) {
     728            6 :                     offset += 1;
     729           12 :                     newPars->id = myVehicleParameter->id + myScaleSuffix + toString(i + offset);
     730              :                 }
     731          172 :                 newPars->depart = origDepart + MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
     732          172 :                 if (vehControl.hasVTypeDistribution(myVehicleParameter->vtypeid)) {
     733              :                     // resample type
     734           27 :                     vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     735              :                 }
     736          587 :                 vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes, source, !myAmLoadingState);
     737          172 :                 vehControl.addVehicle(newPars->id, vehicle);
     738              :             }
     739       294188 :             myVehicleParameter = nullptr;
     740              :         } else {
     741          938 :             vehControl.deleteVehicle(vehicle, true);
     742          938 :             myVehicleParameter = nullptr;
     743              :             vehicle = nullptr;
     744              :         }
     745              :     } else {
     746              :         // strange: another vehicle with the same id already exists
     747           78 :         if (!MSGlobals::gStateLoaded) {
     748              :             // and was not loaded while loading a simulation state
     749              :             // -> error
     750           12 :             std::string veh_id = myVehicleParameter->id;
     751           12 :             deleteActivePlanAndVehicleParameter();
     752           24 :             std::string scaleWarning = "";
     753           18 :             if (vehControl.getScale() * vtype->getParameter().scale > 1 && veh_id.find(myScaleSuffix) != std::string::npos) {
     754              :                 scaleWarning = "\n   (Possibly duplicate id due to using option --scale. Set option --scale-suffix to prevent this)";
     755              :             }
     756           24 :             throw ProcessError("Another vehicle with the id '" + veh_id + "' exists." + scaleWarning);
     757              :         } else {
     758              :             // ok, it seems to be loaded previously while loading a simulation state
     759              :             vehicle = nullptr;
     760              :         }
     761              :     }
     762              :     // check whether the vehicle shall be added directly to the network or
     763              :     //  shall stay in the internal buffer
     764       294188 :     if (vehicle != nullptr) {
     765       294188 :         if (vehicle->getParameter().departProcedure == DepartDefinition::GIVEN || vehicle->getParameter().departProcedure == DepartDefinition::BEGIN) {
     766       293613 :             MSNet::getInstance()->getInsertionControl().add(vehicle);
     767              :         }
     768              :     }
     769              : }
     770              : 
     771              : 
     772              : ConstMSRoutePtr
     773           14 : MSRouteHandler::addVehicleStopsToImplicitRoute(ConstMSRoutePtr route, bool isPermanent) {
     774              :     // the route was defined without edges and its current edges were
     775              :     // derived from route-stops.
     776              :     // We may need to add additional edges for the vehicle-stops
     777              :     assert(myVehicleParameter->wasSet(VEHPARS_ROUTE_SET));
     778              :     assert(route->getStops().size() > 0);
     779           14 :     ConstMSEdgeVector edges = route->getEdges();
     780           42 :     for (SUMOVehicleParameter::Stop stop : myVehicleParameter->stops) {
     781           28 :         MSEdge* stopEdge = MSEdge::dictionary(stop.edge);
     782           28 :         if (stop.index == 0) {
     783           14 :             if (edges.front() != stopEdge ||
     784            0 :                     route->getStops().front().endPos < stop.endPos) {
     785           14 :                 edges.insert(edges.begin(), stopEdge);
     786              :             }
     787           14 :         } else if (stop.index == STOP_INDEX_END) {
     788           14 :             if (edges.back() != stopEdge ||
     789            0 :                     route->getStops().back().endPos > stop.endPos) {
     790           14 :                 edges.push_back(stopEdge);
     791              :             }
     792              :         } else {
     793            0 :             WRITE_WARNINGF(TL("Could not merge vehicle stops for vehicle '%' into implicitly defined route '%'"), myVehicleParameter->id, route->getID());
     794              :         }
     795           28 :     }
     796           14 :     ConstMSRoutePtr newRoute = std::make_shared<MSRoute>("!" + myVehicleParameter->id, edges,
     797           28 :                                isPermanent, new RGBColor(route->getColor()), route->getStops());
     798           28 :     if (!MSRoute::dictionary(newRoute->getID(), newRoute)) {
     799            0 :         throw ProcessError("Could not adapt implicit route for " + std::string(isPermanent ? "flow" : "vehicle") + "  '" + myVehicleParameter->id + "'");
     800              :     }
     801           14 :     return newRoute;
     802           14 : }
     803              : 
     804              : 
     805              : void
     806        40719 : MSRouteHandler::closeTransportable() {
     807              :     try {
     808        40719 :         if (myActiveTransportablePlan->size() == 0) {
     809           12 :             std::string error = myActiveTypeName + " '" + myVehicleParameter->id + "' has no plan.";
     810            6 :             error[0] = (char)::toupper((char)error[0]);
     811           12 :             throw ProcessError(error);
     812              :         }
     813              :         // let's check whether this transportable had to depart before the simulation starts
     814        16815 :         if (!(myAddVehiclesDirectly || checkLastDepart())
     815       179657 :                 || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
     816           10 :             deleteActivePlanAndVehicleParameter();
     817              :             return;
     818              :         }
     819              :         // type existence has been checked on opening
     820        40703 :         MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     821        40703 :         if (myActiveType == ObjectTypeEnum::PERSON
     822        39948 :                 && type->getVehicleClass() != SVC_PEDESTRIAN
     823        40723 :                 && !type->getParameter().wasSet(VTYPEPARS_VEHICLECLASS_SET)) {
     824           48 :             WRITE_WARNINGF(TL("Person '%' receives type '%' which implicitly uses unsuitable vClass '%'."), myVehicleParameter->id, type->getID(), toString(type->getVehicleClass()));
     825              :         }
     826        40703 :         int created = addFlowTransportable(myVehicleParameter->depart, type, myVehicleParameter->id, -1);
     827        40675 :         registerLastDepart();
     828        40675 :         if (created > 0) {
     829        40651 :             resetActivePlanAndVehicleParameter();
     830              :         } else {
     831           24 :             deleteActivePlanAndVehicleParameter();
     832              :         }
     833           34 :     } catch (ProcessError&) {
     834           34 :         deleteActivePlanAndVehicleParameter();
     835           34 :         throw;
     836           34 :     }
     837              : }
     838              : 
     839              : 
     840              : void
     841        39964 : MSRouteHandler::closePerson() {
     842        39964 :     closeTransportable();
     843        39938 : }
     844              : 
     845              : 
     846              : void
     847          755 : MSRouteHandler::closeContainer() {
     848          755 :     closeTransportable();
     849          747 : }
     850              : 
     851              : 
     852              : void
     853         1647 : MSRouteHandler::closePersonFlow() {
     854         1647 :     closeTransportableFlow();
     855         1638 : }
     856              : 
     857              : 
     858              : void
     859          287 : MSRouteHandler::closeContainerFlow() {
     860          287 :     closeTransportableFlow();
     861          287 : }
     862              : 
     863              : 
     864              : void
     865         1934 : MSRouteHandler::closeTransportableFlow() {
     866              :     try {
     867         1934 :         const std::string fid = myVehicleParameter->id;
     868         1934 :         if (myActiveTransportablePlan->size() == 0) {
     869            0 :             throw ProcessError(myActiveTypeName + "Flow '" + fid + "' has no plan.");
     870              :         }
     871              :         // let's check whether this transportable (person/container) had to depart before the simulation starts
     872         1934 :         if (!(myAddVehiclesDirectly || checkLastDepart())
     873         9679 :                 || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
     874            0 :             deleteActivePlanAndVehicleParameter();
     875              :             return;
     876              :         }
     877              :         // instantiate all persons/containers of this flow
     878              :         int i = 0;
     879         1934 :         registerLastDepart();
     880         1934 :         std::string baseID = myVehicleParameter->id;
     881         1934 :         if (myVehicleParameter->repetitionProbability > 0) {
     882          206 :             if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
     883            0 :                 throw ProcessError("probabilistic " + myActiveTypeName + "Flow '" + fid + "' must specify end time");
     884              :             } else {
     885       440709 :                 for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
     886       440503 :                     if (RandHelper::rand(&myParsingRNG) < myVehicleParameter->repetitionProbability) {
     887              :                         // type existence has been checked on opening
     888        32993 :                         MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     889        32993 :                         addFlowTransportable(t, type, baseID, i++);
     890              :                     }
     891              :                 }
     892              :             }
     893              :         } else {
     894         1728 :             SUMOTime depart = myVehicleParameter->depart;
     895         1728 :             if (myVehicleParameter->repetitionOffset < 0) {
     896              :                 // poisson: randomize first depart
     897           64 :                 myVehicleParameter->incrementFlow(1, &myParsingRNG);
     898              :             }
     899       429587 :             for (; i < myVehicleParameter->repetitionNumber && (myVehicleParameter->repetitionNumber != std::numeric_limits<int>::max()
     900        78581 :                     || depart + myVehicleParameter->repetitionTotalOffset <= myVehicleParameter->repetitionEnd); i++) {
     901              :                 // type existence has been checked on opening
     902       427868 :                 MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     903       427868 :                 addFlowTransportable(depart + myVehicleParameter->repetitionTotalOffset, type, baseID, i);
     904       427859 :                 myVehicleParameter->incrementFlow(1, &myParsingRNG);
     905              :             }
     906              :         }
     907         1925 :         resetActivePlanAndVehicleParameter();
     908            9 :     } catch (ProcessError&) {
     909            9 :         deleteActivePlanAndVehicleParameter();
     910            9 :         throw;
     911            9 :     }
     912         1925 :     myStartTriggeredInFlow = false;
     913              : }
     914              : 
     915              : 
     916              : int
     917       501564 : MSRouteHandler::addFlowTransportable(SUMOTime depart, MSVehicleType* type, const std::string& baseID, int i) {
     918              :     try {
     919              :         int numCreated = 0;
     920       501564 :         MSNet* const net = MSNet::getInstance();
     921       501564 :         MSTransportableControl& tc = myActiveType == ObjectTypeEnum::PERSON ? net->getPersonControl() : net->getContainerControl();
     922       501543 :         const MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     923              :         //MSTransportableControl& pc = net->getPersonControl();
     924       501543 :         const int quota = vc.getQuota(vc.getScale() * type->getParameter().scale, tc.getLoadedNumber());
     925       501543 :         if (quota == 0) {
     926              :             tc.addDiscarded();
     927              :         }
     928      1003254 :         for (int j = 0; j < quota; j++) {
     929       501727 :             if (i > 0 || j > 0) {
     930              :                 // copy parameter and plan because the transportable takes over responsibility
     931       459135 :                 SUMOVehicleParameter* copyParam = new SUMOVehicleParameter();
     932       459135 :                 *copyParam = *myVehicleParameter;
     933       459135 :                 myVehicleParameter = copyParam;
     934       459135 :                 MSTransportable::MSTransportablePlan* copyPlan = new MSTransportable::MSTransportablePlan();
     935              :                 MSStage* lastStage = nullptr;
     936      1424763 :                 for (MSStage* const s : *myActiveTransportablePlan) {
     937       965628 :                     copyPlan->push_back(s->clone());
     938       965628 :                     if (lastStage != nullptr && s->getStageType() == MSStageType::WALKING && lastStage->getDestinationStop() != nullptr) {
     939            0 :                         MSStageMoving* walk = static_cast<MSStageMoving*>(copyPlan->back());
     940            0 :                         walk->setDepartPos(lastStage->getDestinationStop()->getAccessPos(walk->getEdge(), &myParsingRNG));
     941              :                     }
     942              :                     lastStage = s;
     943              :                 }
     944       459135 :                 myActiveTransportablePlan = copyPlan;
     945       459135 :                 if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
     946          198 :                     const double initialDepartPos = RandHelper::rand(myActiveTransportablePlan->front()->getDestination()->getLength(), &myParsingRNG);
     947          198 :                     myActiveTransportablePlan->front()->setArrivalPos(initialDepartPos);
     948              :                 }
     949              :             }
     950       501727 :             myVehicleParameter->id = (baseID
     951      1925350 :                                       + (i >= 0 ? "." + toString(i) : "")
     952      1505421 :                                       + (j > 0 ? "." + toString(j) : ""));
     953       501727 :             myVehicleParameter->depart = depart += net->getInsertionControl().computeRandomDepartOffset();
     954       501727 :             MSTransportable* transportable = myActiveType == ObjectTypeEnum::PERSON ?
     955       305838 :                                              tc.buildPerson(myVehicleParameter, type, myActiveTransportablePlan, &myParsingRNG) :
     956       195889 :                                              tc.buildContainer(myVehicleParameter, type, myActiveTransportablePlan);
     957       501727 :             numCreated++;
     958       501727 :             if (!tc.add(transportable)) {
     959           16 :                 std::string error = "Another " + myActiveTypeName + " with the id '" + myVehicleParameter->id + "' exists.";
     960           16 :                 delete transportable;
     961           16 :                 resetActivePlanAndVehicleParameter();
     962           16 :                 if (!MSGlobals::gStateLoaded) {
     963           32 :                     throw ProcessError(error);
     964              :                 }
     965       311254 :             } else if ((net->hasPersons() && net->getPersonControl().get(myVehicleParameter->id) != nullptr)
     966       807549 :                        && (net->hasContainers() && net->getContainerControl().get(myVehicleParameter->id) != nullptr)) {
     967           48 :                 WRITE_WARNINGF(TL("There exists a person and a container with the same id '%'. Starting with SUMO 1.9.0 this is an error."), myVehicleParameter->id);
     968              :             }
     969              :         }
     970       501527 :         return numCreated;
     971           37 :     } catch (ProcessError&) {
     972           37 :         deleteActivePlanAndVehicleParameter();
     973           37 :         throw;
     974           37 :     }
     975              : }
     976              : 
     977              : 
     978              : void
     979        58543 : MSRouteHandler::closeVType() {
     980        58543 :     MSVehicleType* vehType = MSVehicleType::build(*myCurrentVType);
     981        58539 :     vehType->check();
     982        58539 :     if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
     983              :         const std::string id = vehType->getID();
     984          177 :         delete vehType;
     985          177 :         if (!MSGlobals::gStateLoaded) {
     986           60 :             throw ProcessError(TLF("Another vehicle type (or distribution) with the id '%' exists.", id));
     987              :         }
     988              :     } else {
     989        58362 :         if (myCurrentVTypeDistribution != nullptr) {
     990         1012 :             myCurrentVTypeDistribution->add(vehType, vehType->getDefaultProbability());
     991              :         }
     992              :     }
     993        58519 : }
     994              : 
     995              : 
     996              : void
     997        18803 : MSRouteHandler::closeFlow() {
     998        18803 :     myInsertStopEdgesAt = -1;
     999        18803 :     if (myVehicleParameter->repetitionNumber == 0) {
    1000            0 :         delete myVehicleParameter;
    1001            0 :         myVehicleParameter = nullptr;
    1002           13 :         return;
    1003              :     }
    1004              :     // let's check whether vehicles had to depart before the simulation starts
    1005        18803 :     myVehicleParameter->repetitionsDone = 0;
    1006        18803 :     if (myVehicleParameter->repetitionProbability < 0) {
    1007        15040 :         const SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
    1008        15717 :         while (myVehicleParameter->repetitionTotalOffset < offsetToBegin) {
    1009          690 :             myVehicleParameter->incrementFlow(1, &myParsingRNG);
    1010          690 :             if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
    1011           13 :                 delete myVehicleParameter;
    1012           13 :                 myVehicleParameter = nullptr;
    1013           13 :                 return;
    1014              :             }
    1015              :         }
    1016              :     }
    1017        18790 :     if (MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG) == nullptr) {
    1018            0 :         throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
    1019              :     }
    1020        29106 :     if (myVehicleParameter->routeid[0] == '!' && MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
    1021         6712 :         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1022         6712 :         closeRoute(true);
    1023              :     }
    1024        18774 :     ConstMSRoutePtr route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
    1025        18774 :     if (route == nullptr) {
    1026            0 :         throw ProcessError("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
    1027              :     }
    1028        18774 :     if (route->mustReroute()) {
    1029           14 :         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1030           14 :         if (myVehicleParameter->stops.size() > 0) {
    1031           21 :             route = addVehicleStopsToImplicitRoute(route, true);
    1032            7 :             myVehicleParameter->routeid = route->getID();
    1033              :         }
    1034              :     }
    1035        18774 :     if (myVehicleParameter->departEdgeProcedure != RouteIndexDefinition::DEFAULT) {
    1036           20 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
    1037           43 :                 myVehicleParameter->departEdgeProcedure == RouteIndexDefinition::GIVEN &&
    1038            9 :                 myVehicleParameter->departEdge >= (int)route->getEdges().size()) {
    1039           14 :             throw ProcessError("Flow '" + myVehicleParameter->id + "' has invalid departEdge index "
    1040           28 :                                + toString(myVehicleParameter->departEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
    1041              :         }
    1042              :     }
    1043        18767 :     if (myVehicleParameter->arrivalEdgeProcedure != RouteIndexDefinition::DEFAULT) {
    1044            9 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
    1045           18 :                 myVehicleParameter->arrivalEdgeProcedure == RouteIndexDefinition::GIVEN &&
    1046            2 :                 myVehicleParameter->arrivalEdge >= (int)route->getEdges().size()) {
    1047            0 :             throw ProcessError("Flow '" + myVehicleParameter->id + "' has invalid arrivalEdge index "
    1048            0 :                                + toString(myVehicleParameter->arrivalEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
    1049              :         }
    1050              :     }
    1051        18767 :     myActiveRouteID = "";
    1052              : 
    1053              :     // check whether the vehicle shall be added directly to the network or
    1054              :     //  shall stay in the internal buffer
    1055        18767 :     if (myAddVehiclesDirectly || checkLastDepart()) {
    1056        18767 :         if (MSNet::getInstance()->getInsertionControl().addFlow(myVehicleParameter)) {
    1057        18733 :             registerLastDepart();
    1058              :         } else {
    1059           34 :             if (MSGlobals::gStateLoaded) {
    1060           34 :                 delete myVehicleParameter;
    1061              :             } else {
    1062            0 :                 throw ProcessError(TLF("Another flow with the id '%' exists.", myVehicleParameter->id));
    1063              :             }
    1064              :         }
    1065              :     }
    1066        18767 :     myVehicleParameter = nullptr;
    1067              : }
    1068              : 
    1069              : 
    1070              : void
    1071        37525 : MSRouteHandler::closeTrip() {
    1072        37525 :     myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1073        37525 :     closeRoute(true);
    1074        36933 :     closeVehicle();
    1075        36920 : }
    1076              : 
    1077              : void
    1078         4735 : MSRouteHandler::addRide(const SUMOSAXAttributes& attrs) {
    1079         4735 :     addRideOrTransport(attrs, SUMO_TAG_RIDE);
    1080         4703 : }
    1081              : 
    1082              : void
    1083          846 : MSRouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
    1084          846 :     addRideOrTransport(attrs, SUMO_TAG_TRANSPORT);
    1085          836 : }
    1086              : 
    1087              : void
    1088         5581 : MSRouteHandler::addRideOrTransport(const SUMOSAXAttributes& attrs, const SumoXMLTag modeTag) {
    1089         5581 :     if (myVehicleParameter == nullptr) {
    1090            0 :         throw ProcessError(TLF("Cannot define % stage without %.", toString(modeTag), toString(modeTag)));
    1091              :     }
    1092              :     try {
    1093         6427 :         const std::string mode = modeTag == SUMO_TAG_RIDE ? "ride" : "transport";
    1094         5623 :         std::string agent = "person";
    1095         5623 :         std::string stop = "bus stop";
    1096         5581 :         if (myActiveType == ObjectTypeEnum::CONTAINER) {
    1097              :             agent = "container";
    1098              :             stop = "container stop";
    1099              :         }
    1100              : 
    1101         5581 :         if (!((myActiveType == ObjectTypeEnum::PERSON && modeTag == SUMO_TAG_RIDE) ||
    1102          846 :                 (myActiveType == ObjectTypeEnum::CONTAINER && modeTag == SUMO_TAG_TRANSPORT))) {
    1103           24 :             throw ProcessError("Found " + mode + " inside " + agent + " element");
    1104              :         }
    1105         5569 :         const std::string aid = myVehicleParameter->id;
    1106         5569 :         bool ok = true;
    1107              :         const MSEdge* from = nullptr;
    1108        11168 :         const std::string desc = attrs.getOpt<std::string>(SUMO_ATTR_LINES, aid.c_str(), ok, "ANY");
    1109         5599 :         StringTokenizer st(desc);
    1110        11138 :         MSStoppingPlace* s = retrieveStoppingPlace(attrs, " in " + agent + " '" + aid + "'");
    1111              :         MSEdge* to = nullptr;
    1112         5569 :         if (s != nullptr) {
    1113         1114 :             to = &s->getLane().getEdge();
    1114              :         }
    1115         5569 :         double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, aid.c_str(), ok,
    1116         1114 :                             s == nullptr ? std::numeric_limits<double>::infinity() : s->getEndLanePosition());
    1117              : 
    1118              :         const SUMOVehicleParameter* startVeh = nullptr;
    1119              :         const MSEdge* startVehFrom = nullptr;
    1120         5569 :         if (myActiveTransportablePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
    1121          846 :             if (st.size() != 1) {
    1122            0 :                 throw ProcessError("Triggered departure for " + agent + " '" + aid + "' requires a unique lines value.");
    1123              :             }
    1124              :             // agent starts
    1125          846 :             MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
    1126          846 :             const std::string vehID = st.front();
    1127          846 :             SUMOVehicle* sVeh = vehControl.getVehicle(vehID);
    1128          846 :             if (sVeh == nullptr) {
    1129           40 :                 if (MSNet::getInstance()->hasFlow(vehID)) {
    1130           32 :                     startVeh = MSNet::getInstance()->getInsertionControl().getFlowPars(vehID);
    1131           32 :                     if (startVeh != nullptr) {
    1132           32 :                         ConstMSRoutePtr const route = MSRoute::dictionary(startVeh->routeid);
    1133           32 :                         startVehFrom = route->getEdges().front();
    1134              :                         // flows are inserted at the end of the time step so we
    1135              :                         // do delay the pedestrian event by one time step
    1136           32 :                         myVehicleParameter->depart = startVeh->depart + DELTA_T;
    1137           32 :                         myStartTriggeredInFlow = true;
    1138              :                     }
    1139              :                 }
    1140              :             } else {
    1141          806 :                 startVeh = &sVeh->getParameter();
    1142          806 :                 startVehFrom = sVeh->getRoute().getEdges().front();
    1143          806 :                 myVehicleParameter->depart = startVeh->depart;
    1144              :             }
    1145              :             if (startVeh == nullptr) {
    1146           16 :                 throw ProcessError("Unknown vehicle '" + vehID + "' in triggered departure for " + agent + " '" + aid + "'.");
    1147              :             }
    1148          838 :             if (startVeh->departProcedure == DepartDefinition::TRIGGERED) {
    1149            0 :                 throw ProcessError("Cannot use triggered vehicle '" + vehID + "' in triggered departure for " + agent + " '" + aid + "'.");
    1150              :             }
    1151              :         }
    1152              : 
    1153         5561 :         if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
    1154         3803 :             const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, aid.c_str(), ok);
    1155         3803 :             from = MSEdge::dictionary(fromID);
    1156         3803 :             if (from == nullptr) {
    1157            0 :                 throw ProcessError("The from edge '" + fromID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
    1158              :             }
    1159         3803 :             if (!myActiveTransportablePlan->empty() && myActiveTransportablePlan->back()->getDestination() != from) {
    1160           54 :                 const bool stopWithAccess = (myActiveTransportablePlan->back()->getDestinationStop() != nullptr
    1161           54 :                                              && &myActiveTransportablePlan->back()->getDestinationStop()->getLane().getEdge() == from);
    1162           54 :                 const bool transferAtJunction = (from->getFromJunction() == myActiveTransportablePlan->back()->getDestination()->getFromJunction()
    1163           54 :                                                  || from->getFromJunction() == myActiveTransportablePlan->back()->getDestination()->getToJunction());
    1164           54 :                 if (!(stopWithAccess || transferAtJunction)) {
    1165           14 :                     throw ProcessError("Disconnected plan for " + agent + " '" + aid +
    1166           28 :                                        "' (edge '" + fromID + "' != edge '" + myActiveTransportablePlan->back()->getDestination()->getID() + "').");
    1167              :                 }
    1168              :             }
    1169         3789 :             if (startVeh != nullptr && startVehFrom != from) {
    1170            8 :                 throw ProcessError("Disconnected plan for triggered " + agent + " '" + aid +
    1171           16 :                                    "' (edge '" + fromID + "' != edge '" + startVehFrom->getID() + "').");
    1172              :             }
    1173         1758 :         } else if (startVeh != nullptr) {
    1174              :             from = startVehFrom;
    1175              :         }
    1176         5539 :         if (myActiveTransportablePlan->empty()) {
    1177         3512 :             if (from == nullptr) {
    1178            0 :                 throw ProcessError("The start edge for " + agent + " '" + aid + "' is not known.");
    1179              :             } else {
    1180         3512 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(
    1181         7024 :                         from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
    1182              :             }
    1183              :         }
    1184              :         // given attribute may override given stopping place due access requirements
    1185         5539 :         if (to == nullptr || attrs.hasAttribute(SUMO_ATTR_TO)) {
    1186         4501 :             const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, aid.c_str(), ok);
    1187         4501 :             to = MSEdge::dictionary(toID);
    1188         4501 :             if (to == nullptr) {
    1189            0 :                 throw ProcessError("The to edge '" + toID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
    1190              :             }
    1191              :         }
    1192         5539 :         const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, aid.c_str(), ok, OptionsCont::getOptions().getString("persontrip.default.group"));
    1193         5539 :         const std::string intendedVeh = attrs.getOpt<std::string>(SUMO_ATTR_INTENDED, nullptr, ok, "");
    1194         5539 :         const SUMOTime intendedDepart = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DEPART, nullptr, ok, -1);
    1195        11078 :         arrivalPos = SUMOVehicleParameter::interpretEdgePos(arrivalPos, to->getLength(), SUMO_ATTR_ARRIVALPOS, agent + " '" + aid + "' takes a " + mode + " to edge '" + to->getID() + "'");
    1196         5539 :         myActiveTransportablePlan->push_back(new MSStageDriving(from, to, s, arrivalPos, 0.0, st.getVector(), group, intendedVeh, intendedDepart));
    1197         5539 :         myParamStack.push_back(myActiveTransportablePlan->back());
    1198         5611 :     } catch (ProcessError&) {
    1199           42 :         deleteActivePlanAndVehicleParameter();
    1200           42 :         throw;
    1201           42 :     }
    1202         5539 : }
    1203              : 
    1204              : MSStoppingPlace*
    1205        81844 : MSRouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, SUMOVehicleParameter::Stop* stopParam) {
    1206        81844 :     bool ok = true;
    1207              :     // dummy stop parameter to hold the attributes
    1208        81844 :     SUMOVehicleParameter::Stop stop;
    1209        81844 :     if (stopParam != nullptr) {
    1210        35280 :         stop = *stopParam;
    1211              :     } else {
    1212        93128 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
    1213        46564 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
    1214        46564 :         stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
    1215        46564 :         stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
    1216        46564 :         stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
    1217        93128 :         stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
    1218              :     }
    1219              :     MSStoppingPlace* toStop = nullptr;
    1220        81844 :     if (stop.busstop != "") {
    1221        10925 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
    1222        10925 :         if (toStop == nullptr) {
    1223           12 :             ok = false;
    1224           36 :             WRITE_ERROR(TLF("The busStop '%' is not known%.", stop.busstop, errorSuffix));
    1225              :         }
    1226        70919 :     } else if (stop.containerstop != "") {
    1227         1189 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
    1228         1189 :         if (toStop == nullptr) {
    1229            0 :             ok = false;
    1230            0 :             WRITE_ERROR(TLF("The containerStop '%' is not known%.", stop.containerstop, errorSuffix));
    1231              :         }
    1232        69730 :     } else if (stop.parkingarea != "") {
    1233         9139 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
    1234         9139 :         if (toStop == nullptr) {
    1235            0 :             ok = false;
    1236            0 :             WRITE_ERROR(TLF("The parkingArea '%' is not known%.", stop.parkingarea, errorSuffix));
    1237              :         }
    1238        60591 :     } else if (stop.chargingStation != "") {
    1239              :         // ok, we have a charging station
    1240         1578 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
    1241         1578 :         if (toStop == nullptr) {
    1242            0 :             ok = false;
    1243            0 :             WRITE_ERROR(TLF("The chargingStation '%' is not known%.", stop.chargingStation, errorSuffix));
    1244              :         }
    1245        59013 :     } else if (stop.overheadWireSegment != "") {
    1246              :         // ok, we have an overhead wire segment
    1247            0 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.overheadWireSegment, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
    1248            0 :         if (toStop == nullptr) {
    1249            0 :             ok = false;
    1250            0 :             WRITE_ERROR(TLF("The overhead wire segment '%' is not known%.", stop.overheadWireSegment, errorSuffix));
    1251              :         }
    1252              :     }
    1253        81844 :     if (!ok && MSGlobals::gCheckRoutes) {
    1254           18 :         throw ProcessError(TLF("Invalid stop definition%.", errorSuffix));
    1255              :     }
    1256        81838 :     return toStop;
    1257        81844 : }
    1258              : 
    1259              : Parameterised*
    1260        35307 : MSRouteHandler::addStop(const SUMOSAXAttributes& attrs) {
    1261              :     Parameterised* result = nullptr;
    1262              :     try {
    1263              :         std::string errorSuffix;
    1264        35307 :         if (myActiveType == ObjectTypeEnum::PERSON) {
    1265         3483 :             errorSuffix = " in person '" + myVehicleParameter->id + "'";
    1266        34146 :         } else if (myActiveType == ObjectTypeEnum::CONTAINER) {
    1267          600 :             errorSuffix = " in container '" + myVehicleParameter->id + "'";
    1268        33946 :         } else if (myVehicleParameter != nullptr) {
    1269        92433 :             errorSuffix = " in vehicle '" + myVehicleParameter->id + "'";
    1270              :         } else {
    1271         9515 :             errorSuffix = " in route '" + myActiveRouteID + "'";
    1272              :         }
    1273        35307 :         SUMOVehicleParameter::Stop stop;
    1274        70614 :         bool ok = parseStop(stop, attrs, errorSuffix, MsgHandler::getErrorInstance());
    1275        35307 :         if (!ok) {
    1276           27 :             if (MSGlobals::gCheckRoutes) {
    1277           27 :                 throw ProcessError();
    1278              :             }
    1279              :             return result;
    1280              :         }
    1281        35280 :         const MSEdge* edge = nullptr;
    1282        35280 :         MSStoppingPlace* toStop = retrieveStoppingPlace(attrs, errorSuffix, &stop);
    1283              :         // if one of the previous stops is defined
    1284        35274 :         if (toStop != nullptr) {
    1285        18224 :             const MSLane& l = toStop->getLane();
    1286              :             stop.lane = l.getID();
    1287        18224 :             if ((stop.parametersSet & STOP_END_SET) == 0) {
    1288        18208 :                 stop.endPos = toStop->getEndLanePosition();
    1289              :             } else {
    1290           16 :                 stop.endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, nullptr, ok);
    1291              :             }
    1292        18224 :             stop.startPos = toStop->getBeginLanePosition();
    1293        18224 :             edge = &l.getEdge();
    1294              :         } else {
    1295              :             // no, the lane and the position should be given directly
    1296              :             // get the lane
    1297        17050 :             stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
    1298        17160 :             stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
    1299        17050 :             if (ok && stop.edge != "") { // edge is given directly
    1300         1273 :                 edge = MSEdge::dictionary(stop.edge);
    1301         1273 :                 if (edge == nullptr || (edge->isInternal() && !MSGlobals::gUsingInternalLanes)) {
    1302           18 :                     throw ProcessError(TLF("The edge '%' for a stop is not known%.", stop.edge, errorSuffix));
    1303              :                 }
    1304        15777 :             } else if (ok && stop.lane != "") { // lane is given directly
    1305        15711 :                 MSLane* stopLane = MSLane::dictionary(stop.lane);
    1306        15711 :                 if (stopLane == nullptr) {
    1307              :                     // check for opposite-direction stop
    1308          153 :                     stopLane = MSBaseVehicle::interpretOppositeStop(stop);
    1309          153 :                     if (stopLane != nullptr) {
    1310          145 :                         edge = MSEdge::dictionary(stop.edge);
    1311              :                     }
    1312              :                 } else {
    1313        15558 :                     edge = &stopLane->getEdge();
    1314              :                 }
    1315        15703 :                 if (stopLane == nullptr || (stopLane->isInternal() && !MSGlobals::gUsingInternalLanes)) {
    1316          150 :                     throw ProcessError(TLF("The lane '%' for a stop is not known%.", stop.lane, errorSuffix));
    1317              :                 }
    1318           66 :             } else if (ok && ((attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y))
    1319           31 :                               || (attrs.hasAttribute(SUMO_ATTR_LON) && attrs.hasAttribute(SUMO_ATTR_LAT)))) {
    1320              :                 Position pos;
    1321              :                 bool geo = false;
    1322           49 :                 if (attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y)) {
    1323           35 :                     pos = Position(attrs.get<double>(SUMO_ATTR_X, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_Y, myVehicleParameter->id.c_str(), ok));
    1324              :                 } else {
    1325           14 :                     pos = Position(attrs.get<double>(SUMO_ATTR_LON, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_LAT, myVehicleParameter->id.c_str(), ok));
    1326              :                     geo = true;
    1327              :                 }
    1328           49 :                 PositionVector positions;
    1329           49 :                 positions.push_back(pos);
    1330              :                 ConstMSEdgeVector geoEdges;
    1331              :                 SUMOVehicleClass vClass = SVC_PASSENGER;
    1332           49 :                 auto& vc = MSNet::getInstance()->getVehicleControl();
    1333           49 :                 if (!vc.getVTypeDistribution(myVehicleParameter->vtypeid)) {
    1334           49 :                     MSVehicleType* const type = vc.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
    1335           49 :                     if (type != nullptr) {
    1336           49 :                         vClass = type->getParameter().vehicleClass;
    1337              :                     }
    1338              :                 }
    1339           49 :                 parseGeoEdges(positions, geo, vClass, geoEdges, myVehicleParameter->id, true, ok, true);
    1340           49 :                 if (ok) {
    1341           42 :                     edge = geoEdges.front();
    1342           42 :                     if (geo) {
    1343           14 :                         GeoConvHelper::getFinal().x2cartesian_const(pos);
    1344              :                     }
    1345           42 :                     stop.parametersSet |= STOP_END_SET;
    1346           42 :                     stop.endPos = edge->getLanes()[0]->getShape().nearest_offset_to_point2D(pos, false);
    1347              :                 } else {
    1348           21 :                     throw ProcessError(TLF("Could not map stop position '%' to the network%.", pos, errorSuffix));
    1349              :                 }
    1350           56 :             } else {
    1351           17 :                 if (myActiveTransportablePlan && !myActiveTransportablePlan->empty()) { // use end of movement before
    1352           11 :                     toStop = myActiveTransportablePlan->back()->getDestinationStop();
    1353           11 :                     if (toStop != nullptr) { // use end of movement before definied as a stopping place
    1354            0 :                         edge = &toStop->getLane().getEdge();
    1355            0 :                         stop.lane = toStop->getLane().getID();
    1356            0 :                         stop.endPos = toStop->getEndLanePosition();
    1357            0 :                         stop.startPos = toStop->getBeginLanePosition();
    1358              :                     } else { // use end of movement before definied as lane/edge
    1359           11 :                         edge = myActiveTransportablePlan->back()->getDestination();
    1360           11 :                         stop.lane = edge->getLanes()[0]->getID();
    1361           11 :                         stop.endPos = myActiveTransportablePlan->back()->getArrivalPos();
    1362           22 :                         stop.startPos = MAX2(0., stop.endPos - MIN_STOP_LENGTH);
    1363              :                     }
    1364              :                 } else {
    1365           12 :                     const std::string msg = TLF("A stop must be placed on a busStop, a chargingStation, an overheadWireSegment, a containerStop, a parkingArea, an edge or a lane%.", errorSuffix);
    1366            6 :                     if (MSGlobals::gCheckRoutes) {
    1367            0 :                         throw ProcessError(msg);
    1368              :                     } else {
    1369           18 :                         WRITE_WARNING(msg);
    1370              :                         return result;
    1371              :                     }
    1372              :                 }
    1373              :             }
    1374        16981 :             stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
    1375        16981 :             if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
    1376            0 :                 WRITE_WARNINGF(TL("Deprecated attribute 'pos' in description of stop%."), errorSuffix);
    1377            0 :                 stop.endPos = attrs.getOpt<double>(SUMO_ATTR_POSITION, nullptr, ok, stop.endPos);
    1378              :             }
    1379        33342 :             stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, MAX2(0., stop.endPos - MIN_STOP_LENGTH));
    1380        16981 :             if (!myAmLoadingState) {
    1381        24617 :                 const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS))
    1382        16744 :                                          || !MSGlobals::gCheckRoutes;
    1383        16744 :                 if (!ok || (checkStopPos(stop.startPos, stop.endPos, edge->getLength(), 0, friendlyPos) != StopPos::STOPPOS_VALID)) {
    1384            0 :                     throw ProcessError(TLF("Invalid start or end position for stop on %'%.",
    1385            0 :                                            stop.lane != "" ? ("lane '" + stop.lane) : ("edge '" + stop.edge), errorSuffix));
    1386              :                 }
    1387              :             }
    1388              :         }
    1389        35205 :         stop.edge = edge->getID();
    1390        35205 :         if (myActiveTransportablePlan) {
    1391         1354 :             if (myActiveTransportablePlan->empty()) {
    1392          543 :                 double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
    1393          956 :                                    ? myVehicleParameter->departPos
    1394          491 :                                    : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
    1395          904 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(
    1396         1808 :                         edge, toStop, -1, myVehicleParameter->depart, departPos, "start", true));
    1397          450 :             } else if (myActiveTransportablePlan->back()->getDestination() != edge) {
    1398           21 :                 if (myActiveTransportablePlan->back()->getDestination()->isTazConnector()) {
    1399            7 :                     myActiveTransportablePlan->back()->setDestination(edge, toStop);
    1400              :                 } else {
    1401           56 :                     throw ProcessError(TLF("Disconnected plan for % '%' (%!=%).", myActiveTypeName, myVehicleParameter->id,
    1402           28 :                                 edge->getID(), myActiveTransportablePlan->back()->getDestination()->getID()));
    1403              :                 }
    1404              :             }
    1405              :             // transporting veh stops somewhere
    1406          429 :             else if (myActiveTransportablePlan->back()->getStageType() == MSStageType::WAITING
    1407          429 :                      && (attrs.hasAttribute(SUMO_ATTR_ENDPOS) || attrs.hasAttribute(SUMO_ATTR_STARTPOS))) {
    1408           12 :                 const double start = SUMOVehicleParameter::interpretEdgePos(stop.startPos, edge->getLength(), SUMO_ATTR_STARTPOS, "stopping at " + edge->getID());
    1409           12 :                 const double end = SUMOVehicleParameter::interpretEdgePos(stop.endPos, edge->getLength(), SUMO_ATTR_ENDPOS, "stopping at " + edge->getID());
    1410           12 :                 const double prevAr = myActiveTransportablePlan->back()->getArrivalPos();
    1411           12 :                 if (start > prevAr + NUMERICAL_EPS || end < prevAr - NUMERICAL_EPS) {
    1412           36 :                     WRITE_WARNINGF(TL("Disconnected plan for % '%' (stop range %-% does not cover previous arrival position %)."),
    1413              :                                    myActiveTypeName, myVehicleParameter->id, toString(start), toString(end), toString(prevAr));
    1414              :                 }
    1415              :             }
    1416         1340 :             std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "");
    1417         1340 :             double pos = (stop.startPos + stop.endPos) / 2.;
    1418         1340 :             if (!myActiveTransportablePlan->empty()) {
    1419         1340 :                 pos = myActiveTransportablePlan->back()->getArrivalPos();
    1420              :             }
    1421         1340 :             myActiveTransportablePlan->push_back(new MSStageWaiting(edge, toStop, stop.duration, stop.until, pos, actType, false));
    1422         1340 :             result = myActiveTransportablePlan->back();
    1423              : 
    1424        33851 :         } else if (myVehicleParameter != nullptr) {
    1425        30716 :             myVehicleParameter->stops.push_back(stop);
    1426        30716 :             result = &myVehicleParameter->stops.back();
    1427              :         } else {
    1428         3135 :             myActiveRouteStops.push_back(stop);
    1429              :             result = &myActiveRouteStops.back();
    1430              :         }
    1431        35191 :         if (myInsertStopEdgesAt >= 0) {
    1432              :             //std::cout << " myInsertStopEdgesAt=" << myInsertStopEdgesAt << " edge=" << edge->getID() << " myRoute=" << toString(myActiveRoute) << "\n";
    1433         7554 :             if (edge->isInternal()) {
    1434            9 :                 if (myInsertStopEdgesAt > 0 && *(myActiveRoute.begin() + (myInsertStopEdgesAt - 1)) != edge->getNormalBefore()) {
    1435            0 :                     myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge->getNormalBefore());
    1436            0 :                     myInsertStopEdgesAt++;
    1437              :                 }
    1438            9 :                 myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge->getNormalSuccessor());
    1439            9 :                 myInsertStopEdgesAt++;
    1440              :             } else {
    1441         7545 :                 myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
    1442         7545 :                 myInsertStopEdgesAt++;
    1443              :             }
    1444        27637 :         } else if (myHaveVia) {
    1445              :             // vias were loaded, check for consistency
    1446         3396 :             if (std::find(myActiveRoute.begin(), myActiveRoute.end(), edge) == myActiveRoute.end()) {
    1447           21 :                 WRITE_WARNINGF(TL("Stop edge '%' missing in attribute 'via' for % '%'."),
    1448              :                                edge->getID(), myActiveTypeName, myVehicleParameter->id);
    1449              :             }
    1450              :         }
    1451        35417 :     } catch (ProcessError&) {
    1452          110 :         deleteActivePlanAndVehicleParameter();
    1453          110 :         throw;
    1454          110 :     }
    1455        35191 :     return result;
    1456              : }
    1457              : 
    1458              : 
    1459              : void
    1460        40905 : MSRouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
    1461              :                                    const MSEdge* fromEdge, const MSEdge*& toEdge,
    1462              :                                    double& departPos, double& arrivalPos, MSStoppingPlace*& bs,
    1463              :                                    const MSStage* const lastStage, bool& ok) {
    1464              :     try {
    1465        40905 :         const std::string description = "person '" + personID + "' walking from edge '" + fromEdge->getID() + "'";
    1466              : 
    1467        40905 :         if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
    1468            0 :             WRITE_WARNING(TL("The attribute departPos is no longer supported for walks, please use the person attribute, the arrivalPos of the previous step or explicit stops."));
    1469              :         }
    1470        40905 :         departPos = 0.;
    1471        40905 :         if (lastStage != nullptr) {
    1472        32863 :             if (lastStage->getDestinationStop() != nullptr) {
    1473          285 :                 departPos = lastStage->getDestinationStop()->getAccessPos(fromEdge, &myParsingRNG);
    1474        32578 :             } else if (lastStage->getDestination() == fromEdge) {
    1475        32525 :                 departPos = lastStage->getArrivalPos();
    1476           53 :             } else if (lastStage->getDestination()->getToJunction() == fromEdge->getToJunction()) {
    1477           22 :                 departPos = fromEdge->getLength();
    1478              :             }
    1479              :         }
    1480              : 
    1481        40905 :         bs = retrieveStoppingPlace(attrs, " " + description);
    1482        40905 :         if (bs != nullptr) {
    1483         6441 :             arrivalPos = bs->getAccessPos(toEdge != nullptr ? toEdge : &bs->getLane().getEdge());
    1484         3391 :             if (arrivalPos < 0) {
    1485            0 :                 throw ProcessError("Bus stop '" + bs->getID() + "' is not connected to arrival edge '" + toEdge->getID() + "' for " + description + ".");
    1486              :             }
    1487         3391 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1488            8 :                 const double length = toEdge != nullptr ? toEdge->getLength() : bs->getLane().getLength();
    1489            8 :                 const double arrPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, description, length,
    1490            8 :                                       attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
    1491            8 :                 if (arrPos >= bs->getBeginLanePosition() && arrPos < bs->getEndLanePosition()) {
    1492            4 :                     arrivalPos = arrPos;
    1493              :                 } else {
    1494           12 :                     WRITE_WARNINGF(TL("Ignoring arrivalPos for % because it is outside the given stop '%'."), description, toString(SUMO_ATTR_BUS_STOP));
    1495            4 :                     arrivalPos = bs->getAccessPos(&bs->getLane().getEdge());
    1496              :                 }
    1497              :             }
    1498              :         } else {
    1499        37514 :             if (toEdge == nullptr) {
    1500            0 :                 throw ProcessError(TLF("No destination edge for %.", description));
    1501              :             }
    1502        37514 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1503        31700 :                 arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, description, toEdge->getLength(),
    1504        63400 :                              attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
    1505              :             } else {
    1506         5814 :                 arrivalPos = toEdge->getLength() / 2.;
    1507              :             }
    1508              :         }
    1509            0 :     } catch (ProcessError&) {
    1510            0 :         deleteActivePlanAndVehicleParameter();
    1511            0 :         throw;
    1512            0 :     }
    1513        40905 : }
    1514              : 
    1515              : 
    1516              : void
    1517         8055 : MSRouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
    1518         8055 :     if (myVehicleParameter == nullptr) {
    1519           12 :         throw ProcessError(TL("Cannot define person stage without person."));
    1520              :     }
    1521              :     try {
    1522              :         myActiveRoute.clear();
    1523         8049 :         bool ok = true;
    1524              :         const char* const id = myVehicleParameter->id.c_str();
    1525              :         const MSEdge* from = nullptr;
    1526         8049 :         const MSEdge* to = nullptr;
    1527         8049 :         parseFromViaTo(SUMO_TAG_PERSON, attrs);
    1528         8042 :         myInsertStopEdgesAt = -1;
    1529        10543 :         if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
    1530        10445 :                 || attrs.hasAttribute(SUMO_ATTR_FROMXY) || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
    1531         5688 :             from = myActiveRoute.front();
    1532         2354 :         } else if (myActiveTransportablePlan->empty()) {
    1533            0 :             throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
    1534              :         } else {
    1535         2354 :             from = myActiveTransportablePlan->back()->getDestination();
    1536              :         }
    1537        11924 :         if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
    1538        11162 :             || attrs.hasAttribute(SUMO_ATTR_TOXY) || attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
    1539         4992 :             to = myActiveRoute.back();
    1540              :         } // else, to may also be derived from stopping place
    1541              : 
    1542         8042 :         const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
    1543         8042 :         if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1544            0 :             throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1545              :         }
    1546              : 
    1547         8042 :         double departPos = 0;
    1548         8042 :         double arrivalPos = 0;
    1549         8042 :         MSStoppingPlace* stoppingPlace = nullptr;
    1550         8042 :         parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, stoppingPlace, nullptr, ok);
    1551              : 
    1552         8042 :         SVCPermissions modeSet = 0;
    1553         8042 :         if (attrs.hasAttribute(SUMO_ATTR_MODES)) {
    1554         1924 :             const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
    1555              :             std::string errorMsg;
    1556              :             // try to parse person modes
    1557         1924 :             if (!SUMOVehicleParameter::parsePersonModes(modes, "person", id, modeSet, errorMsg)) {
    1558            0 :                 throw InvalidArgument(errorMsg);
    1559              :             }
    1560              :         } else {
    1561         7080 :             modeSet = myVehicleParameter->modes;
    1562              :         }
    1563         8042 :         const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, OptionsCont::getOptions().getString("persontrip.default.group"));
    1564         8042 :         MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
    1565        24126 :         const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, myVehicleParameter->vTypes);
    1566        16183 :         for (StringTokenizer st(types); st.hasNext();) {
    1567           99 :             const std::string vtypeid = st.next();
    1568           99 :             const MSVehicleType* const vType = vehControl.getVType(vtypeid);
    1569           99 :             if (vType == nullptr) {
    1570            0 :                 throw InvalidArgument("The vehicle type '" + vtypeid + "' in a trip for person '" + myVehicleParameter->id + "' is not known.");
    1571              :             }
    1572          198 :             modeSet |= (vType->getVehicleClass() == SVC_BICYCLE) ? SVC_BICYCLE : SVC_PASSENGER;
    1573         8042 :         }
    1574         8042 :         const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id, ok, -1.);
    1575         8042 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
    1576            0 :             throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
    1577              :         }
    1578         8042 :         const double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
    1579        16084 :         const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), -1, "personTrip");
    1580         8042 :         if (ok) {
    1581         8042 :             if (myActiveTransportablePlan->empty()) {
    1582         5539 :                 double initialDepartPos = myVehicleParameter->departPos;
    1583         5539 :                 if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
    1584              :                     initialDepartPos = RandHelper::rand(from->getLength(), &myParsingRNG);
    1585              :                 }
    1586        11078 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(from, nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
    1587              :             }
    1588         8042 :             myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1589         8042 :             MSStoppingPlace* fromStop = myActiveTransportablePlan->empty() ? nullptr : myActiveTransportablePlan->back()->getDestinationStop();
    1590        11092 :             myActiveTransportablePlan->push_back(new MSStageTrip(from, fromStop, to == nullptr ? &stoppingPlace->getLane().getEdge() : to,
    1591              :                                                  stoppingPlace, duration, modeSet, types, speed, walkFactor, group,
    1592        11092 :                                                  departPosLat, attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS), arrivalPos));
    1593         8042 :             myParamStack.push_back(myActiveTransportablePlan->back());
    1594         8042 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1595         1467 :                 myActiveTransportablePlan->back()->markSet(VEHPARS_ARRIVALPOS_SET);
    1596              :             }
    1597              :         }
    1598              :         myActiveRoute.clear();
    1599            7 :     } catch (ProcessError&) {
    1600            7 :         deleteActivePlanAndVehicleParameter();
    1601            7 :         throw;
    1602            7 :     }
    1603         8042 : }
    1604              : 
    1605              : 
    1606              : void
    1607        38379 : MSRouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
    1608        38379 :     if (myVehicleParameter == nullptr) {
    1609            0 :         throw ProcessError(TL("Cannot define person stage without person."));
    1610              :     }
    1611        38379 :     myActiveRouteID = "";
    1612        38379 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1613              :         try {
    1614              :             myActiveRoute.clear();
    1615        32873 :             bool ok = true;
    1616        32873 :             const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, nullptr, ok, -1);
    1617        32873 :             if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1618           24 :                 throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1619              :             }
    1620              :             double speed = -1; // default to vType speed
    1621        32865 :             if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
    1622          313 :                 speed = attrs.get<double>(SUMO_ATTR_SPEED, nullptr, ok);
    1623          313 :                 if (speed <= 0) {
    1624            0 :                     throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
    1625              :                 }
    1626              :             }
    1627        32865 :             double departPos = 0;
    1628        32865 :             double arrivalPos = 0;
    1629        32865 :             MSStoppingPlace* bs = nullptr;
    1630        32865 :             if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1631          616 :                 myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
    1632          616 :                 ConstMSRoutePtr route = MSRoute::dictionary(myActiveRouteID, &myParsingRNG);
    1633          616 :                 if (route == nullptr) {
    1634            0 :                     throw ProcessError("The route '" + myActiveRouteID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
    1635              :                 }
    1636          616 :                 myActiveRoute = route->getEdges();
    1637              :             } else {
    1638        64496 :                 MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, myActiveRouteID);
    1639              :             }
    1640        32863 :             if (myActiveTransportablePlan->empty()) {
    1641        32404 :                 double initialDepartPos = myVehicleParameter->departPos;
    1642        32404 :                 if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
    1643           22 :                     initialDepartPos = RandHelper::rand(myActiveRoute.front()->getLength(), &myParsingRNG);
    1644              :                 }
    1645        64808 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
    1646              :             }
    1647        32863 :             parseWalkPositions(attrs, myVehicleParameter->id, myActiveRoute.front(), myActiveRoute.back(), departPos, arrivalPos, bs, myActiveTransportablePlan->back(), ok);
    1648        32863 :             if (myActiveRoute.empty()) {
    1649            0 :                 throw ProcessError(TLF("No edges to walk for person '%'.", myVehicleParameter->id));
    1650              :             }
    1651        33132 :             if (myActiveTransportablePlan->back()->getDestination() != myActiveRoute.front() &&
    1652        32863 :                     myActiveTransportablePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getFromJunction() &&
    1653          240 :                     myActiveTransportablePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getToJunction()) {
    1654          215 :                 if (myActiveTransportablePlan->back()->getDestinationStop() == nullptr || myActiveTransportablePlan->back()->getDestinationStop()->getAccessPos(myActiveRoute.front()) < 0.) {
    1655           24 :                     throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + " not connected to " + myActiveTransportablePlan->back()->getDestination()->getID() + ").");
    1656              :                 }
    1657              :             }
    1658        32851 :             const int departLane = attrs.getOpt<int>(SUMO_ATTR_DEPARTLANE, nullptr, ok, -1);
    1659        65702 :             const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), departLane, "walk");
    1660        32851 :             myActiveTransportablePlan->push_back(new MSStageWalking(myVehicleParameter->id, myActiveRoute, bs, duration, speed, departPos, arrivalPos, departPosLat, departLane, myActiveRouteID));
    1661        32851 :             myParamStack.push_back(myActiveTransportablePlan->back());
    1662        32851 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1663        30241 :                 myActiveTransportablePlan->back()->markSet(VEHPARS_ARRIVALPOS_SET);
    1664              :             }
    1665              :             myActiveRoute.clear();
    1666           22 :         } catch (ProcessError&) {
    1667           22 :             deleteActivePlanAndVehicleParameter();
    1668           22 :             throw;
    1669           22 :         }
    1670              :     } else { // parse walks from->to as person trips
    1671         5506 :         addPersonTrip(attrs);
    1672              :     }
    1673        38357 : }
    1674              : 
    1675              : double
    1676        40893 : MSRouteHandler::interpretDepartPosLat(const std::string& value, int departLane, const std::string& element) {
    1677        40893 :     double pos = MSPModel::UNSPECIFIED_POS_LAT;
    1678        40893 :     if (value == "") {
    1679              :         return pos;
    1680              :     }
    1681              :     std::string error;
    1682              :     DepartPosLatDefinition dpd;
    1683          250 :     if (SUMOVehicleParameter::parseDepartPosLat(value, element, myVehicleParameter->id, pos, dpd, error)) {
    1684          250 :         if (dpd != DepartPosLatDefinition::GIVEN) {
    1685            4 :             const MSLane* lane = MSStageMoving::checkDepartLane(myActiveRoute.front(), SVC_IGNORING, departLane, myVehicleParameter->id);
    1686            4 :             if (lane == nullptr) {
    1687            0 :                 throw ProcessError(TLF("Could not find departure lane for walk of person '%' when interpreting departPosLat", myVehicleParameter->id));
    1688              :             }
    1689            4 :             const double usableWidth = lane->getWidth() - 0.5;
    1690            4 :             switch (dpd) {
    1691            0 :                 case DepartPosLatDefinition::RIGHT:
    1692            0 :                     pos = -usableWidth / 2;
    1693            0 :                     break;
    1694            0 :                 case DepartPosLatDefinition::LEFT:
    1695            0 :                     pos = usableWidth / 2;
    1696            0 :                     break;
    1697            0 :                 case DepartPosLatDefinition::CENTER:
    1698            0 :                     pos = 0;
    1699            0 :                     break;
    1700            4 :                 case DepartPosLatDefinition::RANDOM:
    1701              :                 case DepartPosLatDefinition::FREE:
    1702              :                 case DepartPosLatDefinition::RANDOM_FREE:
    1703              :                     /// @note must be randomized for every person individually when loading a personFlow
    1704            4 :                     pos = MSPModel::RANDOM_POS_LAT;
    1705            4 :                     break;
    1706              :                 default:
    1707              :                     break;
    1708              :             }
    1709              :         }
    1710              :     } else {
    1711            0 :         throw ProcessError(error);
    1712              :     }
    1713          250 :     return pos;
    1714              : }
    1715              : 
    1716              : 
    1717              : void
    1718        42745 : MSRouteHandler::addTransportable(const SUMOSAXAttributes& /*attrs*/, const bool isPerson) {
    1719        42745 :     myActiveType = isPerson ? ObjectTypeEnum::PERSON : ObjectTypeEnum::CONTAINER;
    1720        42745 :     if (!MSNet::getInstance()->getVehicleControl().hasVType(myVehicleParameter->vtypeid)) {
    1721            0 :         const std::string error = TLF("The type '%' for % '%' is not known.", myVehicleParameter->vtypeid, myActiveTypeName, myVehicleParameter->id);
    1722            0 :         deleteActivePlanAndVehicleParameter();
    1723            0 :         throw ProcessError(error);
    1724              :     }
    1725        42745 :     myActiveTransportablePlan = new MSTransportable::MSTransportablePlan();
    1726        42745 : }
    1727              : 
    1728              : 
    1729              : void
    1730          487 : MSRouteHandler::addTranship(const SUMOSAXAttributes& attrs) {
    1731              :     try {
    1732              :         myActiveRoute.clear();
    1733          487 :         const std::string cid = myVehicleParameter->id;
    1734          487 :         bool ok = true;
    1735          487 :         const MSEdge* from = nullptr;
    1736          487 :         const MSEdge* to = nullptr;
    1737              :         MSStoppingPlace* cs = nullptr;
    1738              : 
    1739              :         double speed;
    1740          487 :         const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid);
    1741          487 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED)) { // speed is explicitly set
    1742           71 :             speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, -1);
    1743           71 :             if (!ok) {
    1744            0 :                 throw ProcessError(TLF("Could not read tranship speed for container '%'.", cid));
    1745              :             }
    1746          416 :         } else if (vtype != nullptr && vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) { // speed is set by vtype
    1747              :             speed = vtype->getMaxSpeed();
    1748              :         } else { // default speed value
    1749          416 :             speed = DEFAULT_CONTAINER_TRANSHIP_SPEED;
    1750              :         }
    1751          487 :         if (speed <= 0) {
    1752            0 :             throw ProcessError(TLF("Non-positive tranship speed for container '%'.", cid));
    1753              :         }
    1754              :         // values from preceding stage:
    1755              :         const MSEdge* preEdge = nullptr;
    1756              :         double prePos = 0;
    1757          487 :         if (!myActiveTransportablePlan->empty()) {
    1758          152 :             preEdge = myActiveTransportablePlan->back()->getDestination();
    1759          152 :             prePos = myActiveTransportablePlan->back()->getArrivalPos();
    1760              :         }
    1761              :         // set depart position as given attribute value, arrival position of preceding stage or default (=0)
    1762          487 :         double departPos = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS, cid.c_str(), ok, prePos);
    1763              : 
    1764          487 :         if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    1765          252 :             MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, cid.c_str(), ok), myActiveRoute, myActiveRouteID);
    1766              :         } else {
    1767              :             // set 'from':
    1768          361 :             if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
    1769          279 :                 const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, cid.c_str(), ok);
    1770          279 :                 from = MSEdge::dictionary(fromID);
    1771          279 :                 if (from == nullptr) {
    1772            0 :                     throw ProcessError("The from edge '" + fromID + "' within a tranship of container '" + cid + "' is not known.");
    1773              :                 }
    1774          279 :                 if (preEdge != nullptr && preEdge != from) {
    1775            0 :                     throw ProcessError("Disconnected plan for container '" + cid + "' (" + from->getID() + "!=" + preEdge->getID() + ").");
    1776              :                 }
    1777           82 :             } else if (preEdge == nullptr) {
    1778            0 :                 throw ProcessError(TLF("The start edge for container '%' is not known.", cid));
    1779              :             } else {
    1780           82 :                 from = preEdge;
    1781              :             }
    1782              :             // set 'to':
    1783          361 :             if (attrs.hasAttribute(SUMO_ATTR_TO)) {
    1784          271 :                 const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, cid.c_str(), ok);
    1785          271 :                 to = MSEdge::dictionary(toID);
    1786          271 :                 if (to == nullptr) {
    1787            0 :                     throw ProcessError("The to edge '" + toID + "' within a tranship of container '" + cid + "' is not known.");
    1788              :                 }
    1789              :             } else {
    1790           90 :                 const std::string description = "container '" + cid + "' transhipping from edge '" + from->getID() + "'";
    1791           90 :                 cs = retrieveStoppingPlace(attrs, " " + description);
    1792           90 :                 if (cs != nullptr) {
    1793           90 :                     to = &cs->getLane().getEdge();
    1794              :                 } else {
    1795            0 :                     throw ProcessError(TLF("Inconsistent tranship for container '%', needs either: 'edges', 'to', 'containerStop' (or any other stopping place)", cid));
    1796              :                 }
    1797              :             }
    1798          361 :             myActiveRoute.push_back(from);
    1799          361 :             myActiveRoute.push_back(to);
    1800              :         }
    1801          487 :         if (myActiveRoute.empty()) {
    1802            0 :             throw ProcessError(TLF("No edges to tranship container '%'.", cid));
    1803              :         }
    1804          487 :         if (preEdge == nullptr) { // additional 'stop' to start the container plan
    1805          335 :             myActiveTransportablePlan->push_back(new MSStageWaiting(
    1806          670 :                     myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, departPos, "start", true));
    1807              :         }
    1808          974 :         double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, cid.c_str(), ok,
    1809          487 :                             cs == nullptr ? myActiveRoute.back()->getLength() : cs->getEndLanePosition());
    1810          487 :         myActiveTransportablePlan->push_back(new MSStageTranship(myActiveRoute, cs, speed, departPos, arrivalPos));
    1811          487 :         myParamStack.push_back(myActiveTransportablePlan->back());
    1812              :         myActiveRoute.clear();
    1813            0 :     } catch (ProcessError&) {
    1814            0 :         deleteActivePlanAndVehicleParameter();
    1815            0 :         throw;
    1816            0 :     }
    1817          487 : }
    1818              : 
    1819              : 
    1820              : void
    1821          119 : MSRouteHandler::initLaneTree(NamedRTree* tree) {
    1822         4763 :     for (const auto& edge : MSEdge::getAllEdges()) {
    1823         4644 :         if (edge->isNormal() || MSGlobals::gUsingInternalLanes) {
    1824         8518 :             for (MSLane* lane : edge->getLanes()) {
    1825         4178 :                 Boundary b = lane->getShape().getBoxBoundary();
    1826         4178 :                 const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
    1827         4178 :                 const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
    1828         4178 :                 tree->Insert(cmin, cmax, lane);
    1829         4178 :             }
    1830              :         }
    1831              :     }
    1832          119 : }
    1833              : 
    1834              : 
    1835              : SumoRNG*
    1836            0 : MSRouteHandler::getRNG() {
    1837            0 :     return &myParsingRNG;
    1838              : }
    1839              : 
    1840              : 
    1841              : MSEdge*
    1842          980 : MSRouteHandler::retrieveEdge(const std::string& id) {
    1843          980 :     return MSEdge::dictionary(id);
    1844              : }
    1845              : 
    1846              : 
    1847              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1