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

Generated by: LCOV version 2.0-1