LCOV - code coverage report
Current view: top level - src/microsim - MSRouteHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.0 % 1172 1055
Test Date: 2026-07-26 16:30:20 Functions: 97.7 % 43 42

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 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        79515 : MSRouteHandler::MSRouteHandler(const std::string& file, bool addVehiclesDirectly) :
      59              :     SUMORouteHandler(file, addVehiclesDirectly ? "" : "routes", true),
      60       159030 :     MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
      61       159030 :                OptionsCont::getOptions().getBool("mapmatch.taz"),
      62        79515 :                OptionsCont::getOptions().getFloat("mapmatch.distance"),
      63              :                MsgHandler::getErrorInstance()),
      64        79515 :     myActiveRouteRepeat(0),
      65        79515 :     myActiveRoutePeriod(0),
      66        79515 :     myActiveRoutePermanent(false),
      67        79515 :     myActiveType(ObjectTypeEnum::UNDEFINED),
      68        79515 :     myHaveVia(false),
      69        79515 :     myActiveTransportablePlan(nullptr),
      70        79515 :     myAddVehiclesDirectly(addVehiclesDirectly),
      71        79515 :     myCurrentVTypeDistribution(nullptr),
      72        79515 :     myCurrentRouteDistribution(nullptr),
      73        79515 :     myAmLoadingState(false),
      74        79515 :     myScaleSuffix(OptionsCont::getOptions().getString("scale-suffix")),
      75        79515 :     myReplayRerouting(OptionsCont::getOptions().getBool("replay-rerouting")),
      76       353419 :     myStartTriggeredInFlow(false) {
      77        79515 :     myActiveRoute.reserve(100);
      78        79515 : }
      79              : 
      80              : 
      81       193072 : MSRouteHandler::~MSRouteHandler() {}
      82              : 
      83              : 
      84              : void
      85          751 : MSRouteHandler::deleteActivePlanAndVehicleParameter() {
      86          751 :     if (myActiveTransportablePlan != nullptr) {
      87          470 :         for (MSStage* const s : *myActiveTransportablePlan) {
      88          283 :             delete s;
      89              :         }
      90          187 :         delete myActiveTransportablePlan;
      91              :     }
      92          751 :     delete myVehicleParameter;
      93          751 :     resetActivePlanAndVehicleParameter();
      94          751 : }
      95              : 
      96              : 
      97              : void
      98        46495 : MSRouteHandler::resetActivePlanAndVehicleParameter() {
      99        46495 :     myVehicleParameter = nullptr;
     100        46495 :     myActiveTransportablePlan = nullptr;
     101        46495 :     myActiveType = ObjectTypeEnum::UNDEFINED;
     102        46495 : }
     103              : 
     104              : 
     105              : void
     106        78852 : MSRouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs) {
     107        78852 :     const std::string element = toString(tag);
     108              :     myActiveRoute.clear();
     109        78852 :     bool useTaz = OptionsCont::getOptions().getBool("with-taz");
     110        78852 :     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        78852 :     bool ok = true;
     115        78900 :     const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
     116              :     SUMOVehicleClass vClass = SVC_PASSENGER;
     117        78852 :     auto& vc = MSNet::getInstance()->getVehicleControl();
     118        78852 :     if (!vc.getVTypeDistribution(myVehicleParameter->vtypeid)) {
     119        78010 :         MSVehicleType* const type = vc.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     120        78010 :         if (type != nullptr) {
     121        78010 :             vClass = type->getParameter().vehicleClass;
     122              :         }
     123              :     }
     124              :     // from-attributes
     125        97242 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_FROM) && !attrs.hasAttribute(SUMO_ATTR_FROMXY) && !attrs.hasAttribute(SUMO_ATTR_FROMLONLAT))) &&
     126        35482 :             (attrs.hasAttribute(SUMO_ATTR_FROM_TAZ) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION))) {
     127         2298 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION);
     128         3596 :         const std::string tazType = useJunction ? "junction" : "taz";
     129         3596 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM_TAZ, myVehicleParameter->id.c_str(), ok);
     130         2326 :         const MSEdge* fromTaz = MSEdge::dictionary(tazID + "-source");
     131         2298 :         if (fromTaz == nullptr) {
     132           21 :             throw ProcessError("Source " + tazType + " '" + tazID + "' not known " + rid + "!"
     133           63 :                                + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
     134         2277 :         } else if (fromTaz->getNumSuccessors() == 0 && tag != SUMO_TAG_PERSON) {
     135           21 :             throw ProcessError("Source " + tazType + " '" + tazID + "' has no outgoing edges " + rid + "!");
     136              :         } else {
     137         2270 :             myActiveRoute.push_back(fromTaz);
     138              :         }
     139        76554 :     } 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        76379 :     } 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       152750 :         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
     153        76351 :                                myActiveRoute, rid);
     154              :     }
     155        78824 :     if (!ok) {
     156           14 :         throw ProcessError();
     157              :     }
     158              : 
     159              :     // via-attributes
     160        78810 :     if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
     161        69158 :         myInsertStopEdgesAt = (int)myActiveRoute.size();
     162              :     }
     163              :     ConstMSEdgeVector viaEdges;
     164        78810 :     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        78754 :     } 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        78747 :     } 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       157460 :         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok),
     179              :                                viaEdges, rid);
     180              :     }
     181        78804 :     if (!viaEdges.empty()) {
     182          906 :         myHaveVia = true;
     183              :     }
     184        84959 :     for (const MSEdge* e : viaEdges) {
     185         6155 :         myActiveRoute.push_back(e);
     186         6155 :         myVehicleParameter->via.push_back(e->getID());
     187              :     }
     188              : 
     189              :     // to-attributes
     190        98965 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_TO) && !attrs.hasAttribute(SUMO_ATTR_TOXY) && !attrs.hasAttribute(SUMO_ATTR_TOLONLAT))) &&
     191        38624 :             (attrs.hasAttribute(SUMO_ATTR_TO_TAZ) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION))) {
     192         3101 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION);
     193         4799 :         const std::string tazType = useJunction ? "junction" : "taz";
     194         4799 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO_TAZ, myVehicleParameter->id.c_str(), ok, true);
     195         3101 :         const MSEdge* toTaz = MSEdge::dictionary(tazID + "-sink");
     196         3101 :         if (toTaz == nullptr) {
     197            0 :             throw ProcessError("Sink " + tazType + " '" + tazID + "' not known " + rid + "!"
     198            0 :                                + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
     199         3101 :         } else if (toTaz->getNumPredecessors() == 0 && tag != SUMO_TAG_PERSON) {
     200            0 :             throw ProcessError("Sink " + tazType + " '" + tazID + "' has no incoming edges " + rid + "!");
     201              :         } else {
     202         3101 :             myActiveRoute.push_back(toTaz);
     203              :         }
     204        75703 :     } 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        75528 :     } 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       150986 :         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true),
     218        75493 :                                myActiveRoute, rid);
     219              :     }
     220        78804 :     myActiveRouteID = "!" + myVehicleParameter->id;
     221        78804 :     if (myVehicleParameter->routeid == "") {
     222              :         myVehicleParameter->routeid = myActiveRouteID;
     223              :     }
     224       157614 : }
     225              : 
     226              : 
     227              : void
     228     11036014 : MSRouteHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
     229              :     try {
     230        53268 :         if (myActiveTransportablePlan != nullptr && myActiveTransportablePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED
     231     11036952 :                 && 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     11036014 :         if (myVehicleParameter == nullptr) {
     236     10819952 :             myActiveTypeName = toString((SumoXMLTag)element);
     237     10819952 :             myHaveVia = false;
     238              :         }
     239     11036014 :         SUMORouteHandler::myStartElement(element, attrs);
     240     11035681 :         switch (element) {
     241         1806 :             case SUMO_TAG_PERSONFLOW:
     242         1806 :                 addTransportable(attrs, true);
     243              :                 break;
     244          313 :             case SUMO_TAG_CONTAINERFLOW:
     245          313 :                 addTransportable(attrs, false);
     246              :                 break;
     247        19575 :             case SUMO_TAG_FLOW:
     248        19575 :                 if (myVehicleParameter) {
     249        19575 :                     parseFromViaTo((SumoXMLTag)element, attrs);
     250              :                 }
     251              :                 break;
     252        50455 :             case SUMO_TAG_TRIP:
     253        50455 :                 parseFromViaTo((SumoXMLTag)element, attrs);
     254              :                 break;
     255              :             default:
     256              :                 break;
     257              :         }
     258          374 :     } catch (ProcessError&) {
     259          374 :         deleteActivePlanAndVehicleParameter();
     260          374 :         throw;
     261          374 :     }
     262     11035640 : }
     263              : 
     264              : 
     265              : void
     266          527 : MSRouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
     267          527 :     bool ok = true;
     268          527 :     myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     269          527 :     if (ok) {
     270          527 :         myCurrentVTypeDistribution = new RandomDistributor<MSVehicleType*>();
     271          527 :         if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
     272              :             std::vector<double> probs;
     273          106 :             if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
     274           87 :                 StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentVTypeDistributionID.c_str(), ok));
     275          273 :                 while (st.hasNext()) {
     276          372 :                     probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
     277              :                 }
     278           87 :             }
     279          106 :             const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
     280          106 :             StringTokenizer st(vTypes);
     281          106 :             int probIndex = 0;
     282          330 :             while (st.hasNext()) {
     283          224 :                 const std::string& vtypeID = st.next();
     284          224 :                 const RandomDistributor<MSVehicleType*>* const dist = MSNet::getInstance()->getVehicleControl().getVTypeDistribution(vtypeID);
     285          224 :                 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          210 :                     MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(vtypeID, &myParsingRNG);
     294          210 :                     if (type == nullptr) {
     295            0 :                         throw ProcessError("Unknown vtype '" + vtypeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
     296              :                     }
     297          210 :                     const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : type->getDefaultProbability());
     298          210 :                     myCurrentVTypeDistribution->add(type, prob);
     299              :                 }
     300          224 :                 probIndex++;
     301              :             }
     302          106 :             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          212 :         }
     307              :     }
     308          527 : }
     309              : 
     310              : 
     311              : void
     312          527 : MSRouteHandler::closeVehicleTypeDistribution() {
     313          527 :     if (myCurrentVTypeDistribution != nullptr) {
     314          527 :         if (MSGlobals::gStateLoaded && MSNet::getInstance()->getVehicleControl().hasVTypeDistribution(myCurrentVTypeDistributionID)) {
     315           28 :             myCurrentVTypeDistribution = nullptr;
     316              :             delete myCurrentVTypeDistribution;
     317           28 :             return;
     318              :         }
     319          499 :         if (myCurrentVTypeDistribution->getOverallProb() == 0) {
     320            0 :             delete myCurrentVTypeDistribution;
     321            0 :             throw ProcessError(TLF("Vehicle type distribution '%' is empty.", myCurrentVTypeDistributionID));
     322              :         }
     323          499 :         if (!MSNet::getInstance()->getVehicleControl().addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
     324            0 :             delete myCurrentVTypeDistribution;
     325            0 :             throw ProcessError(TLF("Another vehicle type (or distribution) with the id '%' exists.", myCurrentVTypeDistributionID));
     326              :         }
     327          499 :         myCurrentVTypeDistribution = nullptr;
     328              :     }
     329              : }
     330              : 
     331              : 
     332              : void
     333       152475 : MSRouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
     334              :     myActiveRoute.clear();
     335       152475 :     myInsertStopEdgesAt = -1;
     336              :     // check whether the id is really necessary
     337              :     std::string rid;
     338       152475 :     if (myCurrentRouteDistribution != nullptr) {
     339         6692 :         myActiveRouteID = myCurrentRouteDistributionID + "#" + toString(myCurrentRouteDistribution->getProbs().size()); // !!! document this
     340        10038 :         rid =  "distribution '" + myCurrentRouteDistributionID + "'";
     341       149129 :     } else if (myVehicleParameter != nullptr) {
     342              :         // ok, a vehicle is wrapping the route,
     343              :         //  we may use this vehicle's id as default
     344       111302 :         myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
     345       111302 :         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     346           24 :             WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
     347              :         }
     348              :     } else {
     349        37827 :         bool ok = true;
     350        37827 :         myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok, false);
     351        37827 :         if (!ok) {
     352            0 :             return;
     353              :         }
     354       113481 :         rid = "'" + myActiveRouteID + "'";
     355              :     }
     356       152475 :     if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
     357       342630 :         rid =  "for vehicle '" + myVehicleParameter->id + "'";
     358              :     }
     359       152475 :     bool ok = true;
     360       152475 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     361       304746 :         MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
     362              :     }
     363       304874 :     myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
     364       152499 :     if (myActiveRouteRefID != "" && MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG) == nullptr) {
     365           42 :         throw ProcessError(TLF("Invalid reference to route '%' in route %.", myActiveRouteRefID, rid));
     366              :     }
     367       152423 :     myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
     368       152423 :     myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
     369       152423 :     myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
     370       152423 :     myActiveRouteReplacedAtTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_REPLACED_AT_TIME, myActiveRouteID.c_str(), ok, -1);
     371       152423 :     myActiveRouteReplacedIndex = attrs.getOpt<int>(SUMO_ATTR_REPLACED_ON_INDEX, myActiveRouteID.c_str(), ok, 0);
     372       152423 :     myActiveRoutePeriod = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CYCLETIME, myActiveRouteID.c_str(), ok,
     373              :                           // handle obsolete attribute name
     374              :                           attrs.getOptSUMOTimeReporting(SUMO_ATTR_PERIOD, myActiveRouteID.c_str(), ok, 0));
     375       152423 :     myActiveRoutePermanent = attrs.getOpt<bool>(SUMO_ATTR_STATE, myActiveRouteID.c_str(), ok, false);
     376       152423 :     if (attrs.hasAttribute(SUMO_ATTR_PERIOD)) {
     377            0 :         WRITE_WARNING(TL("Attribute 'period' is deprecated for route. Use 'cycleTime' instead."));
     378              :     }
     379       152423 :     myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
     380       152423 :     if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
     381           52 :         WRITE_WARNING(TLF("Invalid cost for route '%'.", myActiveRouteID));
     382              :     }
     383              : }
     384              : 
     385              : 
     386              : void
     387        10751 : MSRouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
     388              :     // Currently unused
     389        10751 : }
     390              : 
     391              : 
     392              : void
     393         8824 : MSRouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
     394              :     // Currently unused
     395         8824 : }
     396              : 
     397              : 
     398              : void
     399        50455 : MSRouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
     400              :     // Currently unsued
     401        50455 : }
     402              : 
     403              : 
     404              : void
     405       209422 : MSRouteHandler::closeRoute(const bool mayBeDisconnected) {
     406       209422 :     std::string type = "vehicle";
     407       209422 :     if (mayBeDisconnected) {
     408        56999 :         if (myVehicleParameter->repetitionNumber >= 0) {
     409              :             type = "flow";
     410              :         } else {
     411              :             type = "trip";
     412              :         }
     413              :     }
     414              : 
     415              :     try {
     416       209422 :         const bool mustReroute = myActiveRoute.size() == 0 && myActiveRouteStops.size() != 0;
     417              :         if (mustReroute) {
     418              :             // implicit route from stops
     419          105 :             for (const SUMOVehicleParameter::Stop& stop : myActiveRouteStops) {
     420           84 :                 myActiveRoute.push_back(MSEdge::dictionary(stop.edge));
     421              :             }
     422              :         }
     423       209422 :         if (myActiveRoute.size() == 0) {
     424           60 :             delete myActiveRouteColor;
     425           60 :             myActiveRouteColor = nullptr;
     426           60 :             if (myActiveRouteRefID != "" && myCurrentRouteDistribution != nullptr) {
     427           48 :                 ConstMSRoutePtr route = MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG);
     428           48 :                 if (route != nullptr) {
     429           96 :                     myCurrentRouteDistribution->add(route, myActiveRouteProbability);
     430              :                 }
     431           48 :                 myActiveRouteID = "";
     432              :                 myActiveRouteRefID = "";
     433              :                 return;
     434              :             }
     435           12 :             if (myVehicleParameter != nullptr) {
     436           18 :                 throw ProcessError("The route for " + type + " '" + myVehicleParameter->id + "' has no edges.");
     437              :             } else {
     438           18 :                 throw ProcessError(TLF("Route '%' has no edges.", myActiveRouteID));
     439              :             }
     440              :         }
     441       209362 :         if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
     442            0 :             throw ProcessError("The routing information for " + type + " '" + myVehicleParameter->id + "' is insufficient.");
     443              :         }
     444       209362 :         if (myActiveRouteRepeat > 0) {
     445              :             // duplicate route
     446          972 :             ConstMSEdgeVector tmpEdges = myActiveRoute;
     447          972 :             auto tmpStops = myActiveRouteStops;
     448              : 
     449          972 :             if (MSGlobals::gCheckRoutes) {
     450              :                 SUMOVehicleClass vClass = SVC_IGNORING;
     451          972 :                 std::string errSuffix = ".";
     452          972 :                 if (myVehicleParameter != nullptr) {
     453          742 :                     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
     454          742 :                     MSVehicleType* vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     455          742 :                     if (vtype != nullptr) {
     456              :                         vClass = vtype->getVehicleClass();
     457         2976 :                         errSuffix = TLF(" for vehicle '%' with vClass %.", myVehicleParameter->id, getVehicleClassNames(vClass));
     458              :                     }
     459              :                 }
     460          972 :                 if (myActiveRoute.size() > 0 && !myActiveRoute.back()->isConnectedTo(*myActiveRoute.front(), vClass)) {
     461           16 :                     if (tmpStops.size() == 0 || tmpStops.back().jump < 0) {
     462           32 :                         throw ProcessError(TLF("Disconnected route '%' when repeating. Last edge '%' is not connected to first edge '%'%",
     463           24 :                                                myActiveRouteID, myActiveRoute.back()->getID(), myActiveRoute.front()->getID(), errSuffix));
     464              :                     }
     465              :                 }
     466              :             }
     467        10331 :             for (int i = 0; i < myActiveRouteRepeat; i++) {
     468         9375 :                 myActiveRoute.insert(myActiveRoute.begin(), tmpEdges.begin(), tmpEdges.end());
     469        11231 :                 for (SUMOVehicleParameter::Stop stop : tmpStops) {
     470         1864 :                     if (stop.until > 0) {
     471         1312 :                         if (myActiveRoutePeriod <= 0) {
     472            8 :                             const std::string description = myVehicleParameter != nullptr
     473           16 :                                                             ?  "for " + type + " '" + myVehicleParameter->id + "'"
     474           24 :                                                             :  "'" + myActiveRouteID + "'";
     475           24 :                             throw ProcessError(TLF("Cannot repeat stops with 'until' in route % because no cycleTime is defined.", description));
     476              :                         }
     477         1304 :                         stop.until += myActiveRoutePeriod * (i + 1);
     478              :                     }
     479         1856 :                     if (stop.arrival > 0) {
     480           25 :                         if (myActiveRoutePeriod <= 0) {
     481            0 :                             const std::string description = myVehicleParameter != nullptr
     482            0 :                                                             ?  "for " + type + " '" + myVehicleParameter->id + "'"
     483            0 :                                                             :  "'" + myActiveRouteID + "'";
     484            0 :                             throw ProcessError(TLF("Cannot repeat stops with 'arrival' in route % because no cycleTime is defined.", description));
     485              :                         }
     486           25 :                         stop.arrival += myActiveRoutePeriod * (i + 1);
     487              :                     }
     488         1856 :                     stop.index = STOP_INDEX_REPEAT;
     489         1856 :                     myActiveRouteStops.push_back(stop);
     490         1864 :                 }
     491              :             }
     492          956 :             if (myActiveRouteStops.size() > 0) {
     493              :                 // never jump on the last stop of a repeating route
     494          132 :                 myActiveRouteStops.back().jump = -1;
     495              :             }
     496          988 :         }
     497       209346 :         MSRoute* const route = new MSRoute(myActiveRouteID, myActiveRoute,
     498       418692 :                                            myAmLoadingState ? myActiveRoutePermanent : myVehicleParameter == nullptr,
     499       209346 :                                            myActiveRouteColor, myActiveRouteStops,
     500       209346 :                                            myActiveRouteReplacedAtTime, myActiveRouteReplacedIndex);
     501       209346 :         route->setPeriod(myActiveRoutePeriod);
     502       209346 :         route->setCosts(myCurrentCosts);
     503              :         route->setReroute(mustReroute);
     504              :         myActiveRoute.clear();
     505              :         ConstMSRoutePtr constRoute = std::shared_ptr<const MSRoute>(route);
     506       418692 :         if (!MSRoute::dictionary(myActiveRouteID, constRoute)) {
     507          420 :             if (!MSGlobals::gStateLoaded) {
     508           26 :                 if (myVehicleParameter != nullptr) {
     509           20 :                     if (MSNet::getInstance()->getVehicleControl().getVehicle(myVehicleParameter->id) == nullptr) {
     510           18 :                         throw ProcessError("Another route for " + type + " '" + myVehicleParameter->id + "' exists.");
     511              :                     } else {
     512           42 :                         throw ProcessError(TLF("A vehicle with id '%' already exists.", myVehicleParameter->id));
     513              :                     }
     514              :                 } else {
     515           18 :                     throw ProcessError(TLF("Another route (or distribution) with the id '%' exists.", myActiveRouteID));
     516              :                 }
     517              :             }
     518              :         } else {
     519       208926 :             if (myCurrentRouteDistribution != nullptr) {
     520         6586 :                 myCurrentRouteDistribution->add(constRoute, myActiveRouteProbability);
     521              :             }
     522              :         }
     523              :         myActiveRouteID = "";
     524       209320 :         myActiveRouteColor = nullptr;
     525              :         myActiveRouteStops.clear();
     526       209320 :         myActiveRouteRepeat = 0;
     527           54 :     } catch (ProcessError&) {
     528           54 :         deleteActivePlanAndVehicleParameter();
     529           54 :         throw;
     530           54 :     }
     531              : }
     532              : 
     533              : 
     534              : void
     535         1285 : MSRouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
     536              :     // check whether the id is really necessary
     537         1285 :     if (myVehicleParameter != nullptr) {
     538              :         // ok, a vehicle is wrapping the route,
     539              :         //  we may use this vehicle's id as default
     540         1099 :         myCurrentRouteDistributionID = "!" + myVehicleParameter->id; // !!! document this
     541              :         // we have to record this or we cannot remove the distribution later
     542         1099 :         myVehicleParameter->routeid = myCurrentRouteDistributionID;
     543              :     } else {
     544          186 :         bool ok = true;
     545          186 :         myCurrentRouteDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     546          186 :         if (!ok) {
     547            0 :             return;
     548              :         }
     549              :     }
     550         1285 :     myCurrentRouteDistribution = new RandomDistributor<ConstMSRoutePtr>();
     551              :     std::vector<double> probs;
     552         1285 :     if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
     553           15 :         bool ok = true;
     554           15 :         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentRouteDistributionID.c_str(), ok));
     555           45 :         while (st.hasNext()) {
     556           60 :             probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
     557              :         }
     558           15 :     }
     559         1285 :     if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
     560           22 :         bool ok = true;
     561           22 :         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, myCurrentRouteDistributionID.c_str(), ok));
     562           22 :         int probIndex = 0;
     563           66 :         while (st.hasNext()) {
     564           44 :             std::string routeID = st.next();
     565           44 :             ConstMSRoutePtr route = MSRoute::dictionary(routeID, &myParsingRNG);
     566           44 :             if (route == nullptr) {
     567            0 :                 throw ProcessError("Unknown route '" + routeID + "' in distribution '" + myCurrentRouteDistributionID + "'.");
     568              :             }
     569           44 :             const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : 1.0);
     570           44 :             myCurrentRouteDistribution->add(route, prob, false);
     571           44 :             probIndex++;
     572              :         }
     573           22 :         if (probs.size() > 0 && probIndex != (int)probs.size()) {
     574            0 :             WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
     575              :                           " routes in routeDistribution '" + myCurrentRouteDistributionID + "'");
     576              :         }
     577           22 :     }
     578         1285 : }
     579              : 
     580              : 
     581              : void
     582         1271 : MSRouteHandler::closeRouteDistribution() {
     583         1271 :     if (myCurrentRouteDistribution != nullptr) {
     584         1271 :         const bool haveSameID = MSRoute::dictionary(myCurrentRouteDistributionID, &myParsingRNG) != nullptr;
     585         1271 :         if (MSGlobals::gStateLoaded && haveSameID) {
     586            4 :             delete myCurrentRouteDistribution;
     587            2 :             myCurrentRouteDistribution = nullptr;
     588            2 :             return;
     589              :         }
     590         1259 :         if (haveSameID) {
     591            0 :             delete myCurrentRouteDistribution;
     592            0 :             throw ProcessError(TLF("Another route (or distribution) with the id '%' exists.", myCurrentRouteDistributionID));
     593              :         }
     594         1269 :         if (myCurrentRouteDistribution->getOverallProb() == 0) {
     595            2 :             delete myCurrentRouteDistribution;
     596            6 :             throw ProcessError(TLF("Route distribution '%' is empty.", myCurrentRouteDistributionID));
     597              :         }
     598         1267 :         MSRoute::dictionary(myCurrentRouteDistributionID, myCurrentRouteDistribution, myVehicleParameter == nullptr);
     599         1267 :         myCurrentRouteDistribution = nullptr;
     600              :     }
     601              : }
     602              : 
     603              : 
     604              : void
     605       314495 : MSRouteHandler::closeVehicle() {
     606              :     // get nested route
     607       314495 :     const std::string embeddedRouteID = "!" + myVehicleParameter->id;
     608              :     ConstMSRoutePtr route = nullptr;
     609       314495 :     if (myReplayRerouting) {
     610         2832 :         RandomDistributor<ConstMSRoutePtr>* rDist = MSRoute::distDictionary(embeddedRouteID);
     611         2832 :         if (rDist != nullptr && rDist->getVals().size() > 0) {
     612              :             route = rDist->getVals().front();
     613              :         }
     614              :     }
     615       314495 :     if (route == nullptr) {
     616       626948 :         route = MSRoute::dictionary(embeddedRouteID, &myParsingRNG);
     617              :     }
     618       314495 :     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
     619       314495 :     if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN) {
     620              :         // let's check whether this vehicle had to depart before the simulation starts
     621       313913 :         if (!(myAddVehiclesDirectly || checkLastDepart()) || (myVehicleParameter->depart <= MSNet::getInstance()->getStateLoaderTime() && !myAmLoadingState)) {
     622         1123 :             mySkippedVehicles.insert(myVehicleParameter->id);
     623              :             return;
     624              :         }
     625              :     }
     626              : 
     627              :     // get the vehicle's type
     628              :     MSVehicleType* vtype = nullptr;
     629              : 
     630              :     try {
     631       313372 :         if (myVehicleParameter->vtypeid != "") {
     632       313372 :             vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     633       313372 :             if (vtype == nullptr) {
     634            0 :                 throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
     635              :             }
     636       313372 :             if (vtype->getVehicleClass() == SVC_PEDESTRIAN) {
     637           90 :                 WRITE_WARNINGF(TL("Vehicle type '%' with vClass=pedestrian should only be used for persons and not for vehicle '%'."), vtype->getID(), myVehicleParameter->id);
     638              :             }
     639              :         } else {
     640              :             // there should be one (at least the default one)
     641            0 :             vtype = vehControl.getVType(DEFAULT_VTYPE_ID, &myParsingRNG);
     642              :         }
     643       313372 :         if (myVehicleParameter->wasSet(VEHPARS_ROUTE_SET)) {
     644              :             // if the route id was given, prefer that one
     645       155491 :             if (route != nullptr && !myAmLoadingState) {
     646           18 :                 WRITE_WARNINGF(TL("Ignoring child element 'route' for vehicle '%' because attribute 'route' is set."), myVehicleParameter->id);
     647              :             }
     648       310982 :             route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
     649              :         }
     650       313372 :         if (route == nullptr) {
     651              :             // nothing found? -> error
     652           21 :             if (myVehicleParameter->routeid != "") {
     653           45 :                 throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
     654              :             } else {
     655           18 :                 throw ProcessError(TLF("Vehicle '%' has no route.", myVehicleParameter->id));
     656              :             }
     657              :         }
     658       313351 :         myActiveRouteID = "";
     659              : 
     660           21 :     } catch (ProcessError&) {
     661           21 :         deleteActivePlanAndVehicleParameter();
     662           21 :         throw;
     663           21 :     }
     664       313351 :     if (route->mustReroute()) {
     665           14 :         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
     666           14 :         if (myVehicleParameter->stops.size() > 0) {
     667           14 :             route = addVehicleStopsToImplicitRoute(route, false);
     668              :         }
     669              :     }
     670       313351 :     if (myVehicleParameter->departEdgeProcedure != RouteIndexDefinition::DEFAULT) {
     671          206 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
     672          416 :                 myVehicleParameter->departEdgeProcedure == RouteIndexDefinition::GIVEN &&
     673          182 :                 myVehicleParameter->departEdge >= (int)route->getEdges().size()) {
     674           14 :             throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has invalid departEdge index "
     675           35 :                                + toString(myVehicleParameter->departEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
     676              :         }
     677              :     }
     678       313344 :     if (myVehicleParameter->arrivalEdgeProcedure != RouteIndexDefinition::DEFAULT) {
     679           53 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
     680          113 :                 myVehicleParameter->arrivalEdgeProcedure == RouteIndexDefinition::GIVEN &&
     681           46 :                 myVehicleParameter->arrivalEdge >= (int)route->getEdges().size()) {
     682           14 :             throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has invalid arrivalEdge index "
     683           35 :                                + toString(myVehicleParameter->arrivalEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
     684              :         }
     685              :     }
     686              : 
     687              :     // try to build the vehicle
     688              :     SUMOVehicle* vehicle = nullptr;
     689       313337 :     if (vehControl.getVehicle(myVehicleParameter->id) == nullptr) {
     690       313098 :         MSVehicleControl::VehicleDefinitionSource source = (myAmLoadingState) ? MSVehicleControl::VehicleDefinitionSource::STATE : MSVehicleControl::VehicleDefinitionSource::ROUTEFILE;
     691              :         try {
     692       313098 :             vehicle = vehControl.buildVehicle(myVehicleParameter, route, vtype, !MSGlobals::gCheckRoutes, source, !myAmLoadingState);
     693          108 :         } catch (const ProcessError& e) {
     694          107 :             myVehicleParameter = nullptr;
     695          107 :             if (!MSGlobals::gCheckRoutes) {
     696           15 :                 WRITE_WARNING(e.what());
     697           15 :                 vehControl.deleteVehicle(vehicle, true);
     698           15 :                 myVehicleParameter = nullptr;
     699              :                 vehicle = nullptr;
     700              :                 return;
     701              :             } else {
     702           92 :                 throw;
     703              :             }
     704          107 :         }
     705       312990 :         const SUMOTime origDepart = myVehicleParameter->depart;
     706              :         // maybe we do not want this vehicle to be inserted due to scaling
     707       312990 :         int quota = myAmLoadingState ? 1 : vehControl.getQuota(vehControl.getScale() * vtype->getParameter().scale);
     708       308072 :         if (quota > 0) {
     709       312034 :             registerLastDepart();
     710       312034 :             myVehicleParameter->depart += MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
     711       312034 :             vehControl.addVehicle(myVehicleParameter->id, vehicle);
     712       312034 :             if (myReplayRerouting) {
     713         2832 :                 RandomDistributor<ConstMSRoutePtr>* rDist = MSRoute::distDictionary(embeddedRouteID);
     714         2832 :                 if (rDist != nullptr) {
     715         2759 :                     for (int i = 0; i < (int)rDist->getVals().size() - 1; i++) {
     716         1738 :                         SUMOTime replacedAt = rDist->getVals()[i]->getReplacedTime();
     717         1738 :                         auto* cmd = new Command_RouteReplacement(vehicle->getID(), rDist->getVals()[i + 1]);
     718         1738 :                         if (i == 0 && replacedAt >= 0 && replacedAt == myVehicleParameter->depart) {
     719              :                             // routing in the insertion step happens *after* insertion
     720           26 :                             MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(cmd, replacedAt);
     721              :                         } else {
     722         1712 :                             MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(cmd, replacedAt);
     723              :                         }
     724              :                     }
     725              :                 }
     726              :             }
     727              :             int offset = 0;
     728       312206 :             for (int i = 1; i < quota; i++) {
     729          172 :                 if (vehicle->getParameter().departProcedure == DepartDefinition::GIVEN || vehicle->getParameter().departProcedure == DepartDefinition::BEGIN) {
     730          156 :                     MSNet::getInstance()->getInsertionControl().add(vehicle);
     731              :                 }
     732          172 :                 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*myVehicleParameter);
     733          344 :                 newPars->id = myVehicleParameter->id + myScaleSuffix + toString(i + offset);
     734          178 :                 while (vehControl.getVehicle(newPars->id) != nullptr) {
     735            6 :                     offset += 1;
     736           12 :                     newPars->id = myVehicleParameter->id + myScaleSuffix + toString(i + offset);
     737              :                 }
     738          172 :                 newPars->depart = origDepart + MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
     739          172 :                 if (vehControl.hasVTypeDistribution(myVehicleParameter->vtypeid)) {
     740              :                     // resample type
     741           27 :                     vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     742              :                 }
     743          312 :                 vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes, source, !myAmLoadingState);
     744          172 :                 vehControl.addVehicle(newPars->id, vehicle);
     745              :             }
     746       312034 :             myVehicleParameter = nullptr;
     747              :         } else {
     748          956 :             vehControl.deleteVehicle(vehicle, true);
     749          956 :             myVehicleParameter = nullptr;
     750              :             vehicle = nullptr;
     751              :         }
     752              :     } else {
     753              :         // strange: another vehicle with the same id already exists
     754          239 :         if (!MSGlobals::gStateLoaded) {
     755              :             // and was not loaded while loading a simulation state
     756              :             // -> error
     757           12 :             std::string veh_id = myVehicleParameter->id;
     758           12 :             deleteActivePlanAndVehicleParameter();
     759           24 :             std::string scaleWarning = "";
     760           18 :             if (vehControl.getScale() * vtype->getParameter().scale > 1 && veh_id.find(myScaleSuffix) != std::string::npos) {
     761              :                 scaleWarning = "\n   (Possibly duplicate id due to using option --scale. Set option --scale-suffix to prevent this)";
     762              :             }
     763           36 :             throw ProcessError("Another vehicle with the id '" + veh_id + "' exists." + scaleWarning);
     764              :         } else {
     765              :             // ok, it seems to be loaded previously while loading a simulation state
     766              :             vehicle = nullptr;
     767              :         }
     768              :     }
     769              :     // check whether the vehicle shall be added directly to the network or
     770              :     //  shall stay in the internal buffer
     771       312034 :     if (vehicle != nullptr) {
     772       312034 :         if (vehicle->getParameter().departProcedure == DepartDefinition::GIVEN || vehicle->getParameter().departProcedure == DepartDefinition::BEGIN) {
     773       311459 :             MSNet::getInstance()->getInsertionControl().add(vehicle);
     774              :         }
     775              :     }
     776              : }
     777              : 
     778              : 
     779              : ConstMSRoutePtr
     780           14 : MSRouteHandler::addVehicleStopsToImplicitRoute(ConstMSRoutePtr route, bool isPermanent) {
     781              :     // the route was defined without edges and its current edges were
     782              :     // derived from route-stops.
     783              :     // We may need to add additional edges for the vehicle-stops
     784              :     assert(myVehicleParameter->wasSet(VEHPARS_ROUTE_SET));
     785              :     assert(route->getStops().size() > 0);
     786           14 :     ConstMSEdgeVector edges = route->getEdges();
     787           42 :     for (SUMOVehicleParameter::Stop stop : myVehicleParameter->stops) {
     788           28 :         MSEdge* stopEdge = MSEdge::dictionary(stop.edge);
     789           28 :         if (stop.index == 0) {
     790           14 :             if (edges.front() != stopEdge ||
     791            0 :                     route->getStops().front().endPos < stop.endPos) {
     792           14 :                 edges.insert(edges.begin(), stopEdge);
     793              :             }
     794           14 :         } else if (stop.index == STOP_INDEX_END) {
     795           14 :             if (edges.back() != stopEdge ||
     796            0 :                     route->getStops().back().endPos > stop.endPos) {
     797           14 :                 edges.push_back(stopEdge);
     798              :             }
     799              :         } else {
     800            0 :             WRITE_WARNINGF(TL("Could not merge vehicle stops for vehicle '%' into implicitly defined route '%'"), myVehicleParameter->id, route->getID());
     801              :         }
     802           28 :     }
     803           28 :     ConstMSRoutePtr newRoute = std::make_shared<MSRoute>("!" + myVehicleParameter->id, edges,
     804           28 :                                isPermanent, new RGBColor(route->getColor()), route->getStops());
     805           28 :     if (!MSRoute::dictionary(newRoute->getID(), newRoute)) {
     806            0 :         throw ProcessError("Could not adapt implicit route for " + std::string(isPermanent ? "flow" : "vehicle") + "  '" + myVehicleParameter->id + "'");
     807              :     }
     808           14 :     return newRoute;
     809           14 : }
     810              : 
     811              : 
     812              : void
     813        43722 : MSRouteHandler::closeTransportable() {
     814              :     try {
     815        43722 :         if (myActiveTransportablePlan->size() == 0) {
     816           12 :             std::string error = myActiveTypeName + " '" + myVehicleParameter->id + "' has no plan.";
     817            6 :             error[0] = (char)::toupper((char)error[0]);
     818            6 :             throw ProcessError(error);
     819              :         }
     820              :         // let's check whether this transportable had to depart before the simulation starts
     821        19577 :         if (!(myAddVehiclesDirectly || checkLastDepart())
     822        63293 :                 || (myVehicleParameter->depart <= MSNet::getInstance()->getStateLoaderTime() && !myAmLoadingState)) {
     823           31 :             deleteActivePlanAndVehicleParameter();
     824              :             return;
     825              :         }
     826              :         // type existence has been checked on opening
     827        43685 :         MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     828        43685 :         if (myActiveType == ObjectTypeEnum::PERSON
     829        41626 :                 && type->getVehicleClass() != SVC_PEDESTRIAN
     830        43741 :                 && !type->getParameter().wasSet(VTYPEPARS_VEHICLECLASS_SET)) {
     831           52 :             WRITE_WARNINGF(TL("Person '%' receives type '%' which implicitly uses unsuitable vClass '%'."), myVehicleParameter->id, type->getID(), toString(type->getVehicleClass()));
     832              :         }
     833        43685 :         int created = addFlowTransportable(myVehicleParameter->depart, type, myVehicleParameter->id, -1);
     834        43657 :         registerLastDepart();
     835        43657 :         if (created > 0) {
     836        43633 :             resetActivePlanAndVehicleParameter();
     837              :         } else {
     838           24 :             deleteActivePlanAndVehicleParameter();
     839              :         }
     840           34 :     } catch (ProcessError&) {
     841           34 :         deleteActivePlanAndVehicleParameter();
     842           34 :         throw;
     843           34 :     }
     844              : }
     845              : 
     846              : 
     847              : void
     848        41663 : MSRouteHandler::closePerson() {
     849        41663 :     closeTransportable();
     850        41637 : }
     851              : 
     852              : 
     853              : void
     854         2059 : MSRouteHandler::closeContainer() {
     855         2059 :     closeTransportable();
     856         2051 : }
     857              : 
     858              : 
     859              : void
     860         1796 : MSRouteHandler::closePersonFlow() {
     861         1796 :     closeTransportableFlow();
     862         1784 : }
     863              : 
     864              : 
     865              : void
     866          311 : MSRouteHandler::closeContainerFlow() {
     867          311 :     closeTransportableFlow();
     868          311 : }
     869              : 
     870              : 
     871              : void
     872         2107 : MSRouteHandler::closeTransportableFlow() {
     873              :     try {
     874         2107 :         const std::string fid = myVehicleParameter->id;
     875         2107 :         if (myActiveTransportablePlan->size() == 0) {
     876            0 :             throw ProcessError(myActiveTypeName + "Flow '" + fid + "' has no plan.");
     877              :         }
     878              :         // let's check whether this transportable (person/container) had to depart before the simulation starts
     879         2107 :         if (!(myAddVehiclesDirectly || checkLastDepart())
     880         4214 :                 || (myVehicleParameter->depart <= MSNet::getInstance()->getStateLoaderTime() && !myAmLoadingState)) {
     881            0 :             deleteActivePlanAndVehicleParameter();
     882              :             return;
     883              :         }
     884              :         // instantiate all persons/containers of this flow
     885              :         int i = 0;
     886         2107 :         registerLastDepart();
     887         2107 :         std::string baseID = myVehicleParameter->id;
     888         2107 :         if (myVehicleParameter->repetitionProbability > 0) {
     889          207 :             if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
     890            0 :                 throw ProcessError("probabilistic " + myActiveTypeName + "Flow '" + fid + "' must specify end time");
     891              :             } else {
     892       444310 :                 for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
     893       444103 :                     if (RandHelper::rand(&myParsingRNG) < myVehicleParameter->repetitionProbability) {
     894              :                         // type existence has been checked on opening
     895        34795 :                         MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     896        34795 :                         addFlowTransportable(t, type, baseID, i++);
     897              :                     }
     898              :                 }
     899              :             }
     900              :         } else {
     901         1900 :             SUMOTime depart = myVehicleParameter->depart;
     902         1900 :             if (myVehicleParameter->repetitionOffset < 0) {
     903              :                 // poisson: randomize first depart
     904           99 :                 myVehicleParameter->incrementFlow(1, &myParsingRNG);
     905              :             }
     906       451354 :             for (; i < myVehicleParameter->repetitionNumber && (myVehicleParameter->repetitionNumber != std::numeric_limits<long long int>::max()
     907        81858 :                     || depart + myVehicleParameter->repetitionTotalOffset <= myVehicleParameter->repetitionEnd); i++) {
     908              :                 // type existence has been checked on opening
     909       449466 :                 MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
     910       449466 :                 addFlowTransportable(depart + myVehicleParameter->repetitionTotalOffset, type, baseID, i);
     911       449454 :                 myVehicleParameter->incrementFlow(1, &myParsingRNG);
     912              :             }
     913              :         }
     914         2095 :         resetActivePlanAndVehicleParameter();
     915           12 :     } catch (ProcessError&) {
     916           12 :         deleteActivePlanAndVehicleParameter();
     917           12 :         throw;
     918           12 :     }
     919         2095 :     myStartTriggeredInFlow = false;
     920              : }
     921              : 
     922              : 
     923              : int
     924       527946 : MSRouteHandler::addFlowTransportable(SUMOTime depart, MSVehicleType* type, const std::string& baseID, int i) {
     925              :     try {
     926              :         int numCreated = 0;
     927       527946 :         MSNet* const net = MSNet::getInstance();
     928       527946 :         MSTransportableControl& tc = myActiveType == ObjectTypeEnum::PERSON ? net->getPersonControl() : net->getContainerControl();
     929       527922 :         const MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     930              :         //MSTransportableControl& pc = net->getPersonControl();
     931       527922 :         const int quota = vc.getQuota(vc.getScale() * type->getParameter().scale, tc.getLoadedNumber());
     932       527922 :         if (quota == 0) {
     933              :             tc.addDiscarded();
     934              :         }
     935      1056348 :         for (int j = 0; j < quota; j++) {
     936       528442 :             if (i > 0 || j > 0) {
     937              :                 // copy parameter and plan because the transportable takes over responsibility
     938       482698 :                 SUMOVehicleParameter* copyParam = new SUMOVehicleParameter();
     939       482698 :                 *copyParam = *myVehicleParameter;
     940       482698 :                 myVehicleParameter = copyParam;
     941       482698 :                 MSTransportable::MSTransportablePlan* copyPlan = new MSTransportable::MSTransportablePlan();
     942              :                 MSStage* lastStage = nullptr;
     943      1554555 :                 for (MSStage* const s : *myActiveTransportablePlan) {
     944      1071857 :                     copyPlan->push_back(s->clone());
     945      1071857 :                     if (lastStage != nullptr && s->getStageType() == MSStageType::WALKING && lastStage->getDestinationStop() != nullptr) {
     946            0 :                         MSStageMoving* walk = static_cast<MSStageMoving*>(copyPlan->back());
     947            0 :                         walk->setDepartPos(lastStage->getDestinationStop()->getAccessPos(walk->getEdge(), &myParsingRNG));
     948              :                     }
     949              :                     lastStage = s;
     950              :                 }
     951       482698 :                 myActiveTransportablePlan = copyPlan;
     952       482698 :                 if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
     953          198 :                     const double initialDepartPos = RandHelper::rand(myActiveTransportablePlan->front()->getDestination()->getLength(), &myParsingRNG);
     954          198 :                     myActiveTransportablePlan->front()->setArrivalPos(initialDepartPos);
     955              :                 }
     956              :             }
     957       528442 :             myVehicleParameter->id = (baseID
     958      2025958 :                                       + (i >= 0 ? "." + toString(i) : "")
     959      1585326 :                                       + (j > 0 ? "." + toString(j) : ""));
     960       528442 :             if (MSGlobals::gStateLoaded && tc.get(myVehicleParameter->id) != nullptr) {
     961            0 :                 return numCreated;
     962              :             }
     963       528442 :             myVehicleParameter->depart = depart += net->getInsertionControl().computeRandomDepartOffset();
     964       528442 :             MSTransportable* transportable = myActiveType == ObjectTypeEnum::PERSON ?
     965       331043 :                                              tc.buildPerson(myVehicleParameter, type, myActiveTransportablePlan, &myParsingRNG) :
     966       197399 :                                              tc.buildContainer(myVehicleParameter, type, myActiveTransportablePlan);
     967       528442 :             numCreated++;
     968       528442 :             if (!tc.add(transportable)) {
     969           16 :                 std::string error = "Another " + myActiveTypeName + " with the id '" + myVehicleParameter->id + "' exists.";
     970           16 :                 delete transportable;
     971           16 :                 resetActivePlanAndVehicleParameter();
     972           16 :                 throw ProcessError(error);
     973       337872 :             } else if ((net->hasPersons() && net->getPersonControl().get(myVehicleParameter->id) != nullptr)
     974       859469 :                        && (net->hasContainers() && net->getContainerControl().get(myVehicleParameter->id) != nullptr)) {
     975           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);
     976              :             }
     977              :         }
     978       527906 :         return numCreated;
     979           40 :     } catch (ProcessError&) {
     980           40 :         deleteActivePlanAndVehicleParameter();
     981           40 :         throw;
     982           40 :     }
     983              : }
     984              : 
     985              : 
     986              : void
     987        62191 : MSRouteHandler::closeVType() {
     988              :     MSVehicleType* vehType = nullptr;
     989        62191 :     if (myCurrentVTypeRef.empty()) {
     990        62109 :         vehType = MSVehicleType::build(*myCurrentVType, getFileName());
     991        62105 :         vehType->check();
     992        62105 :         if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
     993              :             const std::string id = vehType->getID();
     994          194 :             delete vehType;
     995          194 :             if (!MSGlobals::gStateLoaded) {
     996           60 :                 throw ProcessError(TLF("Another vehicle type (or distribution) with the id '%' exists.", id));
     997              :             }
     998              :         }
     999              :     }
    1000        62167 :     if (myCurrentVTypeDistribution != nullptr) {
    1001         1148 :         if (!myCurrentVTypeRef.empty()) {
    1002           82 :             const RandomDistributor<MSVehicleType*>* const dist = MSNet::getInstance()->getVehicleControl().getVTypeDistribution(myCurrentVTypeRef);
    1003           82 :             if (dist != nullptr) {
    1004           32 :                 const double distProb = (myCurrentVTypeProbability >= 0 ? myCurrentVTypeProbability : 1) / dist->getOverallProb();
    1005              :                 std::vector<double>::const_iterator probIt = dist->getProbs().begin();
    1006           48 :                 for (MSVehicleType* const type : dist->getVals()) {
    1007           32 :                     myCurrentVTypeDistribution->add(type, distProb * *probIt);
    1008              :                     probIt++;
    1009              :                 }
    1010              :                 return;
    1011              :             } else {
    1012           66 :                 vehType = MSNet::getInstance()->getVehicleControl().getVType(myCurrentVTypeRef);
    1013              :             }
    1014              :         }
    1015         1132 :         double probability = myCurrentVTypeProbability >= 0 ? myCurrentVTypeProbability : vehType->getDefaultProbability();
    1016         1132 :         myCurrentVTypeDistribution->add(vehType, probability);
    1017              :     }
    1018              : }
    1019              : 
    1020              : 
    1021              : void
    1022        19575 : MSRouteHandler::closeFlow() {
    1023        19575 :     myInsertStopEdgesAt = -1;
    1024        19575 :     if (myVehicleParameter->repetitionNumber == 0) {
    1025            0 :         delete myVehicleParameter;
    1026            0 :         myVehicleParameter = nullptr;
    1027           15 :         return;
    1028              :     }
    1029              :     // let's check whether vehicles had to depart before the simulation starts
    1030        19575 :     myVehicleParameter->repetitionsDone = 0;
    1031        19575 :     if (myVehicleParameter->repetitionProbability < 0) {
    1032        15465 :         const SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
    1033        16798 :         while (myVehicleParameter->repetitionTotalOffset < offsetToBegin) {
    1034         1348 :             myVehicleParameter->incrementFlow(1, &myParsingRNG);
    1035         1348 :             if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
    1036           15 :                 delete myVehicleParameter;
    1037           15 :                 myVehicleParameter = nullptr;
    1038           15 :                 return;
    1039              :             }
    1040              :         }
    1041              :     }
    1042        19560 :     if (MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG) == nullptr) {
    1043            0 :         throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
    1044              :     }
    1045        30302 :     if (myVehicleParameter->routeid[0] == '!' && MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
    1046         6585 :         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1047         6585 :         closeRoute(true);
    1048              :     }
    1049        19560 :     ConstMSRoutePtr route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
    1050        19560 :     if (route == nullptr) {
    1051            0 :         throw ProcessError("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
    1052              :     }
    1053        19560 :     if (route->mustReroute()) {
    1054           14 :         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1055           14 :         if (myVehicleParameter->stops.size() > 0) {
    1056           21 :             route = addVehicleStopsToImplicitRoute(route, true);
    1057            7 :             myVehicleParameter->routeid = route->getID();
    1058              :         }
    1059              :     }
    1060        19560 :     if (myVehicleParameter->departEdgeProcedure != RouteIndexDefinition::DEFAULT) {
    1061           20 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
    1062           49 :                 myVehicleParameter->departEdgeProcedure == RouteIndexDefinition::GIVEN &&
    1063            9 :                 myVehicleParameter->departEdge >= (int)route->getEdges().size()) {
    1064           14 :             throw ProcessError("Flow '" + myVehicleParameter->id + "' has invalid departEdge index "
    1065           35 :                                + toString(myVehicleParameter->departEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
    1066              :         }
    1067              :     }
    1068        19553 :     if (myVehicleParameter->arrivalEdgeProcedure != RouteIndexDefinition::DEFAULT) {
    1069            9 :         if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
    1070           18 :                 myVehicleParameter->arrivalEdgeProcedure == RouteIndexDefinition::GIVEN &&
    1071            2 :                 myVehicleParameter->arrivalEdge >= (int)route->getEdges().size()) {
    1072            0 :             throw ProcessError("Flow '" + myVehicleParameter->id + "' has invalid arrivalEdge index "
    1073            0 :                                + toString(myVehicleParameter->arrivalEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
    1074              :         }
    1075              :     }
    1076        19553 :     myActiveRouteID = "";
    1077              : 
    1078              :     // check whether the vehicle shall be added directly to the network or
    1079              :     //  shall stay in the internal buffer
    1080        19553 :     if (myAddVehiclesDirectly || checkLastDepart()) {
    1081        19553 :         if (MSNet::getInstance()->getInsertionControl().addFlow(myVehicleParameter)) {
    1082        19514 :             registerLastDepart();
    1083              :         } else {
    1084           39 :             if (MSGlobals::gStateLoaded) {
    1085           39 :                 delete myVehicleParameter;
    1086              :             } else {
    1087            0 :                 throw ProcessError(TLF("Another flow with the id '%' exists.", myVehicleParameter->id));
    1088              :             }
    1089              :         }
    1090              :     }
    1091        19553 :     myVehicleParameter = nullptr;
    1092              : }
    1093              : 
    1094              : 
    1095              : void
    1096        50414 : MSRouteHandler::closeTrip() {
    1097        50414 :     myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1098        50414 :     closeRoute(true);
    1099        50414 :     closeVehicle();
    1100        50397 : }
    1101              : 
    1102              : void
    1103         5409 : MSRouteHandler::addRide(const SUMOSAXAttributes& attrs) {
    1104         5409 :     addRideOrTransport(attrs, SUMO_TAG_RIDE);
    1105         5369 : }
    1106              : 
    1107              : void
    1108         1843 : MSRouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
    1109         1843 :     addRideOrTransport(attrs, SUMO_TAG_TRANSPORT);
    1110         1831 : }
    1111              : 
    1112              : void
    1113         7252 : MSRouteHandler::addRideOrTransport(const SUMOSAXAttributes& attrs, const SumoXMLTag modeTag) {
    1114         7252 :     if (myVehicleParameter == nullptr) {
    1115            0 :         throw ProcessError(TLF("Cannot define % stage without %.", toString(modeTag), toString(modeTag)));
    1116              :     }
    1117              :     try {
    1118         9095 :         const std::string mode = modeTag == SUMO_TAG_RIDE ? "ride" : "transport";
    1119         7304 :         std::string agent = "person";
    1120         7304 :         std::string stop = "bus stop";
    1121         7252 :         if (myActiveType == ObjectTypeEnum::CONTAINER) {
    1122              :             agent = "container";
    1123              :             stop = "container stop";
    1124              :         }
    1125              : 
    1126         7252 :         if (!((myActiveType == ObjectTypeEnum::PERSON && modeTag == SUMO_TAG_RIDE) ||
    1127         1843 :                 (myActiveType == ObjectTypeEnum::CONTAINER && modeTag == SUMO_TAG_TRANSPORT))) {
    1128           36 :             throw ProcessError("Found " + mode + " inside " + agent + " element");
    1129              :         }
    1130         7240 :         const std::string aid = myVehicleParameter->id;
    1131         7240 :         bool ok = true;
    1132              :         const MSEdge* from = nullptr;
    1133        14520 :         const std::string desc = attrs.getOpt<std::string>(SUMO_ATTR_LINES, aid.c_str(), ok, LINE_ANY);
    1134         7280 :         StringTokenizer st(desc);
    1135        14480 :         MSStoppingPlace* s = retrieveStoppingPlace(attrs, " in " + agent + " '" + aid + "'");
    1136              :         MSEdge* to = nullptr;
    1137         7240 :         if (s != nullptr) {
    1138         1876 :             to = &s->getLane().getEdge();
    1139              :         }
    1140         7240 :         double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, aid.c_str(), ok,
    1141         1876 :                             s == nullptr ? std::numeric_limits<double>::infinity() : s->getEndLanePosition());
    1142              : 
    1143              :         const SUMOVehicleParameter* startVeh = nullptr;
    1144              :         const MSEdge* startVehFrom = nullptr;
    1145         7240 :         if (myActiveTransportablePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
    1146          902 :             if (st.size() != 1) {
    1147            0 :                 throw ProcessError("Triggered departure for " + agent + " '" + aid + "' requires a unique lines value.");
    1148              :             }
    1149              :             // agent starts
    1150          902 :             MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
    1151          902 :             const std::string vehID = st.front();
    1152          902 :             SUMOVehicle* sVeh = vehControl.getVehicle(vehID);
    1153          902 :             if (sVeh == nullptr) {
    1154           72 :                 if (MSNet::getInstance()->hasFlow(vehID)) {
    1155           40 :                     startVeh = MSNet::getInstance()->getInsertionControl().getFlowPars(vehID);
    1156           40 :                     if (startVeh != nullptr) {
    1157           40 :                         ConstMSRoutePtr const route = MSRoute::dictionary(startVeh->routeid);
    1158           40 :                         startVehFrom = route->getEdges().front();
    1159              :                         // flows are inserted at the end of the time step so we
    1160              :                         // do delay the pedestrian event by one time step
    1161           40 :                         myVehicleParameter->depart = startVeh->depart + DELTA_T;
    1162           40 :                         myStartTriggeredInFlow = true;
    1163              :                     }
    1164              :                 }
    1165              :             } else {
    1166          830 :                 startVeh = &sVeh->getParameter();
    1167          830 :                 startVehFrom = sVeh->getRoute().getEdges().front();
    1168          830 :                 myVehicleParameter->depart = startVeh->depart;
    1169              :             }
    1170              :             if (startVeh == nullptr) {
    1171              :                 if (mySkippedVehicles.count(vehID) == 0) {
    1172           48 :                     throw ProcessError("Unknown vehicle '" + vehID + "' in triggered departure for " + agent + " '" + aid + "'.");
    1173              :                 }
    1174              :                 // we cannot simply throw here because we need to parse the rest of the person (just to discard it)
    1175           16 :                 from = MSEdge::getAllEdges().front();  // a dummy edge to keep parsing active
    1176          870 :             } else if (startVeh->departProcedure == DepartDefinition::TRIGGERED) {
    1177            0 :                 throw ProcessError("Cannot use triggered vehicle '" + vehID + "' in triggered departure for " + agent + " '" + aid + "'.");
    1178              :             }
    1179              :         }
    1180              : 
    1181         7224 :         if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
    1182         4946 :             const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, aid.c_str(), ok);
    1183         4946 :             from = MSEdge::dictionary(fromID);
    1184         4946 :             if (from == nullptr) {
    1185            0 :                 throw ProcessError("The from edge '" + fromID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
    1186              :             }
    1187         4946 :             if (!myActiveTransportablePlan->empty() && myActiveTransportablePlan->back()->getDestination() != from) {
    1188           54 :                 const bool stopWithAccess = (myActiveTransportablePlan->back()->getDestinationStop() != nullptr
    1189           54 :                                              && &myActiveTransportablePlan->back()->getDestinationStop()->getLane().getEdge() == from);
    1190           54 :                 const bool transferAtJunction = (from->getFromJunction() == myActiveTransportablePlan->back()->getDestination()->getFromJunction()
    1191           54 :                                                  || from->getFromJunction() == myActiveTransportablePlan->back()->getDestination()->getToJunction());
    1192           54 :                 if (!(stopWithAccess || transferAtJunction)) {
    1193           14 :                     throw ProcessError("Disconnected plan for " + agent + " '" + aid +
    1194           42 :                                        "' (edge '" + fromID + "' != edge '" + myActiveTransportablePlan->back()->getDestination()->getID() + "').");
    1195              :                 }
    1196              :             }
    1197         4932 :             if (startVeh != nullptr && startVehFrom != from) {
    1198            8 :                 throw ProcessError("Disconnected plan for triggered " + agent + " '" + aid +
    1199           24 :                                    "' (edge '" + fromID + "' != edge '" + startVehFrom->getID() + "').");
    1200              :             }
    1201         2278 :         } else if (startVeh != nullptr) {
    1202              :             from = startVehFrom;
    1203              :         }
    1204         7202 :         if (myActiveTransportablePlan->empty()) {
    1205         4685 :             if (from == nullptr) {
    1206            0 :                 throw ProcessError("The start edge for " + agent + " '" + aid + "' is not known.");
    1207              :             } else {
    1208         4685 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(
    1209         9370 :                         from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
    1210              :             }
    1211              :         }
    1212              :         // given attribute may override given stopping place due access requirements
    1213         7202 :         if (to == nullptr || attrs.hasAttribute(SUMO_ATTR_TO)) {
    1214         5407 :             const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, aid.c_str(), ok);
    1215         5407 :             to = MSEdge::dictionary(toID);
    1216         5407 :             if (to == nullptr) {
    1217            6 :                 throw ProcessError("The to edge '" + toID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
    1218              :             }
    1219              :         }
    1220         7200 :         const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, aid.c_str(), ok, OptionsCont::getOptions().getString("persontrip.default.group"));
    1221         7200 :         const std::string intendedVeh = attrs.getOpt<std::string>(SUMO_ATTR_INTENDED, nullptr, ok, "");
    1222         7200 :         const SUMOTime intendedDepart = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DEPART, nullptr, ok, -1);
    1223        14400 :         arrivalPos = SUMOVehicleParameter::interpretEdgePos(arrivalPos, to->getLength(), SUMO_ATTR_ARRIVALPOS, agent + " '" + aid + "' takes a " + mode + " to edge '" + to->getID() + "'");
    1224         7200 :         MSStageDriving* stage = new MSStageDriving(from, to, s, arrivalPos, 0.0, st.getVector(), group, intendedVeh, intendedDepart);
    1225              :         // for loading from saved state
    1226         7200 :         stage->setDeparted(attrs.getOptSUMOTimeReporting(SUMO_ATTR_STARTED, nullptr, ok, -1));
    1227         7200 :         stage->setEnded(attrs.getOptSUMOTimeReporting(SUMO_ATTR_ENDED, nullptr, ok, -1));
    1228         7200 :         stage->setVehicleID(attrs.getOpt<std::string>(SUMO_ATTR_VEHICLE, nullptr, ok, ""));
    1229         7200 :         stage->setVehicleDistance(attrs.getOpt<double>(SUMO_ATTR_ROUTELENGTH, nullptr, ok, -1));
    1230         7200 :         myActiveTransportablePlan->push_back(stage);
    1231         7200 :         myParamStack.push_back(stage);
    1232         7292 :     } catch (ProcessError&) {
    1233           52 :         deleteActivePlanAndVehicleParameter();
    1234           52 :         throw;
    1235           52 :     }
    1236         7200 : }
    1237              : 
    1238              : MSStoppingPlace*
    1239        88152 : MSRouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, SUMOVehicleParameter::Stop* stopParam) {
    1240        88152 :     bool ok = true;
    1241              :     // dummy stop parameter to hold the attributes
    1242        88152 :     SUMOVehicleParameter::Stop stop;
    1243        88152 :     if (stopParam != nullptr) {
    1244        37930 :         stop = *stopParam;
    1245              :     } else {
    1246       100444 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
    1247        50222 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
    1248        50222 :         stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
    1249        50222 :         stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
    1250        50222 :         stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
    1251       100444 :         stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
    1252              :     }
    1253              :     MSStoppingPlace* toStop = nullptr;
    1254        88152 :     if (stop.busstop != "") {
    1255        12563 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
    1256        12563 :         if (toStop == nullptr) {
    1257           12 :             ok = false;
    1258           36 :             WRITE_ERROR(TLF("The busStop '%' is not known%.", stop.busstop, errorSuffix));
    1259              :         }
    1260        75589 :     } else if (stop.containerstop != "") {
    1261         1551 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
    1262         1551 :         if (toStop == nullptr) {
    1263            0 :             ok = false;
    1264            0 :             WRITE_ERROR(TLF("The containerStop '%' is not known%.", stop.containerstop, errorSuffix));
    1265              :         }
    1266        74038 :     } else if (stop.parkingarea != "") {
    1267        16390 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
    1268        16390 :         if (toStop == nullptr) {
    1269            0 :             ok = false;
    1270            0 :             WRITE_ERROR(TLF("The parkingArea '%' is not known%.", stop.parkingarea, errorSuffix));
    1271              :         }
    1272        57648 :     } else if (stop.chargingStation != "") {
    1273              :         // ok, we have a charging station
    1274         2020 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
    1275         2020 :         if (toStop == nullptr) {
    1276            0 :             ok = false;
    1277            0 :             WRITE_ERROR(TLF("The chargingStation '%' is not known%.", stop.chargingStation, errorSuffix));
    1278              :         }
    1279        55628 :     } else if (stop.overheadWireSegment != "") {
    1280              :         // ok, we have an overhead wire segment
    1281            0 :         toStop = MSNet::getInstance()->getStoppingPlace(stop.overheadWireSegment, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
    1282            0 :         if (toStop == nullptr) {
    1283            0 :             ok = false;
    1284            0 :             WRITE_ERROR(TLF("The overhead wire segment '%' is not known%.", stop.overheadWireSegment, errorSuffix));
    1285              :         }
    1286              :     }
    1287        88152 :     if (!ok && MSGlobals::gCheckRoutes) {
    1288           18 :         throw ProcessError(TLF("Invalid stop definition%.", errorSuffix));
    1289              :     }
    1290        88146 :     return toStop;
    1291        88152 : }
    1292              : 
    1293              : Parameterised*
    1294        37957 : MSRouteHandler::addStop(const SUMOSAXAttributes& attrs) {
    1295              :     Parameterised* result = nullptr;
    1296              :     try {
    1297              :         std::string errorSuffix;
    1298        37957 :         if (myActiveType == ObjectTypeEnum::PERSON) {
    1299         4047 :             errorSuffix = " in person '" + myVehicleParameter->id + "'";
    1300        36608 :         } else if (myActiveType == ObjectTypeEnum::CONTAINER) {
    1301         1026 :             errorSuffix = " in container '" + myVehicleParameter->id + "'";
    1302        36266 :         } else if (myVehicleParameter != nullptr) {
    1303        98877 :             errorSuffix = " in vehicle '" + myVehicleParameter->id + "'";
    1304              :         } else {
    1305         9989 :             errorSuffix = " in route '" + myActiveRouteID + "'";
    1306              :         }
    1307        37957 :         SUMOVehicleParameter::Stop stop;
    1308        75914 :         bool ok = parseStop(stop, attrs, errorSuffix, MsgHandler::getErrorInstance());
    1309        37957 :         if (!ok) {
    1310           27 :             if (MSGlobals::gCheckRoutes) {
    1311           27 :                 throw ProcessError();
    1312              :             }
    1313              :             return result;
    1314              :         }
    1315        37930 :         const MSEdge* edge = nullptr;
    1316              :         // patch chargingStation stop on a parkingArea
    1317        37930 :         if (stop.chargingStation != "") {
    1318         1204 :             const MSChargingStation* cs = dynamic_cast<MSChargingStation*>(MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION));
    1319         1204 :             const MSParkingArea* pa = cs->getParkingArea();
    1320         1204 :             if (pa != nullptr) {
    1321              :                 stop.parkingarea = pa->getID();
    1322           18 :                 stop.parking = ParkingType::OFFROAD;
    1323              :             }
    1324              :         }
    1325        37930 :         MSStoppingPlace* toStop = retrieveStoppingPlace(attrs, errorSuffix, &stop);
    1326              :         // if one of the previous stops is defined
    1327        37924 :         if (toStop != nullptr) {
    1328        26184 :             const MSLane& l = toStop->getLane();
    1329              :             stop.lane = l.getID();
    1330        26184 :             if ((stop.parametersSet & STOP_END_SET) == 0) {
    1331        26168 :                 stop.endPos = toStop->getEndLanePosition();
    1332              :             } else {
    1333           16 :                 stop.endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, nullptr, ok);
    1334              :             }
    1335        26184 :             stop.startPos = toStop->getBeginLanePosition();
    1336        26184 :             edge = &l.getEdge();
    1337              :         } else {
    1338              :             // no, the lane and the position should be given directly
    1339              :             // get the lane
    1340        11740 :             stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
    1341        11740 :             stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
    1342        11740 :             if (ok && stop.edge != "") { // edge is given directly
    1343         1777 :                 edge = MSEdge::dictionary(stop.edge);
    1344         1777 :                 if (edge == nullptr || (edge->isInternal() && !MSGlobals::gUsingInternalLanes)) {
    1345           18 :                     throw ProcessError(TLF("The edge '%' for a stop is not known%.", stop.edge, errorSuffix));
    1346              :                 }
    1347         9963 :             } else if (ok && stop.lane != "") { // lane is given directly
    1348         9889 :                 MSLane* stopLane = MSLane::dictionary(stop.lane);
    1349         9889 :                 if (stopLane == nullptr) {
    1350              :                     // check for opposite-direction stop
    1351          153 :                     stopLane = MSBaseVehicle::interpretOppositeStop(stop);
    1352          153 :                     if (stopLane != nullptr) {
    1353          145 :                         edge = MSEdge::dictionary(stop.edge);
    1354              :                     }
    1355              :                 } else {
    1356         9736 :                     edge = &stopLane->getEdge();
    1357              :                 }
    1358         9881 :                 if (stopLane == nullptr || (stopLane->isInternal() && !MSGlobals::gUsingInternalLanes)) {
    1359           24 :                     throw ProcessError(TLF("The lane '%' for a stop is not known%.", stop.lane, errorSuffix));
    1360              :                 }
    1361           74 :             } else if (ok && ((attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y))
    1362           39 :                               || (attrs.hasAttribute(SUMO_ATTR_LON) && attrs.hasAttribute(SUMO_ATTR_LAT)))) {
    1363              :                 Position pos;
    1364              :                 bool geo = false;
    1365           49 :                 if (attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y)) {
    1366           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));
    1367              :                 } else {
    1368           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));
    1369              :                     geo = true;
    1370              :                 }
    1371           49 :                 PositionVector positions;
    1372           49 :                 positions.push_back(pos);
    1373              :                 ConstMSEdgeVector geoEdges;
    1374              :                 SUMOVehicleClass vClass = SVC_PASSENGER;
    1375           49 :                 auto& vc = MSNet::getInstance()->getVehicleControl();
    1376           49 :                 if (!vc.getVTypeDistribution(myVehicleParameter->vtypeid)) {
    1377           49 :                     MSVehicleType* const type = vc.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
    1378           49 :                     if (type != nullptr) {
    1379           49 :                         vClass = type->getParameter().vehicleClass;
    1380              :                     }
    1381              :                 }
    1382           49 :                 parseGeoEdges(positions, geo, vClass, geoEdges, myVehicleParameter->id, true, ok, true);
    1383           49 :                 if (ok) {
    1384           42 :                     edge = geoEdges.front();
    1385           42 :                     if (geo) {
    1386           14 :                         GeoConvHelper::getFinal().x2cartesian_const(pos);
    1387              :                     }
    1388           42 :                     stop.parametersSet |= STOP_END_SET;
    1389           42 :                     stop.endPos = edge->getLanes()[0]->getShape().nearest_offset_to_point2D(pos, false);
    1390              :                 } else {
    1391           21 :                     throw ProcessError(TLF("Could not map stop position '%' to the network%.", pos, errorSuffix));
    1392              :                 }
    1393           56 :             } else {
    1394           25 :                 if (myActiveTransportablePlan && !myActiveTransportablePlan->empty()) { // use end of movement before
    1395           19 :                     toStop = myActiveTransportablePlan->back()->getDestinationStop();
    1396           19 :                     if (toStop != nullptr) { // use end of movement before definied as a stopping place
    1397            0 :                         edge = &toStop->getLane().getEdge();
    1398            0 :                         stop.lane = toStop->getLane().getID();
    1399            0 :                         stop.endPos = toStop->getEndLanePosition();
    1400            0 :                         stop.startPos = toStop->getBeginLanePosition();
    1401              :                     } else { // use end of movement before definied as lane/edge
    1402           19 :                         edge = myActiveTransportablePlan->back()->getDestination();
    1403           19 :                         stop.lane = edge->getLanes()[0]->getID();
    1404           19 :                         stop.endPos = myActiveTransportablePlan->back()->unspecifiedArrivalPos() ?
    1405            5 :                                       MSStage::ARRIVALPOS_UNSPECIFIED : myActiveTransportablePlan->back()->getArrivalPos();
    1406           38 :                         stop.startPos = MAX2(0., stop.endPos - MIN_STOP_LENGTH);
    1407              :                     }
    1408              :                 } else {
    1409           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);
    1410            6 :                     if (MSGlobals::gCheckRoutes) {
    1411            0 :                         throw ProcessError(msg);
    1412              :                     } else {
    1413           18 :                         WRITE_WARNING(msg);
    1414              :                         return result;
    1415              :                     }
    1416              :                 }
    1417              :             }
    1418        11713 :             stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
    1419        11713 :             if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
    1420            0 :                 WRITE_WARNINGF(TL("Deprecated attribute 'pos' in description of stop%."), errorSuffix);
    1421            0 :                 stop.endPos = attrs.getOpt<double>(SUMO_ATTR_POSITION, nullptr, ok, stop.endPos);
    1422              :             }
    1423        11713 :             if (stop.endPos < 0) {
    1424          777 :                 stop.endPos += edge->getLength();
    1425              :             }
    1426        23352 :             stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, MAX2(0., stop.endPos - MIN_STOP_LENGTH));
    1427        11713 :             if (!myAmLoadingState) {
    1428        20414 :                 const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS))
    1429        11392 :                                          || !MSGlobals::gCheckRoutes;
    1430        11392 :                 if (!ok || (checkStopPos(stop.startPos, stop.endPos, edge->getLength(), 0, friendlyPos) != StopPos::STOPPOS_VALID)) {
    1431            0 :                     throw ProcessError(TLF("Invalid start or end position for stop on %'%.",
    1432            0 :                                            stop.lane != "" ? ("lane '" + stop.lane) : ("edge '" + stop.edge), errorSuffix));
    1433              :                 }
    1434              :             }
    1435              :         }
    1436        37897 :         stop.edge = edge->getID();
    1437        37897 :         if (myActiveTransportablePlan) {
    1438         1684 :             if (myActiveTransportablePlan->empty()) {
    1439          750 :                 double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
    1440         1239 :                                    ? myVehicleParameter->departPos
    1441          698 :                                    : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
    1442         1187 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(
    1443         2374 :                         edge, toStop, -1, myVehicleParameter->depart, departPos, "start", true));
    1444          497 :             } else if (myActiveTransportablePlan->back()->getDestination() != edge) {
    1445           43 :                 if (myActiveTransportablePlan->back()->getDestination()->isTazConnector()) {
    1446            7 :                     myActiveTransportablePlan->back()->setDestination(edge, toStop);
    1447           36 :                 } else if (myActiveTransportablePlan->back()->getJumpDuration() < 0) {
    1448           56 :                     throw ProcessError(TLF("Disconnected plan for % '%' (%!=%).", myActiveTypeName, myVehicleParameter->id,
    1449           42 :                                            edge->getID(), myActiveTransportablePlan->back()->getDestination()->getID()));
    1450              :                 }
    1451              :             }
    1452              :             // transporting veh stops somewhere
    1453          454 :             else if (myActiveTransportablePlan->back()->getStageType() == MSStageType::WAITING
    1454          454 :                      && (attrs.hasAttribute(SUMO_ATTR_ENDPOS) || attrs.hasAttribute(SUMO_ATTR_STARTPOS))) {
    1455           12 :                 const double start = SUMOVehicleParameter::interpretEdgePos(stop.startPos, edge->getLength(), SUMO_ATTR_STARTPOS, "stopping at " + edge->getID());
    1456           12 :                 const double end = SUMOVehicleParameter::interpretEdgePos(stop.endPos, edge->getLength(), SUMO_ATTR_ENDPOS, "stopping at " + edge->getID());
    1457           12 :                 const double prevAr = myActiveTransportablePlan->back()->getArrivalPos();
    1458           12 :                 if (start > prevAr + NUMERICAL_EPS || end < prevAr - NUMERICAL_EPS) {
    1459           36 :                     WRITE_WARNINGF(TL("Disconnected plan for % '%' (stop range %-% does not cover previous arrival position %)."),
    1460              :                                    myActiveTypeName, myVehicleParameter->id, toString(start), toString(end), toString(prevAr));
    1461              :                 }
    1462              :             }
    1463         1670 :             std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "");
    1464         1670 :             double pos = (stop.startPos + stop.endPos) / 2.;
    1465         1670 :             if (!myActiveTransportablePlan->empty() && myActiveTransportablePlan->back()->getJumpDuration() < 0) {
    1466         1648 :                 pos = myActiveTransportablePlan->back()->unspecifiedArrivalPos() ?
    1467         1624 :                       MSStage::ARRIVALPOS_UNSPECIFIED : myActiveTransportablePlan->back()->getArrivalPos();
    1468              :             }
    1469         1670 :             myActiveTransportablePlan->push_back(new MSStageWaiting(edge, toStop, stop.duration, stop.until, pos, actType, false, stop.jump));
    1470         1670 :             result = myActiveTransportablePlan->back();
    1471              : 
    1472        36213 :         } else if (myVehicleParameter != nullptr) {
    1473        32906 :             myVehicleParameter->stops.push_back(stop);
    1474        32906 :             result = &myVehicleParameter->stops.back();
    1475              :         } else {
    1476         3307 :             myActiveRouteStops.push_back(stop);
    1477              :             result = &myActiveRouteStops.back();
    1478              :         }
    1479        37883 :         if (myInsertStopEdgesAt >= 0) {
    1480              :             //std::cout << " myInsertStopEdgesAt=" << myInsertStopEdgesAt << " edge=" << edge->getID() << " myRoute=" << toString(myActiveRoute) << "\n";
    1481         6740 :             if (edge->isInternal()) {
    1482           17 :                 if (myInsertStopEdgesAt > 0 && *(myActiveRoute.begin() + (myInsertStopEdgesAt - 1)) != edge->getNormalBefore()) {
    1483            0 :                     myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge->getNormalBefore());
    1484            0 :                     myInsertStopEdgesAt++;
    1485              :                 }
    1486           17 :                 myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge->getNormalSuccessor());
    1487           17 :                 myInsertStopEdgesAt++;
    1488              :             } else {
    1489         6723 :                 myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
    1490         6723 :                 myInsertStopEdgesAt++;
    1491              :             }
    1492        31143 :         } else if (myHaveVia) {
    1493              :             // vias were loaded, check for consistency
    1494         6725 :             if (std::find(myActiveRoute.begin(), myActiveRoute.end(), edge) == myActiveRoute.end()) {
    1495           21 :                 WRITE_WARNINGF(TL("Stop edge '%' missing in attribute 'via' for % '%'."),
    1496              :                                edge->getID(), myActiveTypeName, myVehicleParameter->id);
    1497              :             }
    1498              :         }
    1499        38025 :     } catch (ProcessError&) {
    1500           68 :         deleteActivePlanAndVehicleParameter();
    1501           68 :         throw;
    1502           68 :     }
    1503        37883 :     return result;
    1504              : }
    1505              : 
    1506              : 
    1507              : void
    1508        42140 : MSRouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
    1509              :                                    const MSEdge* fromEdge, const MSEdge*& toEdge,
    1510              :                                    double& departPos, double& arrivalPos, MSStoppingPlace*& bs,
    1511              :                                    const MSStage* const lastStage, bool& ok) {
    1512              :     try {
    1513        42140 :         const std::string description = "person '" + personID + "' walking from edge '" + fromEdge->getID() + "'";
    1514              : 
    1515        42140 :         if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
    1516            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."));
    1517              :         }
    1518        42140 :         departPos = 0.;
    1519        42140 :         if (lastStage != nullptr) {
    1520        33325 :             if (lastStage->getDestinationStop() != nullptr) {
    1521          313 :                 departPos = lastStage->getDestinationStop()->getAccessPos(fromEdge, &myParsingRNG);
    1522        33012 :             } else if (lastStage->getDestination() == fromEdge) {
    1523        32959 :                 departPos = lastStage->getArrivalPos();
    1524           53 :             } else if (lastStage->getDestination()->getToJunction() == fromEdge->getToJunction()) {
    1525           22 :                 departPos = fromEdge->getLength();
    1526              :             }
    1527              :         }
    1528              : 
    1529        42140 :         bs = retrieveStoppingPlace(attrs, " " + description);
    1530        42140 :         if (bs != nullptr) {
    1531         6851 :             arrivalPos = bs->getAccessPos(toEdge != nullptr ? toEdge : &bs->getLane().getEdge());
    1532         3610 :             if (arrivalPos < 0) {
    1533            0 :                 throw ProcessError("Bus stop '" + bs->getID() + "' is not connected to arrival edge '" + toEdge->getID() + "' for " + description + ".");
    1534              :             }
    1535         3610 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1536            8 :                 const double length = toEdge != nullptr ? toEdge->getLength() : bs->getLane().getLength();
    1537            8 :                 const double arrPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, description, length,
    1538            8 :                                       attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
    1539            8 :                 if (arrPos >= bs->getBeginLanePosition() && arrPos < bs->getEndLanePosition()) {
    1540            4 :                     arrivalPos = arrPos;
    1541              :                 } else {
    1542           12 :                     WRITE_WARNINGF(TL("Ignoring arrivalPos for % because it is outside the given stop '%'."), description, toString(SUMO_ATTR_BUS_STOP));
    1543            4 :                     arrivalPos = bs->getAccessPos(&bs->getLane().getEdge());
    1544              :                 }
    1545              :             }
    1546              :         } else {
    1547        38530 :             if (toEdge == nullptr) {
    1548            0 :                 throw ProcessError(TLF("No destination edge for %.", description));
    1549              :             }
    1550        38530 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1551        31728 :                 arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, description, toEdge->getLength(),
    1552        63456 :                              attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
    1553              :             } else {
    1554         6802 :                 arrivalPos = toEdge->getLength() / 2.;
    1555              :             }
    1556              :         }
    1557            0 :     } catch (ProcessError&) {
    1558            0 :         deleteActivePlanAndVehicleParameter();
    1559            0 :         throw;
    1560            0 :     }
    1561        42140 : }
    1562              : 
    1563              : 
    1564              : void
    1565         8828 : MSRouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
    1566         8828 :     if (myVehicleParameter == nullptr) {
    1567           12 :         throw ProcessError(TL("Cannot define person stage without person."));
    1568              :     }
    1569              :     try {
    1570              :         myActiveRoute.clear();
    1571         8822 :         bool ok = true;
    1572              :         const char* const id = myVehicleParameter->id.c_str();
    1573              :         const MSEdge* from = nullptr;
    1574         8822 :         const MSEdge* to = nullptr;
    1575         8822 :         parseFromViaTo(SUMO_TAG_PERSON, attrs);
    1576         8815 :         myInsertStopEdgesAt = -1;
    1577        11431 :         if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
    1578        11333 :                 || attrs.hasAttribute(SUMO_ATTR_FROMXY) || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
    1579         6346 :             from = myActiveRoute.front();
    1580         2469 :         } else if (myActiveTransportablePlan->empty()) {
    1581            0 :             throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
    1582              :         } else {
    1583         2469 :             from = myActiveTransportablePlan->back()->getDestination();
    1584              :         }
    1585        13056 :         if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
    1586        12126 :                 || attrs.hasAttribute(SUMO_ATTR_TOXY) || attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
    1587         5574 :             to = myActiveRoute.back();
    1588              :         } // else, to may also be derived from stopping place
    1589              : 
    1590         8815 :         const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
    1591         8815 :         if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1592            0 :             throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1593              :         }
    1594              : 
    1595         8815 :         double departPos = 0;
    1596         8815 :         double arrivalPos = 0;
    1597         8815 :         MSStoppingPlace* stoppingPlace = nullptr;
    1598         8815 :         parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, stoppingPlace, nullptr, ok);
    1599              : 
    1600         8815 :         SVCPermissions modeSet = 0;
    1601         8815 :         if (attrs.hasAttribute(SUMO_ATTR_MODES)) {
    1602         2286 :             const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
    1603              :             std::string errorMsg;
    1604              :             // try to parse person modes
    1605         2286 :             if (!SUMOVehicleParameter::parsePersonModes(modes, "person", id, modeSet, errorMsg)) {
    1606            0 :                 throw InvalidArgument(errorMsg);
    1607              :             }
    1608              :         } else {
    1609         7672 :             modeSet = myVehicleParameter->modes;
    1610              :         }
    1611         8815 :         const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, OptionsCont::getOptions().getString("persontrip.default.group"));
    1612         8815 :         MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
    1613        26447 :         const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, myVehicleParameter->vTypes);
    1614        17746 :         for (StringTokenizer st(types); st.hasNext();) {
    1615          118 :             const std::string vtypeid = st.next();
    1616          118 :             const MSVehicleType* const vType = vehControl.getVType(vtypeid);
    1617          118 :             if (vType == nullptr) {
    1618            6 :                 throw InvalidArgument("The vehicle type '" + vtypeid + "' in a trip for person '" + myVehicleParameter->id + "' is not known.");
    1619              :             }
    1620          232 :             modeSet |= (vType->getVehicleClass() == SVC_BICYCLE) ? SVC_BICYCLE : SVC_PASSENGER;
    1621         8815 :         }
    1622         8813 :         const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id, ok, -1.);
    1623         8813 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
    1624            0 :             throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
    1625              :         }
    1626         8813 :         const double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
    1627        17626 :         const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), -1, "personTrip");
    1628         8813 :         if (ok) {
    1629         8813 :             if (myActiveTransportablePlan->empty()) {
    1630         6197 :                 double initialDepartPos = myVehicleParameter->departPos;
    1631         6197 :                 if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
    1632              :                     initialDepartPos = RandHelper::rand(from->getLength(), &myParsingRNG);
    1633              :                 }
    1634        12394 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(from, nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
    1635              :             }
    1636         8813 :             myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
    1637         8813 :             MSStoppingPlace* fromStop = myActiveTransportablePlan->empty() ? nullptr : myActiveTransportablePlan->back()->getDestinationStop();
    1638        12053 :             myActiveTransportablePlan->push_back(new MSStageTrip(from, fromStop, to == nullptr ? &stoppingPlace->getLane().getEdge() : to,
    1639              :                                                  stoppingPlace, duration, modeSet, types, speed, walkFactor, group,
    1640        12053 :                                                  departPosLat, attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS), arrivalPos));
    1641         8815 :             myParamStack.push_back(myActiveTransportablePlan->back());
    1642         8813 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1643         1477 :                 myActiveTransportablePlan->back()->markSet(VEHPARS_ARRIVALPOS_SET);
    1644              :             }
    1645              :         }
    1646              :         myActiveRoute.clear();
    1647            9 :     } catch (ProcessError&) {
    1648            9 :         deleteActivePlanAndVehicleParameter();
    1649            9 :         throw;
    1650            9 :     }
    1651         8813 : }
    1652              : 
    1653              : 
    1654              : void
    1655        39226 : MSRouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
    1656        39226 :     if (myVehicleParameter == nullptr) {
    1657            0 :         throw ProcessError(TL("Cannot define person stage without person."));
    1658              :     }
    1659        39226 :     myActiveRouteID = "";
    1660        39226 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1661              :         try {
    1662              :             myActiveRoute.clear();
    1663        33333 :             bool ok = true;
    1664        33333 :             const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, nullptr, ok, -1);
    1665        33333 :             if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1666           24 :                 throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1667              :             }
    1668              :             double speed = -1; // default to vType speed
    1669        33325 :             if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
    1670          361 :                 speed = attrs.get<double>(SUMO_ATTR_SPEED, nullptr, ok);
    1671          361 :                 if (speed <= 0) {
    1672            0 :                     throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
    1673              :                 }
    1674              :             }
    1675        33325 :             double departPos = 0;
    1676        33325 :             double arrivalPos = 0;
    1677        33325 :             MSStoppingPlace* bs = nullptr;
    1678        33325 :             if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1679          636 :                 myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
    1680          636 :                 ConstMSRoutePtr route = MSRoute::dictionary(myActiveRouteID, &myParsingRNG);
    1681          636 :                 if (route == nullptr) {
    1682            0 :                     throw ProcessError("The route '" + myActiveRouteID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
    1683              :                 }
    1684          636 :                 myActiveRoute = route->getEdges();
    1685              :             } else {
    1686        65378 :                 MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, myActiveRouteID);
    1687              :             }
    1688        33325 :             if (myActiveTransportablePlan->empty()) {
    1689        32830 :                 double initialDepartPos = myVehicleParameter->departPos;
    1690        32830 :                 if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
    1691           23 :                     initialDepartPos = RandHelper::rand(myActiveRoute.front()->getLength(), &myParsingRNG);
    1692              :                 }
    1693        65660 :                 myActiveTransportablePlan->push_back(new MSStageWaiting(myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
    1694              :             }
    1695        33325 :             parseWalkPositions(attrs, myVehicleParameter->id, myActiveRoute.front(), myActiveRoute.back(), departPos, arrivalPos, bs, myActiveTransportablePlan->back(), ok);
    1696        33325 :             if (myActiveRoute.empty()) {
    1697            0 :                 throw ProcessError(TLF("No edges to walk for person '%'.", myVehicleParameter->id));
    1698              :             }
    1699        33613 :             if (myActiveTransportablePlan->back()->getDestination() != myActiveRoute.front() &&
    1700        33325 :                     myActiveTransportablePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getFromJunction() &&
    1701          258 :                     myActiveTransportablePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getToJunction()) {
    1702          234 :                 if (myActiveTransportablePlan->back()->getDestinationStop() == nullptr || myActiveTransportablePlan->back()->getDestinationStop()->getAccessPos(myActiveRoute.front()) < 0.) {
    1703           36 :                     throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + " not connected to " + myActiveTransportablePlan->back()->getDestination()->getID() + ").");
    1704              :                 }
    1705              :             }
    1706        33313 :             const int departLane = attrs.getOpt<int>(SUMO_ATTR_DEPARTLANE, nullptr, ok, -1);
    1707        66626 :             const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), departLane, "walk");
    1708        33313 :             MSStageWalking* stage = new MSStageWalking(myVehicleParameter->id, myActiveRoute, bs, duration, speed, departPos, arrivalPos, departPosLat, departLane, myActiveRouteID);
    1709        33313 :             stage->setDeparted(attrs.getOptSUMOTimeReporting(SUMO_ATTR_STARTED, nullptr, ok, -1));
    1710        33313 :             stage->setEnded(attrs.getOptSUMOTimeReporting(SUMO_ATTR_ENDED, nullptr, ok, -1));
    1711        33313 :             stage->setTotalWaitingTime(attrs.getOptSUMOTimeReporting(SUMO_ATTR_WAITINGTIME, nullptr, ok, 0));
    1712        33327 :             if (attrs.hasAttribute(SUMO_ATTR_EXITTIMES) && OptionsCont::getOptions().getBool("vehroute-output.exit-times")) {
    1713           14 :                 std::vector<SUMOTime>* exitTimes = new std::vector<SUMOTime>();
    1714           44 :                 for (const std::string& tStr : attrs.get<std::vector<std::string> >(SUMO_ATTR_EXITTIMES, nullptr, ok)) {
    1715           30 :                     exitTimes->push_back(string2time(tStr));
    1716           14 :                 }
    1717              :                 stage->setExitTimes(exitTimes);
    1718              :             }
    1719        33313 :             if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1720              :                 stage->markSet(VEHPARS_ARRIVALPOS_SET);
    1721              :             }
    1722        33313 :             myActiveTransportablePlan->push_back(stage);
    1723        33313 :             myParamStack.push_back(stage);
    1724              :             myActiveRoute.clear();
    1725           20 :         } catch (ProcessError&) {
    1726           20 :             deleteActivePlanAndVehicleParameter();
    1727           20 :             throw;
    1728           20 :         }
    1729              :     } else { // parse walks from->to as person trips
    1730         5893 :         addPersonTrip(attrs);
    1731              :     }
    1732        39206 : }
    1733              : 
    1734              : double
    1735        42126 : MSRouteHandler::interpretDepartPosLat(const std::string& value, int departLane, const std::string& element) {
    1736        42126 :     double pos = MSPModel::UNSPECIFIED_POS_LAT;
    1737        42126 :     if (value == "") {
    1738              :         return pos;
    1739              :     }
    1740              :     std::string error;
    1741              :     DepartPosLatDefinition dpd;
    1742          250 :     if (SUMOVehicleParameter::parseDepartPosLat(value, element, myVehicleParameter->id, pos, dpd, error)) {
    1743          250 :         if (dpd != DepartPosLatDefinition::GIVEN) {
    1744            4 :             const MSLane* lane = MSStageMoving::checkDepartLane(myActiveRoute.front(), SVC_IGNORING, departLane, myVehicleParameter->id);
    1745            4 :             if (lane == nullptr) {
    1746            0 :                 throw ProcessError(TLF("Could not find departure lane for walk of person '%' when interpreting departPosLat", myVehicleParameter->id));
    1747              :             }
    1748            4 :             const double usableWidth = lane->getWidth() - 0.5;
    1749            4 :             switch (dpd) {
    1750            0 :                 case DepartPosLatDefinition::RIGHT:
    1751            0 :                     pos = -usableWidth / 2;
    1752            0 :                     break;
    1753            0 :                 case DepartPosLatDefinition::LEFT:
    1754            0 :                     pos = usableWidth / 2;
    1755            0 :                     break;
    1756            0 :                 case DepartPosLatDefinition::CENTER:
    1757            0 :                     pos = 0;
    1758            0 :                     break;
    1759            4 :                 case DepartPosLatDefinition::RANDOM:
    1760              :                 case DepartPosLatDefinition::FREE:
    1761              :                 case DepartPosLatDefinition::RANDOM_FREE:
    1762              :                     /// @note must be randomized for every person individually when loading a personFlow
    1763            4 :                     pos = MSPModel::RANDOM_POS_LAT;
    1764            4 :                     break;
    1765              :                 default:
    1766              :                     break;
    1767              :             }
    1768              :         }
    1769              :     } else {
    1770            0 :         throw ProcessError(error);
    1771              :     }
    1772          250 :     return pos;
    1773              : }
    1774              : 
    1775              : 
    1776              : void
    1777        45931 : MSRouteHandler::addTransportable(const SUMOSAXAttributes& /*attrs*/, const bool isPerson) {
    1778        45931 :     myActiveType = isPerson ? ObjectTypeEnum::PERSON : ObjectTypeEnum::CONTAINER;
    1779        45931 :     if (!MSNet::getInstance()->getVehicleControl().hasVType(myVehicleParameter->vtypeid)) {
    1780            0 :         const std::string error = TLF("The type '%' for % '%' is not known.", myVehicleParameter->vtypeid, myActiveTypeName, myVehicleParameter->id);
    1781            0 :         deleteActivePlanAndVehicleParameter();
    1782            0 :         throw ProcessError(error);
    1783              :     }
    1784        45931 :     myActiveTransportablePlan = new MSTransportable::MSTransportablePlan();
    1785        45931 : }
    1786              : 
    1787              : 
    1788              : void
    1789         1552 : MSRouteHandler::addTranship(const SUMOSAXAttributes& attrs) {
    1790              :     try {
    1791              :         myActiveRoute.clear();
    1792         1552 :         const std::string cid = myVehicleParameter->id;
    1793         1552 :         bool ok = true;
    1794         1552 :         const MSEdge* from = nullptr;
    1795         1552 :         const MSEdge* to = nullptr;
    1796              :         MSStoppingPlace* cs = nullptr;
    1797              : 
    1798              :         double speed;
    1799         1552 :         const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid);
    1800         1552 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED)) { // speed is explicitly set
    1801           71 :             speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, -1);
    1802           71 :             if (!ok) {
    1803            0 :                 throw ProcessError(TLF("Could not read tranship speed for container '%'.", cid));
    1804              :             }
    1805         1481 :         } else if (vtype != nullptr && vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) { // speed is set by vtype
    1806              :             speed = vtype->getMaxSpeed();
    1807              :         } else { // default speed value
    1808         1481 :             speed = DEFAULT_CONTAINER_TRANSHIP_SPEED;
    1809              :         }
    1810         1552 :         if (speed <= 0) {
    1811            0 :             throw ProcessError(TLF("Non-positive tranship speed for container '%'.", cid));
    1812              :         }
    1813              :         // values from preceding stage:
    1814              :         const MSEdge* preEdge = nullptr;
    1815              :         double prePos = 0;
    1816         1552 :         if (!myActiveTransportablePlan->empty()) {
    1817          577 :             preEdge = myActiveTransportablePlan->back()->getDestination();
    1818          577 :             prePos = myActiveTransportablePlan->back()->getArrivalPos();
    1819              :         }
    1820              :         // set depart position as given attribute value, arrival position of preceding stage or default (=0)
    1821         1552 :         double departPos = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS, cid.c_str(), ok, prePos);
    1822              : 
    1823         1552 :         if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
    1824          252 :             MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, cid.c_str(), ok), myActiveRoute, myActiveRouteID);
    1825              :         } else {
    1826              :             // set 'from':
    1827         1426 :             if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
    1828          919 :                 const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, cid.c_str(), ok);
    1829          919 :                 from = MSEdge::dictionary(fromID);
    1830          919 :                 if (from == nullptr) {
    1831            0 :                     throw ProcessError("The from edge '" + fromID + "' within a tranship of container '" + cid + "' is not known.");
    1832              :                 }
    1833          919 :                 if (preEdge != nullptr && preEdge != from) {
    1834            0 :                     throw ProcessError("Disconnected plan for container '" + cid + "' (" + from->getID() + "!=" + preEdge->getID() + ").");
    1835              :                 }
    1836          507 :             } else if (preEdge == nullptr) {
    1837            0 :                 throw ProcessError(TLF("The start edge for container '%' is not known.", cid));
    1838              :             } else {
    1839          507 :                 from = preEdge;
    1840              :             }
    1841              :             // set 'to':
    1842         1426 :             if (attrs.hasAttribute(SUMO_ATTR_TO)) {
    1843          584 :                 const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, cid.c_str(), ok);
    1844          584 :                 to = MSEdge::dictionary(toID);
    1845          584 :                 if (to == nullptr) {
    1846            0 :                     throw ProcessError("The to edge '" + toID + "' within a tranship of container '" + cid + "' is not known.");
    1847              :                 }
    1848              :             } else {
    1849          842 :                 const std::string description = "container '" + cid + "' transhipping from edge '" + from->getID() + "'";
    1850          842 :                 cs = retrieveStoppingPlace(attrs, " " + description);
    1851          842 :                 if (cs != nullptr) {
    1852          842 :                     to = &cs->getLane().getEdge();
    1853              :                 } else {
    1854            0 :                     throw ProcessError(TLF("Inconsistent tranship for container '%', needs either: 'edges', 'to', 'containerStop' (or any other stopping place)", cid));
    1855              :                 }
    1856              :             }
    1857         1426 :             myActiveRoute.push_back(from);
    1858         1426 :             myActiveRoute.push_back(to);
    1859              :         }
    1860         1552 :         if (myActiveRoute.empty()) {
    1861            0 :             throw ProcessError(TLF("No edges to tranship container '%'.", cid));
    1862              :         }
    1863         1552 :         if (preEdge == nullptr) { // additional 'stop' to start the container plan
    1864          975 :             myActiveTransportablePlan->push_back(new MSStageWaiting(
    1865         1950 :                     myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, departPos, "start", true));
    1866              :         }
    1867         3104 :         double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, cid.c_str(), ok,
    1868         1552 :                             cs == nullptr ? myActiveRoute.back()->getLength() : cs->getEndLanePosition());
    1869         1552 :         myActiveTransportablePlan->push_back(new MSStageTranship(myActiveRoute, cs, speed, departPos, arrivalPos));
    1870         1552 :         myParamStack.push_back(myActiveTransportablePlan->back());
    1871              :         myActiveRoute.clear();
    1872            0 :     } catch (ProcessError&) {
    1873            0 :         deleteActivePlanAndVehicleParameter();
    1874            0 :         throw;
    1875            0 :     }
    1876         1552 : }
    1877              : 
    1878              : 
    1879              : void
    1880          126 : MSRouteHandler::initLaneTree(NamedRTree* tree) {
    1881         5124 :     for (const auto& edge : MSEdge::getAllEdges()) {
    1882         4998 :         if (edge->isNormal() || MSGlobals::gUsingInternalLanes) {
    1883         9334 :             for (MSLane* lane : edge->getLanes()) {
    1884         4564 :                 Boundary b = lane->getShape().getBoxBoundary();
    1885         4564 :                 const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
    1886         4564 :                 const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
    1887         4564 :                 tree->Insert(cmin, cmax, lane);
    1888              :             }
    1889              :         }
    1890              :     }
    1891          126 : }
    1892              : 
    1893              : 
    1894              : SumoRNG*
    1895            0 : MSRouteHandler::getRNG() {
    1896            0 :     return &myParsingRNG;
    1897              : }
    1898              : 
    1899              : 
    1900              : const SUMOVTypeParameter*
    1901        62135 : MSRouteHandler::getVTypeParameter(const std::string& refid) {
    1902        62135 :     const auto t = MSNet::getInstance()->getVehicleControl().getVType(refid);
    1903        62135 :     return t != nullptr ? &t->getParameter() : nullptr;
    1904              : }
    1905              : 
    1906              : 
    1907              : MSEdge*
    1908          980 : MSRouteHandler::retrieveEdge(const std::string& id) {
    1909          980 :     return MSEdge::dictionary(id);
    1910              : }
    1911              : 
    1912              : 
    1913              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1