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

Generated by: LCOV version 2.0-1