LCOV - code coverage report
Current view: top level - src/microsim - MSRouteHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 89.9 % 1114 1001
Test Date: 2024-10-24 15:46:30 Functions: 100.0 % 41 41

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

Generated by: LCOV version 2.0-1