LCOV - code coverage report
Current view: top level - src/router - RORouteHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.7 % 779 691
Test Date: 2024-12-21 15:45:41 Functions: 100.0 % 36 36

            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    RORouteHandler.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Sascha Krieg
      18              : /// @author  Michael Behrisch
      19              : /// @date    Mon, 9 Jul 2001
      20              : ///
      21              : // Parser and container for routes during their loading
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <map>
      27              : #include <vector>
      28              : #include <iostream>
      29              : #include <utils/iodevices/OutputDevice.h>
      30              : #include <utils/xml/SUMOSAXHandler.h>
      31              : #include <utils/xml/SUMOXMLDefinitions.h>
      32              : #include <utils/geom/GeoConvHelper.h>
      33              : #include <utils/common/FileHelpers.h>
      34              : #include <utils/common/MsgHandler.h>
      35              : #include <utils/common/StringTokenizer.h>
      36              : #include <utils/common/UtilExceptions.h>
      37              : #include <utils/options/OptionsCont.h>
      38              : #include <utils/vehicle/SUMOVehicleParserHelper.h>
      39              : #include <utils/xml/SUMOSAXReader.h>
      40              : #include <utils/xml/XMLSubSys.h>
      41              : #include <utils/iodevices/OutputDevice_String.h>
      42              : #include "RONet.h"
      43              : #include "ROEdge.h"
      44              : #include "ROLane.h"
      45              : #include "RORouteDef.h"
      46              : #include "RORouteHandler.h"
      47              : 
      48              : // ===========================================================================
      49              : // method definitions
      50              : // ===========================================================================
      51        15628 : RORouteHandler::RORouteHandler(RONet& net, const std::string& file,
      52              :                                const bool tryRepair,
      53              :                                const bool emptyDestinationsAllowed,
      54              :                                const bool ignoreErrors,
      55        15628 :                                const bool checkSchema) :
      56              :     SUMORouteHandler(file, checkSchema ? "routes" : "", true),
      57        31256 :     MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
      58        31256 :                OptionsCont::getOptions().getBool("mapmatch.taz"),
      59        15628 :                OptionsCont::getOptions().getFloat("mapmatch.distance"),
      60        15628 :                ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
      61        15628 :     myNet(net),
      62        15628 :     myActiveRouteRepeat(0),
      63        15628 :     myActiveRoutePeriod(0),
      64        15628 :     myActivePlan(nullptr),
      65        15628 :     myActiveContainerPlan(nullptr),
      66        15628 :     myActiveContainerPlanSize(0),
      67        15628 :     myTryRepair(tryRepair),
      68        15628 :     myEmptyDestinationsAllowed(emptyDestinationsAllowed),
      69        15628 :     myErrorOutput(ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
      70        15628 :     myBegin(string2time(OptionsCont::getOptions().getString("begin"))),
      71        15628 :     myKeepVTypeDist(OptionsCont::getOptions().getBool("keep-vtype-distributions")),
      72        31256 :     myUnsortedInput(OptionsCont::getOptions().exists("unsorted-input") && OptionsCont::getOptions().getBool("unsorted-input")),
      73        15628 :     myCurrentVTypeDistribution(nullptr),
      74        69240 :     myCurrentAlternatives(nullptr) {
      75        15628 :     myActiveRoute.reserve(100);
      76        15628 : }
      77              : 
      78              : 
      79        31256 : RORouteHandler::~RORouteHandler() {
      80        15628 :     delete myCurrentAlternatives;
      81        46884 : }
      82              : 
      83              : 
      84              : void
      85          352 : RORouteHandler::deleteActivePlanAndVehicleParameter() {
      86          352 :     if (myActivePlan != nullptr) {
      87            4 :         for (ROPerson::PlanItem* const it : *myActivePlan) {
      88            0 :             delete it;
      89              :         }
      90            4 :         delete myActivePlan;
      91            4 :         myActivePlan = nullptr;
      92              :     }
      93          352 :     delete myActiveContainerPlan;
      94          352 :     myActiveContainerPlan = nullptr;
      95          352 :     delete myVehicleParameter;
      96          352 :     myVehicleParameter = nullptr;
      97          352 : }
      98              : 
      99              : 
     100              : void
     101        41590 : RORouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs, bool& ok) {
     102        41590 :     const std::string element = toString(tag);
     103              :     myActiveRoute.clear();
     104        41590 :     bool useTaz = OptionsCont::getOptions().getBool("with-taz");
     105        41590 :     if (useTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
     106         1344 :         WRITE_WARNINGF(TL("Taz usage was requested but no taz present in % '%'!"), element, myVehicleParameter->id);
     107              :         useTaz = false;
     108              :     }
     109              :     SUMOVehicleClass vClass = SVC_PASSENGER;
     110        41590 :     if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
     111        41558 :         SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     112        41558 :         if (type != nullptr) {
     113        41484 :             vClass = type->vehicleClass;
     114              :         }
     115              :     }
     116              :     // from-attributes
     117        41590 :     const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
     118        55714 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_FROM) && !attrs.hasAttribute(SUMO_ATTR_FROMXY) && !attrs.hasAttribute(SUMO_ATTR_FROMLONLAT))) &&
     119        26870 :             (attrs.hasAttribute(SUMO_ATTR_FROM_TAZ) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION))) {
     120        12370 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION);
     121        13748 :         const std::string tazType = useJunction ? "junction" : "taz";
     122        13748 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM_TAZ, myVehicleParameter->id.c_str(), ok, true);
     123        24740 :         const ROEdge* fromTaz = myNet.getEdge(tazID + "-source");
     124        12370 :         if (fromTaz == nullptr) {
     125           16 :             myErrorOutput->inform("Source " + tazType + " '" + tazID + "' not known for " + element + " '" + myVehicleParameter->id + "'!"
     126            8 :                                   + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
     127            8 :             ok = false;
     128        12362 :         } else if (fromTaz->getNumSuccessors() == 0 && tag != SUMO_TAG_PERSON) {
     129            0 :             myErrorOutput->inform("Source " + tazType + " '" + tazID + "' has no outgoing edges for " + element + " '" + myVehicleParameter->id + "'!");
     130            0 :             ok = false;
     131              :         } else {
     132        12362 :             myActiveRoute.push_back(fromTaz);
     133              :         }
     134        29220 :     } else if (attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
     135          102 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMXY, myVehicleParameter->id.c_str(), ok), false, vClass, myActiveRoute, rid, true, ok);
     136          102 :         if (myMapMatchTAZ && ok) {
     137           20 :             myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
     138           20 :             myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
     139              :         }
     140        29118 :     } else if (attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
     141           24 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMLONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, myActiveRoute, rid, true, ok);
     142           24 :         if (myMapMatchTAZ && ok) {
     143            0 :             myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
     144            0 :             myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
     145              :         }
     146              :     } else {
     147        58188 :         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok), myActiveRoute, rid, ok);
     148              :     }
     149        41590 :     if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_VIALONLAT) && !attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
     150        34162 :         myInsertStopEdgesAt = (int)myActiveRoute.size();
     151              :     }
     152              : 
     153              :     // via-attributes
     154              :     ConstROEdgeVector viaEdges;
     155        41590 :     if (attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
     156           32 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIAXY, myVehicleParameter->id.c_str(), ok), false, vClass, viaEdges, rid, false, ok);
     157        41558 :     } else if (attrs.hasAttribute(SUMO_ATTR_VIALONLAT)) {
     158            4 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIALONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, viaEdges, rid, false, ok);
     159        41554 :     } else if (attrs.hasAttribute(SUMO_ATTR_VIAJUNCTIONS)) {
     160           24 :         for (std::string junctionID : attrs.get<std::vector<std::string> >(SUMO_ATTR_VIAJUNCTIONS, myVehicleParameter->id.c_str(), ok)) {
     161           32 :             const ROEdge* viaSink = myNet.getEdge(junctionID + "-sink");
     162           16 :             if (viaSink == nullptr) {
     163            0 :                 myErrorOutput->inform("Junction-taz '" + junctionID + "' not found." + JUNCTION_TAZ_MISSING_HELP);
     164            0 :                 ok = false;
     165              :             } else {
     166           16 :                 viaEdges.push_back(viaSink);
     167              :             }
     168            8 :         }
     169              :     } else {
     170        83092 :         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true), viaEdges, rid, ok);
     171              :     }
     172        60764 :     for (const ROEdge* e : viaEdges) {
     173        19174 :         myActiveRoute.push_back(e);
     174        19174 :         myVehicleParameter->via.push_back(e->getID());
     175              :     }
     176              : 
     177              :     // to-attributes
     178        56290 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_TO) && !attrs.hasAttribute(SUMO_ATTR_TOXY) && !attrs.hasAttribute(SUMO_ATTR_TOLONLAT))) &&
     179        28022 :             (attrs.hasAttribute(SUMO_ATTR_TO_TAZ) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION))) {
     180        12362 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION);
     181        13740 :         const std::string tazType = useJunction ? "junction" : "taz";
     182        13740 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO_TAZ, myVehicleParameter->id.c_str(), ok, true);
     183        24724 :         const ROEdge* toTaz = myNet.getEdge(tazID + "-sink");
     184        12362 :         if (toTaz == nullptr) {
     185            0 :             myErrorOutput->inform("Sink " + tazType + " '" + tazID + "' not known for " + element + " '" + myVehicleParameter->id + "'!"
     186            0 :                                   + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
     187            0 :             ok = false;
     188        12362 :         } else if (toTaz->getNumPredecessors() == 0 && tag != SUMO_TAG_PERSON) {
     189            0 :             myErrorOutput->inform("Sink " + tazType + " '" + tazID + "' has no incoming edges for " + element + " '" + myVehicleParameter->id + "'!");
     190            0 :             ok = false;
     191              :         } else {
     192        12362 :             myActiveRoute.push_back(toTaz);
     193              :         }
     194        29228 :     } else if (attrs.hasAttribute(SUMO_ATTR_TOXY)) {
     195          102 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOXY, myVehicleParameter->id.c_str(), ok, true), false, vClass, myActiveRoute, rid, false, ok);
     196          102 :         if (myMapMatchTAZ && ok) {
     197           20 :             myVehicleParameter->toTaz = myActiveRoute.back()->getID();
     198           20 :             myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
     199              :         }
     200        29126 :     } else if (attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
     201           24 :         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOLONLAT, myVehicleParameter->id.c_str(), ok, true), true, vClass, myActiveRoute, rid, false, ok);
     202           24 :         if (myMapMatchTAZ && ok) {
     203            0 :             myVehicleParameter->toTaz = myActiveRoute.back()->getID();
     204            0 :             myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
     205              :         }
     206              :     } else {
     207        58204 :         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true), myActiveRoute, rid, ok);
     208              :     }
     209        41590 :     myActiveRouteID = "!" + myVehicleParameter->id;
     210        41590 :     if (myVehicleParameter->routeid == "") {
     211              :         myVehicleParameter->routeid = myActiveRouteID;
     212              :     }
     213        83180 : }
     214              : 
     215              : 
     216              : void
     217       512650 : RORouteHandler::myStartElement(int element,
     218              :                                const SUMOSAXAttributes& attrs) {
     219              :     try {
     220       512650 :         if (myActivePlan != nullptr && myActivePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED && element != SUMO_TAG_RIDE && element != SUMO_TAG_PARAM) {
     221           12 :             throw ProcessError(TLF("Triggered departure for person '%' requires starting with a ride.", myVehicleParameter->id));
     222       512646 :         } else if (myActiveContainerPlan != nullptr && myActiveContainerPlanSize == 0 && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED && element != SUMO_TAG_TRANSPORT && element != SUMO_TAG_PARAM) {
     223           12 :             throw ProcessError(TLF("Triggered departure for container '%' requires starting with a transport.", myVehicleParameter->id));
     224              :         }
     225       512642 :         SUMORouteHandler::myStartElement(element, attrs);
     226       512302 :         bool ok = true;
     227       512302 :         switch (element) {
     228         1383 :             case SUMO_TAG_PERSON:
     229              :             case SUMO_TAG_PERSONFLOW: {
     230         1383 :                 myActivePlan = new std::vector<ROPerson::PlanItem*>();
     231         1383 :                 break;
     232              :             }
     233              :             case SUMO_TAG_RIDE:
     234              :                 break; // handled in addRide, called from SUMORouteHandler::myStartElement
     235           64 :             case SUMO_TAG_CONTAINER:
     236              :             case SUMO_TAG_CONTAINERFLOW:
     237           64 :                 myActiveContainerPlan = new OutputDevice_String(1);
     238           64 :                 myActiveContainerPlanSize = 0;
     239           64 :                 myActiveContainerPlan->openTag((SumoXMLTag)element);
     240           64 :                 (*myActiveContainerPlan) << attrs;
     241              :                 break;
     242           60 :             case SUMO_TAG_TRANSPORT:
     243              :             case SUMO_TAG_TRANSHIP:
     244           60 :                 if (myActiveContainerPlan == nullptr) {
     245            8 :                     throw ProcessError(TLF("Found % outside container element", toString((SumoXMLTag)element)));
     246              :                 }
     247              :                 // copy container elements
     248           56 :                 myActiveContainerPlan->openTag((SumoXMLTag)element);
     249           56 :                 (*myActiveContainerPlan) << attrs;
     250           56 :                 myActiveContainerPlan->closeTag();
     251           56 :                 myActiveContainerPlanSize++;
     252           56 :                 break;
     253         3465 :             case SUMO_TAG_FLOW:
     254         3465 :                 myActiveRouteProbability = DEFAULT_VEH_PROB;
     255         3465 :                 parseFromViaTo((SumoXMLTag)element, attrs, ok);
     256              :                 break;
     257        36802 :             case SUMO_TAG_TRIP:
     258        36802 :                 myActiveRouteProbability = DEFAULT_VEH_PROB;
     259        36802 :                 parseFromViaTo((SumoXMLTag)element, attrs, ok);
     260              :                 break;
     261              :             default:
     262              :                 break;
     263              :         }
     264          352 :     } catch (ProcessError&) {
     265          352 :         deleteActivePlanAndVehicleParameter();
     266          352 :         throw;
     267          352 :     }
     268       512298 : }
     269              : 
     270              : 
     271              : void
     272           52 : RORouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
     273           52 :     bool ok = true;
     274           52 :     myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     275           52 :     if (ok) {
     276           52 :         myCurrentVTypeDistribution = new RandomDistributor<SUMOVTypeParameter*>();
     277           52 :         if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
     278              :             std::vector<double> probs;
     279           36 :             if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
     280            8 :                 StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentVTypeDistributionID.c_str(), ok));
     281           24 :                 while (st.hasNext()) {
     282           32 :                     probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
     283              :                 }
     284            8 :             }
     285           36 :             const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
     286           36 :             StringTokenizer st(vTypes);
     287           36 :             int probIndex = 0;
     288           84 :             while (st.hasNext()) {
     289           48 :                 const std::string typeID = st.next();
     290           48 :                 const RandomDistributor<SUMOVTypeParameter*>* const dist = myNet.getVTypeDistribution(typeID);
     291            8 :                 if (dist != nullptr) {
     292            8 :                     const double distProb = ((int)probs.size() > probIndex ? probs[probIndex] : 1.) / dist->getOverallProb();
     293              :                     std::vector<double>::const_iterator probIt = dist->getProbs().begin();
     294           24 :                     for (SUMOVTypeParameter* const type : dist->getVals()) {
     295           16 :                         myCurrentVTypeDistribution->add(type, distProb * *probIt);
     296              :                         probIt++;
     297              :                     }
     298              :                 } else {
     299           40 :                     SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
     300           40 :                     if (type == nullptr) {
     301           24 :                         myErrorOutput->inform("Unknown vehicle type '" + typeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
     302              :                     } else {
     303           32 :                         const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : type->defaultProbability);
     304           32 :                         myCurrentVTypeDistribution->add(type, prob);
     305              :                     }
     306              :                 }
     307           48 :                 probIndex++;
     308              :             }
     309           36 :             if (probs.size() > 0 && probIndex != (int)probs.size()) {
     310            0 :                 WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
     311              :                               " types in vTypeDistribution '" + myCurrentVTypeDistributionID + "'");
     312              :             }
     313           72 :         }
     314              :     }
     315           52 : }
     316              : 
     317              : 
     318              : void
     319           52 : RORouteHandler::closeVehicleTypeDistribution() {
     320           52 :     if (myCurrentVTypeDistribution != nullptr) {
     321           52 :         if (myCurrentVTypeDistribution->getOverallProb() == 0) {
     322            8 :             delete myCurrentVTypeDistribution;
     323           24 :             myErrorOutput->inform("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
     324           44 :         } else if (!myNet.addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
     325            0 :             delete myCurrentVTypeDistribution;
     326            0 :             myErrorOutput->inform("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
     327              :         }
     328           52 :         myCurrentVTypeDistribution = nullptr;
     329              :     }
     330           52 : }
     331              : 
     332              : 
     333              : void
     334       242685 : RORouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
     335              :     myActiveRoute.clear();
     336       242685 :     myInsertStopEdgesAt = -1;
     337              :     // check whether the id is really necessary
     338              :     std::string rid;
     339       242685 :     if (myCurrentAlternatives != nullptr) {
     340       232293 :         myActiveRouteID = myCurrentAlternatives->getID();
     341       696879 :         rid =  "distribution '" + myCurrentAlternatives->getID() + "'";
     342        10392 :     } else if (myVehicleParameter != nullptr) {
     343              :         // ok, a vehicle is wrapping the route,
     344              :         //  we may use this vehicle's id as default
     345         3493 :         myVehicleParameter->routeid = myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
     346         3493 :         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     347            0 :             WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
     348              :         }
     349              :     } else {
     350         6899 :         bool ok = true;
     351         6899 :         myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     352         6899 :         if (!ok) {
     353           16 :             return;
     354              :         }
     355        20649 :         rid = "'" + myActiveRouteID + "'";
     356              :     }
     357       242669 :     if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
     358       704298 :         rid =  "for vehicle '" + myVehicleParameter->id + "'";
     359              :     }
     360       242669 :     bool ok = true;
     361       242669 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     362       485322 :         parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid, ok);
     363              :     }
     364       485338 :     myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
     365       242669 :     if (myActiveRouteRefID != "" && myNet.getRouteDef(myActiveRouteRefID) == nullptr) {
     366            0 :         myErrorOutput->inform("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
     367              :     }
     368       242669 :     if (myCurrentAlternatives != nullptr && !attrs.hasAttribute(SUMO_ATTR_PROB)) {
     369           48 :         WRITE_WARNINGF(TL("No probability for route %, using default."), rid);
     370              :     }
     371       242669 :     myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
     372       242669 :     if (ok && myActiveRouteProbability < 0) {
     373           24 :         myErrorOutput->inform("Invalid probability for route '" + myActiveRouteID + "'.");
     374              :     }
     375       242669 :     myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
     376       242669 :     ok = true;
     377       242669 :     myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
     378       242669 :     myActiveRoutePeriod = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CYCLETIME, myActiveRouteID.c_str(), ok, 0);
     379       242669 :     if (myActiveRouteRepeat > 0) {
     380              :         SUMOVehicleClass vClass = SVC_IGNORING;
     381            8 :         if (myVehicleParameter != nullptr) {
     382            0 :             SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     383            0 :             if (type != nullptr) {
     384            0 :                 vClass = type->vehicleClass;
     385              :             }
     386              :         }
     387            8 :         if (myActiveRoute.size() > 0 && !myActiveRoute.back()->isConnectedTo(*myActiveRoute.front(), vClass)) {
     388            0 :             myErrorOutput->inform("Disconnected route " + rid + " when repeating.");
     389              :         }
     390              :     }
     391       242669 :     myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
     392       242669 :     if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
     393           24 :         myErrorOutput->inform("Invalid cost for route '" + myActiveRouteID + "'.");
     394              :     }
     395              : }
     396              : 
     397              : 
     398              : void
     399         2088 : RORouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
     400              :     // currently unused
     401         2088 : }
     402              : 
     403              : 
     404              : void
     405         1377 : RORouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
     406              :     // currently unused
     407         1377 : }
     408              : 
     409              : 
     410              : void
     411        36802 : RORouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
     412              :     // currently unused
     413        36802 : }
     414              : 
     415              : 
     416              : void
     417       280747 : RORouteHandler::closeRoute(const bool mayBeDisconnected) {
     418       280747 :     const bool mustReroute = myActiveRoute.size() == 0 && myActiveRouteStops.size() != 0;
     419              :     if (mustReroute) {
     420              :         // implicit route from stops
     421           20 :         for (const SUMOVehicleParameter::Stop& stop : myActiveRouteStops) {
     422           16 :             ROEdge* edge = myNet.getEdge(stop.edge);
     423           16 :             myActiveRoute.push_back(edge);
     424              :         }
     425              :     }
     426       280747 :     if (myActiveRoute.size() == 0) {
     427          546 :         if (myActiveRouteRefID != "" && myCurrentAlternatives != nullptr) {
     428            8 :             myCurrentAlternatives->addAlternativeDef(myNet.getRouteDef(myActiveRouteRefID));
     429            4 :             myActiveRouteID = "";
     430              :             myActiveRouteRefID = "";
     431            4 :             return;
     432              :         }
     433          542 :         if (myVehicleParameter != nullptr) {
     434         1578 :             myErrorOutput->inform("The route for vehicle '" + myVehicleParameter->id + "' has no edges.");
     435              :         } else {
     436           48 :             myErrorOutput->inform("Route '" + myActiveRouteID + "' has no edges.");
     437              :         }
     438          542 :         myActiveRouteID = "";
     439              :         myActiveRouteStops.clear();
     440          542 :         return;
     441              :     }
     442       280201 :     if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
     443           60 :         myErrorOutput->inform("The routing information for vehicle '" + myVehicleParameter->id + "' is insufficient.");
     444           30 :         myActiveRouteID = "";
     445              :         myActiveRouteStops.clear();
     446           30 :         return;
     447              :     }
     448       765503 :     if (!mayBeDisconnected && OptionsCont::getOptions().exists("no-internal-links") && !OptionsCont::getOptions().getBool("no-internal-links")) {
     449              :         // fix internal edges which did not get parsed
     450              :         const ROEdge* last = nullptr;
     451              :         ConstROEdgeVector fullRoute;
     452      1549748 :         for (const ROEdge* roe : myActiveRoute) {
     453      1307085 :             if (last != nullptr) {
     454      2618147 :                 for (const ROEdge* intern : last->getSuccessors()) {
     455      1553725 :                     if (intern->isInternal() && intern->getSuccessors().size() == 1 && intern->getSuccessors().front() == roe) {
     456            0 :                         fullRoute.push_back(intern);
     457              :                     }
     458              :                 }
     459              :             }
     460      1307085 :             fullRoute.push_back(roe);
     461              :             last = roe;
     462              :         }
     463       242663 :         myActiveRoute = fullRoute;
     464       242663 :     }
     465       280171 :     if (myActiveRouteRepeat > 0) {
     466              :         // duplicate route
     467            8 :         ConstROEdgeVector tmpEdges = myActiveRoute;
     468            8 :         auto tmpStops = myActiveRouteStops;
     469           24 :         for (int i = 0; i < myActiveRouteRepeat; i++) {
     470           16 :             myActiveRoute.insert(myActiveRoute.begin(), tmpEdges.begin(), tmpEdges.end());
     471           80 :             for (SUMOVehicleParameter::Stop stop : tmpStops) {
     472           64 :                 if (stop.until > 0) {
     473           32 :                     if (myActiveRoutePeriod <= 0) {
     474            0 :                         const std::string description = myVehicleParameter != nullptr
     475            0 :                                                         ?  "for vehicle '" + myVehicleParameter->id + "'"
     476            0 :                                                         :  "'" + myActiveRouteID + "'";
     477            0 :                         throw ProcessError(TLF("Cannot repeat stops with 'until' in route % because no cycleTime is defined.", description));
     478              :                     }
     479           32 :                     stop.until += myActiveRoutePeriod * (i + 1);
     480           32 :                     stop.arrival += myActiveRoutePeriod * (i + 1);
     481              :                 }
     482           64 :                 myActiveRouteStops.push_back(stop);
     483           64 :             }
     484              :         }
     485            8 :     }
     486       280171 :     RORoute* route = new RORoute(myActiveRouteID, myCurrentCosts, myActiveRouteProbability, myActiveRoute,
     487       280171 :                                  myActiveRouteColor, myActiveRouteStops);
     488              :     myActiveRoute.clear();
     489       280171 :     if (myCurrentAlternatives == nullptr) {
     490        47882 :         if (myNet.getRouteDef(myActiveRouteID) != nullptr) {
     491            0 :             delete route;
     492            0 :             if (myVehicleParameter != nullptr) {
     493            0 :                 myErrorOutput->inform("Another route for vehicle '" + myVehicleParameter->id + "' exists.");
     494              :             } else {
     495            0 :                 myErrorOutput->inform("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
     496              :             }
     497              :             myActiveRouteID = "";
     498              :             myActiveRouteStops.clear();
     499            0 :             return;
     500              :         } else {
     501        47882 :             myCurrentAlternatives = new RORouteDef(myActiveRouteID, 0, mayBeDisconnected || myTryRepair, mayBeDisconnected);
     502        47882 :             myCurrentAlternatives->addLoadedAlternative(route);
     503        47882 :             myNet.addRouteDef(myCurrentAlternatives);
     504        47882 :             myCurrentAlternatives = nullptr;
     505              :         }
     506              :     } else {
     507       232289 :         myCurrentAlternatives->addLoadedAlternative(route);
     508              :     }
     509              :     myActiveRouteID = "";
     510              :     myActiveRouteStops.clear();
     511              : }
     512              : 
     513              : 
     514              : void
     515        90931 : RORouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
     516              :     // check whether the id is really necessary
     517        90931 :     bool ok = true;
     518              :     std::string id;
     519        90931 :     if (myVehicleParameter != nullptr) {
     520              :         // ok, a vehicle is wrapping the route,
     521              :         //  we may use this vehicle's id as default
     522        89907 :         myVehicleParameter->routeid = id = "!" + myVehicleParameter->id; // !!! document this
     523        89907 :         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     524            0 :             WRITE_WARNINGF(TL("Ids of internal route distributions are ignored (vehicle '%')."), myVehicleParameter->id);
     525              :         }
     526              :     } else {
     527         1024 :         id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     528         1024 :         if (!ok) {
     529              :             return;
     530              :         }
     531              :     }
     532              :     // try to get the index of the last element
     533        90923 :     int index = attrs.getOpt<int>(SUMO_ATTR_LAST, id.c_str(), ok, 0);
     534        90923 :     if (ok && index < 0) {
     535           16 :         myErrorOutput->inform("Negative index of a route alternative (id='" + id + "').");
     536            8 :         return;
     537              :     }
     538              :     // build the alternative cont
     539        90915 :     myCurrentAlternatives = new RORouteDef(id, index, myTryRepair, false);
     540        90915 :     if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
     541            0 :         ok = true;
     542            0 :         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, id.c_str(), ok));
     543            0 :         while (st.hasNext()) {
     544            0 :             const std::string routeID = st.next();
     545            0 :             const RORouteDef* route = myNet.getRouteDef(routeID);
     546            0 :             if (route == nullptr) {
     547            0 :                 myErrorOutput->inform("Unknown route '" + routeID + "' in distribution '" + id + "'.");
     548              :             } else {
     549            0 :                 myCurrentAlternatives->addAlternativeDef(route);
     550              :             }
     551              :         }
     552            0 :     }
     553              : }
     554              : 
     555              : 
     556              : void
     557        90915 : RORouteHandler::closeRouteDistribution() {
     558        90915 :     if (myCurrentAlternatives != nullptr) {
     559        90899 :         if (myCurrentAlternatives->getOverallProb() == 0) {
     560            0 :             myErrorOutput->inform("Route distribution '" + myCurrentAlternatives->getID() + "' is empty.");
     561            0 :             delete myCurrentAlternatives;
     562        90899 :         } else if (!myNet.addRouteDef(myCurrentAlternatives)) {
     563            0 :             myErrorOutput->inform("Another route (or distribution) with the id '" + myCurrentAlternatives->getID() + "' exists.");
     564            0 :             delete myCurrentAlternatives;
     565              :         }
     566        90899 :         myCurrentAlternatives = nullptr;
     567              :     }
     568        90915 : }
     569              : 
     570              : 
     571              : void
     572       130359 : RORouteHandler::closeVehicle() {
     573       130359 :     checkLastDepart();
     574              :     // get the vehicle id
     575       130359 :     if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN && myVehicleParameter->depart < myBegin) {
     576              :         return;
     577              :     }
     578              :     // get vehicle type
     579       130359 :     SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     580       130359 :     if (type == nullptr) {
     581          272 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
     582          136 :         type = myNet.getVehicleTypeSecure(DEFAULT_VTYPE_ID);
     583              :     } else {
     584       130223 :         if (!myKeepVTypeDist) {
     585              :             // fix the type id in case we used a distribution
     586       130219 :             myVehicleParameter->vtypeid = type->id;
     587              :         }
     588              :     }
     589       130359 :     if (type->vehicleClass == SVC_PEDESTRIAN) {
     590          120 :         WRITE_WARNINGF(TL("Vehicle type '%' with vClass=pedestrian should only be used for persons and not for vehicle '%'."), type->id, myVehicleParameter->id);
     591              :     }
     592              :     // get the route
     593       130359 :     RORouteDef* route = myNet.getRouteDef(myVehicleParameter->routeid);
     594       129995 :     if (route == nullptr) {
     595          728 :         myErrorOutput->inform("The route of the vehicle '" + myVehicleParameter->id + "' is not known.");
     596          364 :         return;
     597              :     }
     598       129995 :     if (MsgHandler::getErrorInstance()->wasInformed()) {
     599              :         return;
     600              :     }
     601       129327 :     const bool needCopy = route->getID()[0] != '!';
     602       129327 :     if (needCopy) {
     603         3694 :         route = route->copy("!" + myVehicleParameter->id, myVehicleParameter->depart);
     604              :     }
     605              :     // build the vehicle
     606       129327 :     ROVehicle* veh = new ROVehicle(*myVehicleParameter, route, type, &myNet, myErrorOutput);
     607       129327 :     if (myNet.addVehicle(myVehicleParameter->id, veh)) {
     608       129323 :         registerLastDepart();
     609            4 :     } else if (needCopy) {
     610            4 :         delete route;
     611              :     }
     612       129327 :     delete myVehicleParameter;
     613       129327 :     myVehicleParameter = nullptr;
     614              : }
     615              : 
     616              : 
     617              : void
     618         1807 : RORouteHandler::closeVType() {
     619         1807 :     if (myNet.addVehicleType(myCurrentVType)) {
     620         1807 :         if (myCurrentVTypeDistribution != nullptr) {
     621           32 :             myCurrentVTypeDistribution->add(myCurrentVType, myCurrentVType->defaultProbability);
     622              :         }
     623              :     }
     624         3614 :     if (OptionsCont::getOptions().isSet("restriction-params")) {
     625           12 :         const std::vector<std::string> paramKeys = OptionsCont::getOptions().getStringVector("restriction-params");
     626           12 :         myCurrentVType->cacheParamRestrictions(paramKeys);
     627           12 :     }
     628         1807 :     myCurrentVType = nullptr;
     629         1807 : }
     630              : 
     631              : 
     632              : void
     633         1331 : RORouteHandler::closePerson() {
     634         1331 :     SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     635         1331 :     if (type == nullptr) {
     636            0 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.");
     637            0 :         type = myNet.getVehicleTypeSecure(DEFAULT_PEDTYPE_ID);
     638              :     }
     639         1331 :     if (myActivePlan == nullptr || myActivePlan->empty()) {
     640           40 :         WRITE_WARNINGF(TL("Discarding person '%' because her plan is empty"), myVehicleParameter->id);
     641              :     } else {
     642         1311 :         ROPerson* person = new ROPerson(*myVehicleParameter, type);
     643         2742 :         for (ROPerson::PlanItem* item : *myActivePlan) {
     644         1431 :             person->getPlan().push_back(item);
     645              :         }
     646         1311 :         if (myNet.addPerson(person)) {
     647         1311 :             checkLastDepart();
     648         1311 :             registerLastDepart();
     649              :         }
     650              :     }
     651         1331 :     delete myVehicleParameter;
     652         1331 :     myVehicleParameter = nullptr;
     653         1331 :     delete myActivePlan;
     654         1331 :     myActivePlan = nullptr;
     655         1331 : }
     656              : 
     657              : 
     658              : void
     659           48 : RORouteHandler::closePersonFlow() {
     660              :     std::string typeID = DEFAULT_PEDTYPE_ID;
     661           48 :     if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
     662            0 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for personFlow '" + myVehicleParameter->id + "' is not known.");
     663              :     } else {
     664           48 :         typeID = myVehicleParameter->vtypeid;
     665              :     }
     666           48 :     if (myActivePlan == nullptr || myActivePlan->empty()) {
     667            0 :         WRITE_WARNINGF(TL("Discarding personFlow '%' because their plan is empty"), myVehicleParameter->id);
     668              :     } else {
     669           48 :         checkLastDepart();
     670              :         // instantiate all persons of this flow
     671              :         int i = 0;
     672           48 :         std::string baseID = myVehicleParameter->id;
     673           48 :         if (myVehicleParameter->repetitionProbability > 0) {
     674            4 :             if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
     675            0 :                 throw ProcessError(TLF("probabilistic personFlow '%' must specify end time", myVehicleParameter->id));
     676              :             } else {
     677           44 :                 for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
     678           40 :                     if (RandHelper::rand() < myVehicleParameter->repetitionProbability) {
     679           20 :                         addFlowPerson(typeID, t, baseID, i++);
     680              :                     }
     681              :                 }
     682              :             }
     683              :         } else {
     684           44 :             SUMOTime depart = myVehicleParameter->depart;
     685              :             // uniform sampling of departures from range is equivalent to poisson flow (encoded by negative offset)
     686           88 :             if (OptionsCont::getOptions().getBool("randomize-flows") && myVehicleParameter->repetitionOffset >= 0) {
     687              :                 std::vector<SUMOTime> departures;
     688            4 :                 const SUMOTime range = myVehicleParameter->repetitionNumber * myVehicleParameter->repetitionOffset;
     689           24 :                 for (int j = 0; j < myVehicleParameter->repetitionNumber; ++j) {
     690           20 :                     departures.push_back(depart + RandHelper::rand(range));
     691              :                 }
     692            4 :                 std::sort(departures.begin(), departures.end());
     693              :                 std::reverse(departures.begin(), departures.end());
     694           24 :                 for (; i < myVehicleParameter->repetitionNumber; i++) {
     695           20 :                     addFlowPerson(typeID, departures[i], baseID, i);
     696              :                     depart += myVehicleParameter->repetitionOffset;
     697              :                 }
     698            4 :             } else {
     699           40 :                 const bool triggered = myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED;
     700           40 :                 if (myVehicleParameter->repetitionOffset < 0) {
     701              :                     // poisson: randomize first depart
     702            4 :                     myVehicleParameter->incrementFlow(1);
     703              :                 }
     704          392 :                 for (; i < myVehicleParameter->repetitionNumber && (triggered || depart + myVehicleParameter->repetitionTotalOffset <= myVehicleParameter->repetitionEnd); i++) {
     705          352 :                     addFlowPerson(typeID, depart + myVehicleParameter->repetitionTotalOffset, baseID, i);
     706          352 :                     if (myVehicleParameter->departProcedure != DepartDefinition::TRIGGERED) {
     707          352 :                         myVehicleParameter->incrementFlow(1);
     708              :                     }
     709              :                 }
     710              :             }
     711              :         }
     712              :     }
     713           48 :     delete myVehicleParameter;
     714           48 :     myVehicleParameter = nullptr;
     715           96 :     for (ROPerson::PlanItem* const it : *myActivePlan) {
     716           48 :         delete it;
     717              :     }
     718           48 :     delete myActivePlan;
     719           48 :     myActivePlan = nullptr;
     720           48 : }
     721              : 
     722              : 
     723              : void
     724          392 : RORouteHandler::addFlowPerson(const std::string& typeID, SUMOTime depart, const std::string& baseID, int i) {
     725          392 :     SUMOVehicleParameter pars = *myVehicleParameter;
     726          784 :     pars.id = baseID + "." + toString(i);
     727          392 :     pars.depart = depart;
     728          392 :     const SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
     729          392 :     if (!myKeepVTypeDist) {
     730          392 :         pars.vtypeid = type->id;
     731              :     }
     732          392 :     ROPerson* person = new ROPerson(pars, type);
     733          784 :     for (ROPerson::PlanItem* item : *myActivePlan) {
     734          392 :         person->getPlan().push_back(item->clone());
     735              :     }
     736          392 :     if (myNet.addPerson(person)) {
     737          392 :         if (i == 0) {
     738           48 :             registerLastDepart();
     739              :         }
     740              :     }
     741          392 : }
     742              : 
     743              : 
     744              : void
     745           36 : RORouteHandler::closeContainer() {
     746           36 :     myActiveContainerPlan->closeTag();
     747           36 :     if (myActiveContainerPlanSize > 0) {
     748           36 :         myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
     749           36 :         checkLastDepart();
     750           36 :         registerLastDepart();
     751              :     } else {
     752            0 :         WRITE_WARNINGF(TL("Discarding container '%' because its plan is empty"), myVehicleParameter->id);
     753              :     }
     754           36 :     delete myVehicleParameter;
     755           36 :     myVehicleParameter = nullptr;
     756           36 :     delete myActiveContainerPlan;
     757           36 :     myActiveContainerPlan = nullptr;
     758           36 :     myActiveContainerPlanSize = 0;
     759           36 : }
     760              : 
     761              : 
     762           20 : void RORouteHandler::closeContainerFlow() {
     763           20 :     myActiveContainerPlan->closeTag();
     764           20 :     if (myActiveContainerPlanSize > 0) {
     765           20 :         myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
     766           20 :         checkLastDepart();
     767           20 :         registerLastDepart();
     768              :     } else {
     769            0 :         WRITE_WARNINGF(TL("Discarding containerFlow '%' because its plan is empty"), myVehicleParameter->id);
     770              :     }
     771           20 :     delete myVehicleParameter;
     772           20 :     myVehicleParameter = nullptr;
     773           20 :     delete myActiveContainerPlan;
     774           20 :     myActiveContainerPlan = nullptr;
     775           20 :     myActiveContainerPlanSize = 0;
     776           20 : }
     777              : 
     778              : 
     779              : void
     780         3465 : RORouteHandler::closeFlow() {
     781         3465 :     checkLastDepart();
     782              :     // @todo: consider myScale?
     783         3465 :     if (myVehicleParameter->repetitionNumber == 0) {
     784            8 :         delete myVehicleParameter;
     785            8 :         myVehicleParameter = nullptr;
     786            8 :         return;
     787              :     }
     788              :     // let's check whether vehicles had to depart before the simulation starts
     789         3457 :     myVehicleParameter->repetitionsDone = 0;
     790         3457 :     const SUMOTime offsetToBegin = myBegin - myVehicleParameter->depart;
     791         3641 :     while (myVehicleParameter->repetitionTotalOffset < offsetToBegin) {
     792          186 :         myVehicleParameter->incrementFlow(1);
     793          186 :         if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
     794            2 :             delete myVehicleParameter;
     795            2 :             myVehicleParameter = nullptr;
     796            2 :             return;
     797              :         }
     798              :     }
     799         3455 :     if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
     800           54 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
     801              :     }
     802         3455 :     if (myVehicleParameter->routeid[0] == '!' && myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
     803         1516 :         closeRoute(true);
     804              :     }
     805         3455 :     if (myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
     806           32 :         myErrorOutput->inform("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
     807           16 :         delete myVehicleParameter;
     808           16 :         myVehicleParameter = nullptr;
     809           16 :         return;
     810              :     }
     811         3439 :     myActiveRouteID = "";
     812         3439 :     if (!MsgHandler::getErrorInstance()->wasInformed()) {
     813         6860 :         if (myNet.addFlow(myVehicleParameter, OptionsCont::getOptions().getBool("randomize-flows"))) {
     814         3426 :             registerLastDepart();
     815              :         } else {
     816            8 :             myErrorOutput->inform("Another flow with the id '" + myVehicleParameter->id + "' exists.");
     817            4 :             delete myVehicleParameter;
     818              :         }
     819              :     } else {
     820            9 :         delete myVehicleParameter;
     821              :     }
     822         3439 :     myVehicleParameter = nullptr;
     823         3439 :     myInsertStopEdgesAt = -1;
     824              : }
     825              : 
     826              : 
     827              : void
     828        36546 : RORouteHandler::closeTrip() {
     829        36546 :     closeRoute(true);
     830        36546 :     closeVehicle();
     831        36546 : }
     832              : 
     833              : 
     834              : const SUMOVehicleParameter::Stop*
     835        10126 : RORouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, std::string& id, const SUMOVehicleParameter::Stop* stopParam) {
     836              :     // dummy stop parameter to hold the attributes
     837        10126 :     SUMOVehicleParameter::Stop stop;
     838        10126 :     if (stopParam != nullptr) {
     839         8731 :         stop = *stopParam;
     840              :     } else {
     841         1395 :         bool ok = true;
     842         2790 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
     843         1395 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
     844         1395 :         stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
     845         1395 :         stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
     846         1395 :         stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
     847         2790 :         stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
     848              :     }
     849              :     const SUMOVehicleParameter::Stop* toStop = nullptr;
     850        10126 :     if (stop.busstop != "") {
     851         1754 :         toStop = myNet.getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
     852              :         id = stop.busstop;
     853         1754 :         if (toStop == nullptr) {
     854            0 :             WRITE_ERROR("The busStop '" + stop.busstop + "' is not known" + errorSuffix);
     855              :         }
     856         8372 :     } else if (stop.containerstop != "") {
     857           32 :         toStop = myNet.getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
     858              :         id = stop.containerstop;
     859           32 :         if (toStop == nullptr) {
     860            0 :             WRITE_ERROR("The containerStop '" + stop.containerstop + "' is not known" + errorSuffix);
     861              :         }
     862         8340 :     } else if (stop.parkingarea != "") {
     863           24 :         toStop = myNet.getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
     864              :         id = stop.parkingarea;
     865           24 :         if (toStop == nullptr) {
     866            0 :             WRITE_ERROR("The parkingArea '" + stop.parkingarea + "' is not known" + errorSuffix);
     867              :         }
     868         8316 :     } else if (stop.chargingStation != "") {
     869              :         // ok, we have a charging station
     870           20 :         toStop = myNet.getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
     871              :         id = stop.chargingStation;
     872           20 :         if (toStop == nullptr) {
     873            0 :             WRITE_ERROR("The chargingStation '" + stop.chargingStation + "' is not known" + errorSuffix);
     874              :         }
     875         8296 :     } else if (stop.overheadWireSegment != "") {
     876              :         // ok, we have an overhead wire segment
     877            0 :         toStop = myNet.getStoppingPlace(stop.overheadWireSegment, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
     878              :         id = stop.overheadWireSegment;
     879            0 :         if (toStop == nullptr) {
     880            0 :             WRITE_ERROR("The overhead wire segment '" + stop.overheadWireSegment + "' is not known" + errorSuffix);
     881              :         }
     882              :     }
     883        10126 :     return toStop;
     884        10126 : }
     885              : 
     886              : Parameterised*
     887         8747 : RORouteHandler::addStop(const SUMOSAXAttributes& attrs) {
     888              :     Parameterised* result = nullptr;
     889         8747 :     if (myActiveContainerPlan != nullptr) {
     890           16 :         myActiveContainerPlan->openTag(SUMO_TAG_STOP);
     891           16 :         (*myActiveContainerPlan) << attrs;
     892           16 :         myActiveContainerPlan->closeTag();
     893           16 :         myActiveContainerPlanSize++;
     894           16 :         return result;
     895              :     }
     896              :     std::string errorSuffix;
     897         8731 :     if (myActivePlan != nullptr) {
     898          324 :         errorSuffix = " in person '" + myVehicleParameter->id + "'.";
     899              :     } else if (myActiveContainerPlan != nullptr) {
     900              :         errorSuffix = " in container '" + myVehicleParameter->id + "'.";
     901         8623 :     } else if (myVehicleParameter != nullptr) {
     902        22674 :         errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
     903              :     } else {
     904         3195 :         errorSuffix = " in route '" + myActiveRouteID + "'.";
     905              :     }
     906         8731 :     SUMOVehicleParameter::Stop stop;
     907        17462 :     bool ok = parseStop(stop, attrs, errorSuffix, myErrorOutput);
     908         8731 :     if (!ok) {
     909              :         return result;
     910              :     }
     911              :     // try to parse the assigned bus stop
     912         8731 :     const ROEdge* edge = nullptr;
     913              :     std::string stoppingPlaceID;
     914         8731 :     const SUMOVehicleParameter::Stop* stoppingPlace = retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID, &stop);
     915              :     bool hasPos = false;
     916         8731 :     if (stoppingPlace != nullptr) {
     917         1666 :         stop.lane = stoppingPlace->lane;
     918         1666 :         stop.endPos = stoppingPlace->endPos;
     919         1666 :         stop.startPos = stoppingPlace->startPos;
     920         6664 :         edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
     921              :     } else {
     922              :         // no, the lane and the position should be given
     923         7065 :         stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
     924         7065 :         stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
     925         7065 :         if (ok && stop.edge != "") {
     926           28 :             edge = myNet.getEdge(stop.edge);
     927           28 :             if (edge == nullptr) {
     928            0 :                 myErrorOutput->inform("The edge '" + stop.edge + "' for a stop is not known" + errorSuffix);
     929            0 :                 return result;
     930              :             }
     931         7037 :         } else if (ok && stop.lane != "") {
     932        21015 :             edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
     933         7005 :             if (edge == nullptr) {
     934            0 :                 myErrorOutput->inform("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
     935            0 :                 return result;
     936              :             }
     937           32 :         } else if (ok && ((attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y))
     938           20 :                           || (attrs.hasAttribute(SUMO_ATTR_LON) && attrs.hasAttribute(SUMO_ATTR_LAT)))) {
     939              :             Position pos;
     940              :             bool geo = false;
     941           32 :             if (attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y)) {
     942           12 :                 pos = Position(attrs.get<double>(SUMO_ATTR_X, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_Y, myVehicleParameter->id.c_str(), ok));
     943              :             } else {
     944           20 :                 pos = Position(attrs.get<double>(SUMO_ATTR_LON, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_LAT, myVehicleParameter->id.c_str(), ok));
     945              :                 geo = true;
     946              :             }
     947           32 :             PositionVector positions;
     948           32 :             positions.push_back(pos);
     949              :             ConstROEdgeVector geoEdges;
     950              :             SUMOVehicleClass vClass = SVC_PASSENGER;
     951           32 :             if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
     952           32 :                 SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     953           32 :                 if (type != nullptr) {
     954           32 :                     vClass = type->vehicleClass;
     955              :                 }
     956              :             }
     957           32 :             parseGeoEdges(positions, geo, vClass, geoEdges, myVehicleParameter->id, true, ok, true);
     958           32 :             if (ok) {
     959           32 :                 edge = geoEdges.front();
     960              :                 hasPos = true;
     961           32 :                 if (geo) {
     962           20 :                     GeoConvHelper::getFinal().x2cartesian_const(pos);
     963              :                 }
     964           32 :                 stop.parametersSet |= STOP_END_SET;
     965           32 :                 stop.endPos = edge->getLanes()[0]->getShape().nearest_offset_to_point2D(pos, false);
     966              :             } else {
     967              :                 return result;
     968              :             }
     969           32 :         } else if (!ok || (stop.lane == "" && stop.edge == "")) {
     970            0 :             myErrorOutput->inform("A stop must be placed on a bus stop, a container stop, a parking area, an edge or a lane" + errorSuffix);
     971            0 :             return result;
     972              :         }
     973              :         if (!hasPos) {
     974         7033 :             stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
     975              :         }
     976         7065 :         stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, stop.endPos - 2 * POSITION_EPS);
     977         7086 :         const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS));
     978         7065 :         const double endPosOffset = edge->isInternal() ? edge->getNormalBefore()->getLength() : 0;
     979         7065 :         if (!ok || (checkStopPos(stop.startPos, stop.endPos, edge->getLength() + endPosOffset, POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
     980            0 :             myErrorOutput->inform("Invalid start or end position for stop" + errorSuffix);
     981            0 :             return result;
     982              :         }
     983              :     }
     984         8731 :     stop.edge = edge->getID();
     985         8731 :     if (myActivePlan != nullptr) {
     986          108 :         ROPerson::addStop(*myActivePlan, stop, edge);
     987          108 :         result = myActivePlan->back()->getStopParameters();
     988         8623 :     } else if (myVehicleParameter != nullptr) {
     989         7558 :         myVehicleParameter->stops.push_back(stop);
     990         7558 :         result = &myVehicleParameter->stops.back();
     991              :     } else {
     992         1065 :         myActiveRouteStops.push_back(stop);
     993              :         result = &myActiveRouteStops.back();
     994              :     }
     995         8731 :     if (myInsertStopEdgesAt >= 0) {
     996         3130 :         myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
     997         3130 :         myInsertStopEdgesAt++;
     998              :     }
     999              :     return result;
    1000         8731 : }
    1001              : 
    1002              : 
    1003              : void
    1004           64 : RORouteHandler::addRide(const SUMOSAXAttributes& attrs) {
    1005           64 :     bool ok = true;
    1006           64 :     std::vector<ROPerson::PlanItem*>& plan = *myActivePlan;
    1007           64 :     const std::string pid = myVehicleParameter->id;
    1008              : 
    1009              :     const ROEdge* from = nullptr;
    1010              :     const ROEdge* to = nullptr;
    1011           64 :     parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
    1012           72 :     if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
    1013           72 :             || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
    1014           60 :         if (ok) {
    1015           60 :             from = myActiveRoute.front();
    1016              :         }
    1017            4 :     } else if (plan.empty()) {
    1018            0 :         myErrorOutput->inform("The start edge for person '" + pid + "' is not known.");
    1019            0 :         return;
    1020              :     }
    1021              :     std::string stoppingPlaceID;
    1022          128 :     const SUMOVehicleParameter::Stop* stop = retrieveStoppingPlace(attrs, " for ride of person '" + myVehicleParameter->id + "'", stoppingPlaceID);
    1023           64 :     if (stop != nullptr) {
    1024           84 :         to = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop->lane));
    1025              :     } else {
    1026           40 :         if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
    1027           40 :                 || attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
    1028           36 :             to = myActiveRoute.back();
    1029              :         } else {
    1030            0 :             myErrorOutput->inform("The to edge is missing within a ride of '" + myVehicleParameter->id + "'.");
    1031            0 :             return;
    1032              :         }
    1033              :     }
    1034           64 :     double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
    1035              :                         stop == nullptr ? std::numeric_limits<double>::infinity() : stop->endPos);
    1036           64 :     const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
    1037          128 :     const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, pid.c_str(), ok, "");
    1038              : 
    1039           64 :     if (plan.empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
    1040           20 :         StringTokenizer st(desc);
    1041           20 :         if (st.size() != 1) {
    1042            0 :             myErrorOutput->inform("Triggered departure for person '" + pid + "' requires a unique lines value.");
    1043            0 :             return;
    1044              :         }
    1045           20 :         const std::string vehID = st.front();
    1046           20 :         if (!myNet.knowsVehicle(vehID)) {
    1047           16 :             myErrorOutput->inform("Unknown vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
    1048            8 :             return;
    1049              :         }
    1050           12 :         SUMOTime vehDepart = myNet.getDeparture(vehID);
    1051           12 :         if (vehDepart == -1) {
    1052            0 :             myErrorOutput->inform("Cannot use triggered vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
    1053            0 :             return;
    1054              :         }
    1055           12 :         myVehicleParameter->depart = vehDepart + 1; // write person after vehicle
    1056           20 :     }
    1057           56 :     ROPerson::addRide(plan, from, to, desc, arrivalPos, stoppingPlaceID, group);
    1058              : }
    1059              : 
    1060              : 
    1061              : void
    1062           32 : RORouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
    1063           32 :     if (myActiveContainerPlan != nullptr && myActiveContainerPlanSize == 0 && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
    1064           12 :         bool ok = true;
    1065              :         const std::string pid = myVehicleParameter->id;
    1066           12 :         const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
    1067           16 :         StringTokenizer st(desc);
    1068           12 :         if (st.size() != 1) {
    1069            0 :             throw ProcessError(TLF("Triggered departure for container '%' requires a unique lines value.", pid));
    1070              :         }
    1071           12 :         const std::string vehID = st.front();
    1072           12 :         if (!myNet.knowsVehicle(vehID)) {
    1073            8 :             throw ProcessError("Unknown vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
    1074              :         }
    1075            8 :         SUMOTime vehDepart = myNet.getDeparture(vehID);
    1076            8 :         if (vehDepart == -1) {
    1077            0 :             throw ProcessError("Cannot use triggered vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
    1078              :         }
    1079            8 :         myVehicleParameter->depart = vehDepart + 1; // write container after vehicle
    1080           12 :     }
    1081           28 : }
    1082              : 
    1083              : 
    1084              : void
    1085           32 : RORouteHandler::addTranship(const SUMOSAXAttributes& /*attrs*/) {
    1086           32 : }
    1087              : 
    1088              : 
    1089              : void
    1090       342463 : RORouteHandler::parseEdges(const std::string& desc, ConstROEdgeVector& into,
    1091              :                            const std::string& rid, bool& ok) {
    1092      2065846 :     for (StringTokenizer st(desc); st.hasNext();) {
    1093      1380920 :         const std::string id = st.next();
    1094      1380920 :         const ROEdge* edge = myNet.getEdge(id);
    1095      1380920 :         if (edge == nullptr) {
    1096           42 :             myErrorOutput->inform("The edge '" + id + "' within the route " + rid + " is not known.");
    1097           21 :             ok = false;
    1098              :         } else {
    1099      1380899 :             into.push_back(edge);
    1100              :         }
    1101       342463 :     }
    1102       342463 : }
    1103              : 
    1104              : 
    1105              : void
    1106         1259 : RORouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
    1107              :                                    const ROEdge* /*fromEdge*/, const ROEdge*& toEdge,
    1108              :                                    double& departPos, double& arrivalPos, std::string& busStopID,
    1109              :                                    const ROPerson::PlanItem* const lastStage, bool& ok) {
    1110         1259 :     const std::string description = "walk or personTrip of '" + personID + "'.";
    1111         1259 :     if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
    1112            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."));
    1113              :     }
    1114         1259 :     departPos = myVehicleParameter->departPos;
    1115         1259 :     if (lastStage != nullptr) {
    1116           76 :         departPos = lastStage->getDestinationPos();
    1117              :     }
    1118              : 
    1119         1259 :     busStopID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
    1120              : 
    1121         1259 :     const SUMOVehicleParameter::Stop* bs = retrieveStoppingPlace(attrs, description, busStopID);
    1122         1259 :     if (bs != nullptr) {
    1123          336 :         toEdge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(bs->lane));
    1124          112 :         arrivalPos = (bs->startPos + bs->endPos) / 2;
    1125              :     }
    1126         1259 :     if (toEdge != nullptr) {
    1127         1259 :         if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1128           36 :             arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS,
    1129           36 :                          myHardFail, description, toEdge->getLength(),
    1130           72 :                          attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok));
    1131              :         }
    1132              :     } else {
    1133            0 :         throw ProcessError(TLF("No destination edge for %.", description));
    1134              :     }
    1135         1259 : }
    1136              : 
    1137              : 
    1138              : void
    1139         1259 : RORouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
    1140         1259 :     bool ok = true;
    1141         1259 :     const char* const id = myVehicleParameter->id.c_str();
    1142              :     assert(!attrs.hasAttribute(SUMO_ATTR_EDGES));
    1143              :     const ROEdge* from = nullptr;
    1144         1259 :     const ROEdge* to = nullptr;
    1145         1259 :     parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
    1146         1259 :     myInsertStopEdgesAt = -1;
    1147         1513 :     if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
    1148         1347 :             || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
    1149         1187 :         if (ok) {
    1150         1175 :             from = myActiveRoute.front();
    1151              :         }
    1152           72 :     } else if (myActivePlan->empty()) {
    1153            0 :         throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
    1154              :     } else {
    1155           72 :         from = myActivePlan->back()->getDestination();
    1156              :     }
    1157         1549 :     if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
    1158         1387 :             || attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
    1159         1147 :         to = myActiveRoute.back();
    1160              :     } // else, to may also be derived from stopping place
    1161              : 
    1162         1259 :     const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
    1163         1259 :     if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1164            0 :         throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1165              :     }
    1166              : 
    1167         1259 :     double departPos = 0;
    1168         1259 :     double arrivalPos = std::numeric_limits<double>::infinity();
    1169              :     std::string busStopID;
    1170         1259 :     const ROPerson::PlanItem* const lastStage = myActivePlan->empty() ? nullptr : myActivePlan->back();
    1171         1259 :     parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, busStopID, lastStage, ok);
    1172              : 
    1173         1259 :     const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
    1174         2518 :     const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, "");
    1175              :     SVCPermissions modeSet = 0;
    1176         3705 :     for (StringTokenizer st(modes); st.hasNext();) {
    1177         1187 :         const std::string mode = st.next();
    1178         1187 :         if (mode == "car") {
    1179          390 :             modeSet |= SVC_PASSENGER;
    1180          797 :         } else if (mode == "taxi") {
    1181           48 :             modeSet |= SVC_TAXI;
    1182          749 :         } else if (mode == "bicycle") {
    1183           12 :             modeSet |= SVC_BICYCLE;
    1184          737 :         } else if (mode == "public") {
    1185          737 :             modeSet |= SVC_BUS;
    1186              :         } else {
    1187            0 :             throw InvalidArgument("Unknown person mode '" + mode + "'.");
    1188              :         }
    1189         1259 :     }
    1190         1259 :     const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, "");
    1191         1259 :     double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
    1192         1259 :     if (ok) {
    1193         1247 :         const std::string originStopID = myActivePlan->empty() ?  "" : myActivePlan->back()->getStopDest();
    1194         1247 :         ROPerson::addTrip(*myActivePlan, myVehicleParameter->id, from, to, modeSet, types,
    1195              :                           departPos, originStopID, arrivalPos, busStopID, walkFactor, group);
    1196         1247 :         myParamStack.push_back(myActivePlan->back());
    1197              :     }
    1198         1259 : }
    1199              : 
    1200              : 
    1201              : void
    1202          236 : RORouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
    1203              :     // parse walks from->to as person trips
    1204          236 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1205              :         // XXX allow --repair?
    1206           72 :         bool ok = true;
    1207           72 :         if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1208           12 :             const std::string routeID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
    1209           12 :             RORouteDef* routeDef = myNet.getRouteDef(routeID);
    1210           12 :             const RORoute* route = routeDef != nullptr ? routeDef->getFirstRoute() : nullptr;
    1211           12 :             if (route == nullptr) {
    1212            0 :                 throw ProcessError("The route '" + routeID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
    1213              :             }
    1214           12 :             myActiveRoute = route->getEdgeVector();
    1215              :         } else {
    1216              :             myActiveRoute.clear();
    1217          240 :             parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, " walk for person '" + myVehicleParameter->id + "'", ok);
    1218              :         }
    1219           72 :         const char* const objId = myVehicleParameter->id.c_str();
    1220           72 :         const double duration = attrs.getOpt<double>(SUMO_ATTR_DURATION, objId, ok, -1);
    1221           72 :         if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1222            0 :             throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1223              :         }
    1224           72 :         const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, objId, ok, -1.);
    1225           72 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
    1226            0 :             throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
    1227              :         }
    1228              :         double departPos = 0.;
    1229              :         double arrivalPos = std::numeric_limits<double>::infinity();
    1230           72 :         if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
    1231            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."));
    1232              :         }
    1233           72 :         if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1234           32 :             arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, objId, myActiveRoute.back()->getLength(), attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, objId, ok));
    1235              :         }
    1236              :         std::string stoppingPlaceID;
    1237           72 :         const std::string errorSuffix = " for walk of person '" + myVehicleParameter->id + "'";
    1238           72 :         retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID);
    1239           72 :         if (ok) {
    1240           72 :             ROPerson::addWalk(*myActivePlan, myActiveRoute, duration, speed, departPos, arrivalPos, stoppingPlaceID);
    1241           72 :             myParamStack.push_back(myActivePlan->back());
    1242              :         }
    1243              :     } else {
    1244          164 :         addPersonTrip(attrs);
    1245              :     }
    1246          236 : }
    1247              : 
    1248              : 
    1249              : void
    1250           77 : RORouteHandler::initLaneTree(NamedRTree* tree) {
    1251         6214 :     for (const auto& edgeItem : myNet.getEdgeMap()) {
    1252        11599 :         for (ROLane* lane : edgeItem.second->getLanes()) {
    1253         5462 :             Boundary b = lane->getShape().getBoxBoundary();
    1254         5462 :             const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
    1255         5462 :             const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
    1256         5462 :             tree->Insert(cmin, cmax, lane);
    1257         5462 :         }
    1258              :     }
    1259           77 : }
    1260              : 
    1261              : 
    1262              : ROEdge*
    1263          512 : RORouteHandler::retrieveEdge(const std::string& id) {
    1264          512 :     return myNet.getEdge(id);
    1265              : }
    1266              : 
    1267              : bool
    1268       135239 : RORouteHandler::checkLastDepart() {
    1269       135239 :     if (!myUnsortedInput) {
    1270       134418 :         return SUMORouteHandler::checkLastDepart();
    1271              :     }
    1272              :     return true;
    1273              : }
    1274              : 
    1275              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1