LCOV - code coverage report
Current view: top level - src/router - RORouteHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 87.7 % 822 721
Test Date: 2026-07-26 16:30:20 Functions: 100.0 % 37 37

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    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         3922 : RORouteHandler::RORouteHandler(RONet& net, const std::string& file,
      52              :                                const bool tryRepair,
      53              :                                const bool emptyDestinationsAllowed,
      54              :                                const bool ignoreErrors,
      55         3922 :                                const bool checkSchema) :
      56              :     SUMORouteHandler(file, checkSchema ? "routes" : "", true),
      57         7844 :     MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
      58         7844 :                OptionsCont::getOptions().getBool("mapmatch.taz"),
      59         3922 :                OptionsCont::getOptions().getFloat("mapmatch.distance"),
      60         3922 :                ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
      61         3922 :     myNet(net),
      62         3922 :     myActiveRouteRepeat(0),
      63         3922 :     myActiveRoutePeriod(0),
      64         3922 :     myActivePlan(nullptr),
      65         3922 :     myActiveContainerPlan(nullptr),
      66         3922 :     myActiveContainerPlanSize(0),
      67         3922 :     myTryRepair(tryRepair),
      68         3922 :     myEmptyDestinationsAllowed(emptyDestinationsAllowed),
      69         3922 :     myErrorOutput(ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
      70         3922 :     myBegin(string2time(OptionsCont::getOptions().getString("begin"))),
      71         3922 :     myKeepVTypeDist(OptionsCont::getOptions().getBool("keep-vtype-distributions")),
      72         7844 :     myUnsortedInput(OptionsCont::getOptions().exists("unsorted-input") && OptionsCont::getOptions().getBool("unsorted-input")),
      73         7729 :     myWriteFlows(OptionsCont::getOptions().exists("keep-flows") && OptionsCont::getOptions().getBool("keep-flows")),
      74         3922 :     myCurrentVTypeDistribution(nullptr),
      75         3922 :     myCurrentAlternatives(nullptr),
      76         3922 :     myUseTaz(OptionsCont::getOptions().getBool("with-taz")),
      77         3922 :     myWriteJunctions(OptionsCont::getOptions().exists("write-trips")
      78         7729 :                      && OptionsCont::getOptions().getBool("write-trips")
      79        24401 :                      && OptionsCont::getOptions().getBool("write-trips.junctions")) {
      80         3922 :     myActiveRoute.reserve(100);
      81         3922 : }
      82              : 
      83              : 
      84         7844 : RORouteHandler::~RORouteHandler() {
      85         3922 :     delete myCurrentAlternatives;
      86        11766 : }
      87              : 
      88              : 
      89              : void
      90         3767 : RORouteHandler::deleteActivePlanAndVehicleParameter() {
      91         3767 :     if (myActivePlan != nullptr) {
      92         3613 :         for (ROPerson::PlanItem* const it : *myActivePlan) {
      93          194 :             delete it;
      94              :         }
      95         3419 :         delete myActivePlan;
      96         3419 :         myActivePlan = nullptr;
      97              :     }
      98         3767 :     delete myActiveContainerPlan;
      99         3767 :     myActiveContainerPlan = nullptr;
     100         3767 :     delete myVehicleParameter;
     101         3767 :     myVehicleParameter = nullptr;
     102         3767 : }
     103              : 
     104              : 
     105              : void
     106       191893 : RORouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs, bool& ok) {
     107       191893 :     const std::string element = toString(tag);
     108              :     myActiveRoute.clear();
     109       191893 :     bool useTaz = myUseTaz;
     110       191893 :     if (myUseTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
     111            0 :         WRITE_WARNINGF(TL("Taz usage was requested but no taz present in % '%'!"), element, myVehicleParameter->id);
     112              :         useTaz = false;
     113              :     }
     114              :     SUMOVehicleClass vClass = SVC_PASSENGER;
     115       191893 :     if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
     116       191837 :         SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     117       191837 :         if (type != nullptr) {
     118       191763 :             vClass = type->vehicleClass;
     119              :         }
     120              :     }
     121              :     // from-attributes
     122       191893 :     const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
     123       207859 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_FROM) && !attrs.hasAttribute(SUMO_ATTR_FROMXY) && !attrs.hasAttribute(SUMO_ATTR_FROMLONLAT))) &&
     124        31896 :             (attrs.hasAttribute(SUMO_ATTR_FROM_TAZ) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION))) {
     125        14962 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION);
     126        14998 :         const std::string tazType = useJunction ? "junction" : "taz";
     127        14998 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM_TAZ, myVehicleParameter->id.c_str(), ok, true);
     128        29924 :         const ROEdge* fromTaz = myNet.getEdge(tazID + "-source");
     129        14962 :         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        14954 :         } 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        14954 :             myActiveRoute.push_back(fromTaz);
     138        14954 :             if (useJunction && tag != SUMO_TAG_PERSON && !myWriteJunctions) {
     139         7236 :                 myVehicleParameter->fromTaz = tazID;
     140         7236 :                 myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
     141              :             }
     142              :         }
     143       176931 :     } 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       176829 :     } 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       353610 :         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok), myActiveRoute, rid, ok);
     157              :     }
     158       191893 :     if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_VIALONLAT) && !attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
     159       177252 :         myInsertStopEdgesAt = (int)myActiveRoute.size();
     160              :     }
     161              : 
     162              :     // via-attributes
     163              :     ConstROEdgeVector viaEdges;
     164       191893 :     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       191861 :     } 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       191857 :     } 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       383698 :         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true), viaEdges, rid, ok);
     180              :     }
     181       225493 :     for (const ROEdge* e : viaEdges) {
     182        33600 :         myActiveRoute.push_back(e);
     183        33600 :         myVehicleParameter->via.push_back(e->getID());
     184              :     }
     185              : 
     186              :     // to-attributes
     187       208725 :     if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_TO) && !attrs.hasAttribute(SUMO_ATTR_TOXY) && !attrs.hasAttribute(SUMO_ATTR_TOLONLAT))) &&
     188        33628 :             (attrs.hasAttribute(SUMO_ATTR_TO_TAZ) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION))) {
     189        14950 :         const bool useJunction = attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION);
     190        14986 :         const std::string tazType = useJunction ? "junction" : "taz";
     191        14986 :         const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO_TAZ, myVehicleParameter->id.c_str(), ok, true);
     192        29900 :         const ROEdge* toTaz = myNet.getEdge(tazID + "-sink");
     193        14950 :         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        14950 :         } 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        14950 :             myActiveRoute.push_back(toTaz);
     202        14950 :             if (useJunction && tag != SUMO_TAG_PERSON && !myWriteJunctions) {
     203         7236 :                 myVehicleParameter->toTaz = tazID;
     204         7236 :                 myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
     205              :             }
     206              :         }
     207       176943 :     } 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       176841 :     } 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       353634 :         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true), myActiveRoute, rid, ok);
     221              :     }
     222       191893 :     myActiveRouteID = "!" + myVehicleParameter->id;
     223       191893 :     if (myVehicleParameter->routeid == "") {
     224              :         myVehicleParameter->routeid = myActiveRouteID;
     225              :     }
     226       383786 : }
     227              : 
     228              : 
     229              : void
     230       629248 : RORouteHandler::myStartElement(int element,
     231              :                                const SUMOSAXAttributes& attrs) {
     232              :     try {
     233       629248 :         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       629244 :         } 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       629240 :         SUMORouteHandler::myStartElement(element, attrs);
     239       628900 :         bool ok = true;
     240       628900 :         switch (element) {
     241         3419 :             case SUMO_TAG_PERSON:
     242              :             case SUMO_TAG_PERSONFLOW: {
     243         3419 :                 myActivePlan = new std::vector<ROPerson::PlanItem*>();
     244         3419 :                 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         2605 :             case SUMO_TAG_FLOW:
     267         2605 :                 myActiveRouteProbability = DEFAULT_VEH_PROB;
     268         2605 :                 parseFromViaTo((SumoXMLTag)element, attrs, ok);
     269              :                 break;
     270       185865 :             case SUMO_TAG_TRIP:
     271       185865 :                 myActiveRouteProbability = DEFAULT_VEH_PROB;
     272       185865 :                 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       628896 : }
     282              : 
     283              : 
     284              : void
     285           92 : RORouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
     286           92 :     bool ok = true;
     287           92 :     myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     288           92 :     if (ok) {
     289           92 :         myCurrentVTypeDistribution = new RandomDistributor<SUMOVTypeParameter*>();
     290           92 :         if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
     291              :             std::vector<double> probs;
     292           48 :             if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
     293           16 :                 StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentVTypeDistributionID.c_str(), ok));
     294           48 :                 while (st.hasNext()) {
     295           64 :                     probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
     296              :                 }
     297           16 :             }
     298           48 :             const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
     299           48 :             StringTokenizer st(vTypes);
     300           48 :             int probIndex = 0;
     301          120 :             while (st.hasNext()) {
     302           72 :                 const std::string typeID = st.next();
     303           72 :                 const RandomDistributor<SUMOVTypeParameter*>* const dist = myNet.getVTypeDistribution(typeID);
     304              :                 if (dist != nullptr) {
     305           16 :                     const double distProb = ((int)probs.size() > probIndex ? probs[probIndex] : 1.) / dist->getOverallProb();
     306              :                     std::vector<double>::const_iterator probIt = dist->getProbs().begin();
     307           48 :                     for (SUMOVTypeParameter* const type : dist->getVals()) {
     308           32 :                         myCurrentVTypeDistribution->add(type, distProb * *probIt);
     309              :                         probIt++;
     310              :                     }
     311              :                 } else {
     312           56 :                     SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
     313           56 :                     if (type == nullptr) {
     314           24 :                         myErrorOutput->inform("Unknown vehicle type '" + typeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
     315              :                     } else {
     316           48 :                         const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : type->defaultProbability);
     317           48 :                         myCurrentVTypeDistribution->add(type, prob);
     318              :                     }
     319              :                 }
     320           72 :                 probIndex++;
     321              :             }
     322           48 :             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           96 :         }
     327              :     }
     328           92 : }
     329              : 
     330              : 
     331              : void
     332           92 : RORouteHandler::closeVehicleTypeDistribution() {
     333           92 :     if (myCurrentVTypeDistribution != nullptr) {
     334           92 :         if (myCurrentVTypeDistribution->getOverallProb() == 0) {
     335            8 :             delete myCurrentVTypeDistribution;
     336           24 :             myErrorOutput->inform("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
     337           84 :         } 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           92 :         myCurrentVTypeDistribution = nullptr;
     342              :     }
     343           92 : }
     344              : 
     345              : 
     346              : void
     347       235035 : RORouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
     348              :     myActiveRoute.clear();
     349       235035 :     myInsertStopEdgesAt = -1;
     350              :     // check whether the id is really necessary
     351              :     std::string rid;
     352       235035 :     if (myCurrentAlternatives != nullptr) {
     353       232607 :         myActiveRouteID = myCurrentAlternatives->getID();
     354       697821 :         rid =  "distribution '" + myCurrentAlternatives->getID() + "'";
     355         2428 :     } else if (myVehicleParameter != nullptr) {
     356              :         // ok, a vehicle is wrapping the route,
     357              :         //  we may use this vehicle's id as default
     358         1403 :         myVehicleParameter->routeid = myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
     359         1403 :         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     360            0 :             WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
     361              :         }
     362              :     } else {
     363         1025 :         bool ok = true;
     364         1025 :         myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     365         1025 :         if (!ok) {
     366           16 :             return;
     367              :         }
     368         3027 :         rid = "'" + myActiveRouteID + "'";
     369              :     }
     370       235019 :     if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
     371       698970 :         rid =  "for vehicle '" + myVehicleParameter->id + "'";
     372              :     }
     373       235019 :     bool ok = true;
     374       235019 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     375       470022 :         parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid, ok);
     376              :     }
     377       470038 :     myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
     378       235019 :     if (myActiveRouteRefID != "" && myNet.getRouteDef(myActiveRouteRefID) == nullptr) {
     379            0 :         myErrorOutput->inform("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
     380              :     }
     381       235019 :     if (myCurrentAlternatives != nullptr && !attrs.hasAttribute(SUMO_ATTR_PROB)) {
     382           48 :         WRITE_WARNINGF(TL("No probability for route %, using default."), rid);
     383              :     }
     384       235019 :     myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
     385       235019 :     if (ok && myActiveRouteProbability < 0) {
     386           24 :         myErrorOutput->inform("Invalid probability for route '" + myActiveRouteID + "'.");
     387              :     }
     388       235019 :     myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
     389       235019 :     ok = true;
     390       235019 :     myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
     391       235019 :     myActiveRoutePeriod = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CYCLETIME, myActiveRouteID.c_str(), ok, 0);
     392       235019 :     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       235019 :     myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
     405       235019 :     if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
     406           24 :         myErrorOutput->inform("Invalid cost for route '" + myActiveRouteID + "'.");
     407              :     }
     408              : }
     409              : 
     410              : 
     411              : void
     412         1986 : RORouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
     413              :     // currently unused
     414         1986 : }
     415              : 
     416              : 
     417              : void
     418          619 : RORouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
     419              :     // currently unused
     420          619 : }
     421              : 
     422              : 
     423              : void
     424       185865 : RORouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
     425              :     // currently unused
     426       185865 : }
     427              : 
     428              : 
     429              : void
     430       422802 : RORouteHandler::closeRoute(const bool mayBeDisconnected) {
     431       422802 :     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       422802 :     if (myActiveRoute.size() == 0) {
     440           24 :         if (myActiveRouteRefID != "" && myCurrentAlternatives != nullptr) {
     441            8 :             myCurrentAlternatives->addAlternativeDef(myNet.getRouteDef(myActiveRouteRefID));
     442            4 :             myActiveRouteID = "";
     443              :             myActiveRouteRefID = "";
     444           24 :             return;
     445              :         }
     446           20 :         if (myVehicleParameter != nullptr) {
     447           12 :             myErrorOutput->inform("The route for vehicle '" + myVehicleParameter->id + "' has no edges.");
     448              :         } else {
     449           48 :             myErrorOutput->inform("Route '" + myActiveRouteID + "' has no edges.");
     450              :         }
     451           20 :         myActiveRouteID = "";
     452              :         myActiveRouteStops.clear();
     453           20 :         return;
     454              :     }
     455       422778 :     if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
     456            0 :         myErrorOutput->inform("The routing information for vehicle '" + myVehicleParameter->id + "' is insufficient.");
     457            0 :         myActiveRouteID = "";
     458              :         myActiveRouteStops.clear();
     459            0 :         return;
     460              :     }
     461       892810 :     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      1507390 :         for (const ROEdge* roe : myActiveRoute) {
     466      1272377 :             if (last != nullptr) {
     467      2529244 :                 for (const ROEdge* intern : last->getSuccessors()) {
     468      1491880 :                     if (intern->isInternal() && intern->getSuccessors().size() == 1 && intern->getSuccessors().front() == roe) {
     469            0 :                         fullRoute.push_back(intern);
     470              :                     }
     471              :                 }
     472              :             }
     473      1272377 :             fullRoute.push_back(roe);
     474              :             last = roe;
     475              :         }
     476       235013 :         myActiveRoute = fullRoute;
     477       235013 :     }
     478       422778 :     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       422778 :     std::shared_ptr<RORoute> route = std::make_shared<RORoute>(myActiveRouteID, myCurrentCosts, myActiveRouteProbability, myActiveRoute,
     500       422778 :                                      myActiveRouteColor, myActiveRouteStops);
     501              :     myActiveRoute.clear();
     502       422778 :     if (myCurrentAlternatives == nullptr) {
     503       190175 :         if (myNet.getRouteDef(myActiveRouteID) != nullptr) {
     504            0 :             if (myVehicleParameter != nullptr) {
     505            0 :                 myErrorOutput->inform("Another route for vehicle '" + myVehicleParameter->id + "' exists.");
     506              :             } else {
     507            0 :                 myErrorOutput->inform("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
     508              :             }
     509              :             myActiveRouteID = "";
     510              :             myActiveRouteStops.clear();
     511              :             return;
     512              :         } else {
     513       190175 :             myCurrentAlternatives = new RORouteDef(myActiveRouteID, 0, mayBeDisconnected || myTryRepair, mayBeDisconnected);
     514       190175 :             myCurrentAlternatives->addLoadedAlternative(route);
     515       190175 :             myNet.addRouteDef(myCurrentAlternatives);
     516       190175 :             myCurrentAlternatives = nullptr;
     517              :         }
     518              :     } else {
     519       232603 :         myCurrentAlternatives->addLoadedAlternative(route);
     520              :     }
     521              :     myActiveRouteID = "";
     522              :     myActiveRouteStops.clear();
     523              : }
     524              : 
     525              : 
     526              : void
     527        91133 : RORouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
     528              :     // check whether the id is really necessary
     529        91133 :     bool ok = true;
     530              :     std::string id;
     531        91133 :     if (myVehicleParameter != nullptr) {
     532              :         // ok, a vehicle is wrapping the route,
     533              :         //  we may use this vehicle's id as default
     534        90109 :         myVehicleParameter->routeid = id = "!" + myVehicleParameter->id; // !!! document this
     535        90109 :         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     536            0 :             WRITE_WARNINGF(TL("Ids of internal route distributions are ignored (vehicle '%')."), myVehicleParameter->id);
     537              :         }
     538              :     } else {
     539         1024 :         id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     540         1024 :         if (!ok) {
     541              :             return;
     542              :         }
     543              :     }
     544              :     // try to get the index of the last element
     545        91125 :     int index = attrs.getOpt<int>(SUMO_ATTR_LAST, id.c_str(), ok, 0);
     546        91125 :     if (ok && index < 0) {
     547           16 :         myErrorOutput->inform("Negative index of a route alternative (id='" + id + "').");
     548            8 :         return;
     549              :     }
     550              :     // build the alternative cont
     551        91117 :     myCurrentAlternatives = new RORouteDef(id, index, myTryRepair, false);
     552        91117 :     if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
     553            0 :         ok = true;
     554            0 :         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, id.c_str(), ok));
     555            0 :         while (st.hasNext()) {
     556            0 :             const std::string routeID = st.next();
     557            0 :             const RORouteDef* route = myNet.getRouteDef(routeID);
     558            0 :             if (route == nullptr) {
     559            0 :                 myErrorOutput->inform("Unknown route '" + routeID + "' in distribution '" + id + "'.");
     560              :             } else {
     561            0 :                 myCurrentAlternatives->addAlternativeDef(route);
     562              :             }
     563              :         }
     564            0 :     }
     565              : }
     566              : 
     567              : 
     568              : void
     569        91117 : RORouteHandler::closeRouteDistribution() {
     570        91117 :     if (myCurrentAlternatives != nullptr) {
     571        91101 :         if (myCurrentAlternatives->getOverallProb() == 0) {
     572            0 :             myErrorOutput->inform("Route distribution '" + myCurrentAlternatives->getID() + "' is empty.");
     573            0 :             delete myCurrentAlternatives;
     574        91101 :         } else if (!myNet.addRouteDef(myCurrentAlternatives)) {
     575            0 :             myErrorOutput->inform("Another route (or distribution) with the id '" + myCurrentAlternatives->getID() + "' exists.");
     576            0 :             delete myCurrentAlternatives;
     577              :         } else {
     578       182202 :             if (myVehicleParameter != nullptr
     579       181318 :                     && (myUseTaz || OptionsCont::getOptions().getBool("junction-taz"))
     580        91261 :                     && (myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) ||
     581              :                         myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET))) {
     582              :                 // we are loading a rou.alt.xml, permit rerouting between taz
     583              :                 bool ok = true;
     584              :                 ConstROEdgeVector edges;
     585          160 :                 if (myVehicleParameter->fromTaz != "") {
     586              :                     const std::string tazID = myVehicleParameter->fromTaz;
     587          320 :                     const ROEdge* fromTaz = myNet.getEdge(tazID + "-source");
     588          160 :                     if (fromTaz == nullptr) {
     589            0 :                         myErrorOutput->inform("Source taz '" + tazID + "' not known for vehicle '" + myVehicleParameter->id + "'!");
     590              :                         ok = false;
     591          160 :                     } else if (fromTaz->getNumSuccessors() == 0) {
     592            0 :                         myErrorOutput->inform("Source taz '" + tazID + "' has no outgoing edges for vehicle '" + myVehicleParameter->id + "'!");
     593              :                         ok = false;
     594              :                     } else {
     595          160 :                         edges.push_back(fromTaz);
     596              :                     }
     597              :                 } else {
     598            0 :                     edges.push_back(myCurrentAlternatives->getOrigin());
     599              :                 }
     600          160 :                 if (myVehicleParameter->toTaz != "") {
     601              :                     const std::string tazID = myVehicleParameter->toTaz;
     602          320 :                     const ROEdge* toTaz = myNet.getEdge(tazID + "-sink");
     603          160 :                     if (toTaz == nullptr) {
     604            0 :                         myErrorOutput->inform("Sink taz '" + tazID + "' not known for vehicle '" + myVehicleParameter->id + "'!");
     605              :                         ok = false;
     606          160 :                     } else if (toTaz->getNumPredecessors() == 0) {
     607            0 :                         myErrorOutput->inform("Sink taz '" + tazID + "' has no incoming edges for vehicle '" + myVehicleParameter->id + "'!");
     608              :                         ok = false;
     609              :                     } else {
     610          160 :                         edges.push_back(toTaz);
     611              :                     }
     612              :                 } else {
     613            0 :                     edges.push_back(myCurrentAlternatives->getDestination());
     614              :                 }
     615          160 :                 if (ok) {
     616              :                     // negative probability indicates that this route should not be written
     617          320 :                     myCurrentAlternatives->addLoadedAlternative(std::make_shared<RORoute>(myCurrentAlternatives->getID(), 0, -1, edges, nullptr, myActiveRouteStops));
     618              :                 }
     619          160 :             }
     620              :         }
     621        91101 :         myCurrentAlternatives = nullptr;
     622              :     }
     623        91117 : }
     624              : 
     625              : 
     626              : void
     627       278670 : RORouteHandler::closeVehicle() {
     628       278670 :     checkLastDepart();
     629              :     // get the vehicle id
     630       278670 :     if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN && myVehicleParameter->depart < myBegin) {
     631            4 :         mySkippedVehicles.insert(myVehicleParameter->id);
     632            4 :         return;
     633              :     }
     634              :     // get vehicle type
     635       278666 :     SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     636       278666 :     if (type == nullptr) {
     637          272 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
     638          136 :         type = myNet.getVehicleTypeSecure(DEFAULT_VTYPE_ID);
     639              :     } else {
     640       278530 :         if (!myKeepVTypeDist) {
     641              :             // fix the type id in case we used a distribution
     642       278526 :             myVehicleParameter->vtypeid = type->id;
     643              :         }
     644              :     }
     645       278666 :     if (type->vehicleClass == SVC_PEDESTRIAN) {
     646          120 :         WRITE_WARNINGF(TL("Vehicle type '%' with vClass=pedestrian should only be used for persons and not for vehicle '%'."), type->id, myVehicleParameter->id);
     647              :     }
     648              :     // get the route
     649       278666 :     RORouteDef* route = myNet.getRouteDef(myVehicleParameter->routeid);
     650       278638 :     if (route == nullptr) {
     651           56 :         myErrorOutput->inform("The route of the vehicle '" + myVehicleParameter->id + "' is not known.");
     652           28 :         return;
     653              :     }
     654       278638 :     if (MsgHandler::getErrorInstance()->wasInformed()) {
     655              :         return;
     656              :     }
     657       278490 :     const bool needCopy = route->getID()[0] != '!';
     658       278490 :     if (needCopy) {
     659         2574 :         route = route->copy("!" + myVehicleParameter->id, myVehicleParameter->depart);
     660              :     }
     661              :     // build the vehicle
     662       278490 :     ROVehicle* veh = new ROVehicle(*myVehicleParameter, route, type, &myNet, myErrorOutput);
     663       278490 :     if (myNet.addVehicle(myVehicleParameter->id, veh)) {
     664       278486 :         registerLastDepart();
     665            4 :     } else if (needCopy) {
     666            4 :         delete route;
     667              :     }
     668       278490 :     delete myVehicleParameter;
     669       278490 :     myVehicleParameter = nullptr;
     670              : }
     671              : 
     672              : 
     673              : void
     674         2190 : RORouteHandler::closeVType() {
     675         2190 :     if (myCurrentVTypeRef.empty()) {
     676         2150 :         myNet.addVehicleType(myCurrentVType);
     677         4300 :         if (OptionsCont::getOptions().isSet("restriction-params")) {
     678           24 :             const std::vector<std::string> paramKeys = OptionsCont::getOptions().getStringVector("restriction-params");
     679           24 :             myCurrentVType->cacheParamRestrictions(paramKeys);
     680           24 :         }
     681              :     }
     682         2190 :     if (myCurrentVTypeDistribution != nullptr) {
     683           88 :         if (myCurrentVTypeRef.empty()) {
     684           48 :             myCurrentVTypeDistribution->add(myCurrentVType, myCurrentVType->defaultProbability);
     685              :         } else {
     686           40 :             const RandomDistributor<SUMOVTypeParameter*>* const dist = myNet.getVTypeDistribution(myCurrentVTypeRef);
     687            8 :             if (dist != nullptr) {
     688           16 :                 const double distProb = (myCurrentVTypeProbability >= 0 ? myCurrentVTypeProbability : 1) / dist->getOverallProb();
     689              :                 std::vector<double>::const_iterator probIt = dist->getProbs().begin();
     690           24 :                 for (SUMOVTypeParameter* const type : dist->getVals()) {
     691           16 :                     myCurrentVTypeDistribution->add(type, distProb * *probIt);
     692              :                     probIt++;
     693              :                 }
     694              :             } else {
     695           32 :                 SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(myCurrentVTypeRef);
     696           32 :                 double probability = myCurrentVTypeProbability >= 0 ? myCurrentVTypeProbability : type->defaultProbability;
     697           32 :                 myCurrentVTypeDistribution->add(type, probability);
     698              :             }
     699              :         }
     700              :     }
     701         2190 :     myCurrentVType = nullptr;
     702         2190 : }
     703              : 
     704              : 
     705              : void
     706         3229 : RORouteHandler::closePerson() {
     707         3229 :     SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
     708         3229 :     if (type == nullptr) {
     709            0 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.");
     710            0 :         type = myNet.getVehicleTypeSecure(DEFAULT_PEDTYPE_ID);
     711              :     }
     712         3229 :     if (myActivePlan == nullptr || myActivePlan->empty()) {
     713           40 :         WRITE_WARNINGF(TL("Discarding person '%' because her plan is empty"), myVehicleParameter->id);
     714         3209 :     } else if (myVehicleParameter->departProcedure != DepartDefinition::GIVEN || myVehicleParameter->depart >= myBegin) {
     715         3201 :         ROPerson* person = new ROPerson(*myVehicleParameter, type);
     716         6606 :         for (ROPerson::PlanItem* item : *myActivePlan) {
     717         3405 :             person->getPlan().push_back(item);
     718              :         }
     719         3201 :         myActivePlan->clear();
     720         3201 :         if (myNet.addPerson(person)) {
     721         3201 :             checkLastDepart();
     722         3201 :             registerLastDepart();
     723              :         }
     724              :     }
     725         3229 :     deleteActivePlanAndVehicleParameter();
     726         3229 : }
     727              : 
     728              : 
     729              : void
     730          186 : RORouteHandler::closePersonFlow() {
     731              :     std::string typeID = DEFAULT_PEDTYPE_ID;
     732          186 :     if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
     733            0 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for personFlow '" + myVehicleParameter->id + "' is not known.");
     734              :     } else {
     735          186 :         typeID = myVehicleParameter->vtypeid;
     736              :     }
     737          186 :     if (myActivePlan == nullptr || myActivePlan->empty()) {
     738            0 :         WRITE_WARNINGF(TL("Discarding personFlow '%' because their plan is empty"), myVehicleParameter->id);
     739              :     } else {
     740          186 :         checkLastDepart();
     741              :         // instantiate all persons of this flow
     742              :         int i = 0;
     743          186 :         std::string baseID = myVehicleParameter->id;
     744          186 :         if (myWriteFlows) {
     745          158 :             addFlowPerson(typeID, myVehicleParameter->depart, baseID, i);
     746           28 :         } else if (myVehicleParameter->repetitionProbability > 0) {
     747            4 :             if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
     748            0 :                 throw ProcessError(TLF("probabilistic personFlow '%' must specify end time", myVehicleParameter->id));
     749              :             } else {
     750           44 :                 for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
     751           40 :                     if (RandHelper::rand() < myVehicleParameter->repetitionProbability) {
     752           20 :                         addFlowPerson(typeID, t, baseID, i++);
     753              :                     }
     754              :                 }
     755              :             }
     756              :         } else {
     757           24 :             SUMOTime depart = myVehicleParameter->depart;
     758              :             // uniform sampling of departures from range is equivalent to poisson flow (encoded by negative offset)
     759           48 :             if (OptionsCont::getOptions().getBool("randomize-flows") && myVehicleParameter->repetitionOffset >= 0) {
     760              :                 std::vector<SUMOTime> departures;
     761            4 :                 const SUMOTime range = myVehicleParameter->repetitionNumber * myVehicleParameter->repetitionOffset;
     762           24 :                 for (int j = 0; j < myVehicleParameter->repetitionNumber; ++j) {
     763           20 :                     departures.push_back(depart + RandHelper::rand(range));
     764              :                 }
     765            4 :                 std::sort(departures.begin(), departures.end());
     766              :                 std::reverse(departures.begin(), departures.end());
     767           24 :                 for (; i < myVehicleParameter->repetitionNumber; i++) {
     768           20 :                     addFlowPerson(typeID, departures[i], baseID, i);
     769              :                     depart += myVehicleParameter->repetitionOffset;
     770              :                 }
     771            4 :             } else {
     772           20 :                 const bool triggered = myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED;
     773           20 :                 if (myVehicleParameter->repetitionOffset < 0) {
     774              :                     // poisson: randomize first depart
     775            4 :                     myVehicleParameter->incrementFlow(1);
     776              :                 }
     777          172 :                 for (; i < myVehicleParameter->repetitionNumber && (triggered || depart + myVehicleParameter->repetitionTotalOffset <= myVehicleParameter->repetitionEnd); i++) {
     778          152 :                     addFlowPerson(typeID, depart + myVehicleParameter->repetitionTotalOffset, baseID, i);
     779          152 :                     if (myVehicleParameter->departProcedure != DepartDefinition::TRIGGERED) {
     780          152 :                         myVehicleParameter->incrementFlow(1);
     781              :                     }
     782              :                 }
     783              :             }
     784              :         }
     785              :     }
     786          186 :     deleteActivePlanAndVehicleParameter();
     787          186 : }
     788              : 
     789              : 
     790              : void
     791          350 : RORouteHandler::addFlowPerson(const std::string& typeID, SUMOTime depart, const std::string& baseID, int i) {
     792          350 :     SUMOVehicleParameter pars = *myVehicleParameter;
     793          700 :     pars.id = baseID + "." + toString(i);
     794          350 :     pars.depart = depart;
     795          350 :     const SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
     796          350 :     if (!myKeepVTypeDist) {
     797          350 :         pars.vtypeid = type->id;
     798              :     }
     799          350 :     ROPerson* person = new ROPerson(pars, type);
     800          700 :     for (ROPerson::PlanItem* item : *myActivePlan) {
     801          350 :         person->getPlan().push_back(item->clone());
     802              :     }
     803          350 :     if (myNet.addPerson(person)) {
     804          350 :         if (i == 0) {
     805          186 :             registerLastDepart();
     806              :         }
     807              :     }
     808          350 : }
     809              : 
     810              : 
     811              : void
     812           36 : RORouteHandler::closeContainer() {
     813           36 :     myActiveContainerPlan->closeTag();
     814           36 :     if (myActiveContainerPlanSize > 0) {
     815           36 :         myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
     816           36 :         checkLastDepart();
     817           36 :         registerLastDepart();
     818              :     } else {
     819            0 :         WRITE_WARNINGF(TL("Discarding container '%' because its plan is empty"), myVehicleParameter->id);
     820              :     }
     821           36 :     delete myVehicleParameter;
     822           36 :     myVehicleParameter = nullptr;
     823           36 :     delete myActiveContainerPlan;
     824           36 :     myActiveContainerPlan = nullptr;
     825           36 :     myActiveContainerPlanSize = 0;
     826           36 : }
     827              : 
     828              : 
     829           20 : void RORouteHandler::closeContainerFlow() {
     830           20 :     myActiveContainerPlan->closeTag();
     831           20 :     if (myActiveContainerPlanSize > 0) {
     832           20 :         myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
     833           20 :         checkLastDepart();
     834           20 :         registerLastDepart();
     835              :     } else {
     836            0 :         WRITE_WARNINGF(TL("Discarding containerFlow '%' because its plan is empty"), myVehicleParameter->id);
     837              :     }
     838           20 :     delete myVehicleParameter;
     839           20 :     myVehicleParameter = nullptr;
     840           20 :     delete myActiveContainerPlan;
     841           20 :     myActiveContainerPlan = nullptr;
     842           20 :     myActiveContainerPlanSize = 0;
     843           20 : }
     844              : 
     845              : 
     846              : void
     847         2605 : RORouteHandler::closeFlow() {
     848         2605 :     checkLastDepart();
     849              :     // @todo: consider myScale?
     850         2605 :     if (myVehicleParameter->repetitionNumber == 0) {
     851            8 :         delete myVehicleParameter;
     852            8 :         myVehicleParameter = nullptr;
     853            8 :         return;
     854              :     }
     855              :     // let's check whether vehicles had to depart before the simulation starts
     856         2597 :     myVehicleParameter->repetitionsDone = 0;
     857         2597 :     const SUMOTime offsetToBegin = myBegin - myVehicleParameter->depart;
     858         2781 :     while (myVehicleParameter->repetitionTotalOffset < offsetToBegin) {
     859          186 :         myVehicleParameter->incrementFlow(1);
     860          186 :         if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
     861            2 :             delete myVehicleParameter;
     862            2 :             myVehicleParameter = nullptr;
     863            2 :             return;
     864              :         }
     865              :     }
     866         2595 :     if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
     867           54 :         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
     868              :     }
     869         2595 :     if (myVehicleParameter->routeid[0] == '!' && myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
     870         1902 :         closeRoute(true);
     871              :     }
     872         2595 :     if (myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
     873            0 :         myErrorOutput->inform("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
     874            0 :         delete myVehicleParameter;
     875            0 :         myVehicleParameter = nullptr;
     876            0 :         return;
     877              :     }
     878         2595 :     myActiveRouteID = "";
     879         2595 :     if (!MsgHandler::getErrorInstance()->wasInformed()) {
     880         5172 :         if (myNet.addFlow(myVehicleParameter, OptionsCont::getOptions().getBool("randomize-flows"))) {
     881         2582 :             registerLastDepart();
     882              :         } else {
     883            8 :             myErrorOutput->inform("Another flow with the id '" + myVehicleParameter->id + "' exists.");
     884            4 :             delete myVehicleParameter;
     885              :         }
     886              :     } else {
     887            9 :         delete myVehicleParameter;
     888              :     }
     889         2595 :     myVehicleParameter = nullptr;
     890         2595 :     myInsertStopEdgesAt = -1;
     891              : }
     892              : 
     893              : 
     894              : void
     895       185865 : RORouteHandler::closeTrip() {
     896       185865 :     closeRoute(true);
     897       185865 :     closeVehicle();
     898       185865 : }
     899              : 
     900              : 
     901              : const SUMOVehicleParameter::Stop*
     902         6410 : RORouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, std::string& id, const SUMOVehicleParameter::Stop* stopParam) {
     903              :     // dummy stop parameter to hold the attributes
     904         6410 :     SUMOVehicleParameter::Stop stop;
     905         6410 :     if (stopParam != nullptr) {
     906         2915 :         stop = *stopParam;
     907              :     } else {
     908         3495 :         bool ok = true;
     909         6990 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
     910         3495 :         stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
     911         3495 :         stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
     912         3495 :         stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
     913         3495 :         stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
     914         6990 :         stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
     915              :     }
     916              :     const SUMOVehicleParameter::Stop* toStop = nullptr;
     917         6410 :     if (stop.busstop != "") {
     918         2817 :         toStop = myNet.getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
     919              :         id = stop.busstop;
     920         2817 :         if (toStop == nullptr) {
     921            0 :             WRITE_ERROR("The busStop '" + stop.busstop + "' is not known" + errorSuffix);
     922              :         }
     923         3593 :     } else if (stop.containerstop != "") {
     924           32 :         toStop = myNet.getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
     925              :         id = stop.containerstop;
     926           32 :         if (toStop == nullptr) {
     927            0 :             WRITE_ERROR("The containerStop '" + stop.containerstop + "' is not known" + errorSuffix);
     928              :         }
     929         3561 :     } else if (stop.parkingarea != "") {
     930           24 :         toStop = myNet.getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
     931              :         id = stop.parkingarea;
     932           24 :         if (toStop == nullptr) {
     933            0 :             WRITE_ERROR("The parkingArea '" + stop.parkingarea + "' is not known" + errorSuffix);
     934              :         }
     935         3537 :     } else if (stop.chargingStation != "") {
     936              :         // ok, we have a charging station
     937           20 :         toStop = myNet.getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
     938              :         id = stop.chargingStation;
     939           20 :         if (toStop == nullptr) {
     940            0 :             WRITE_ERROR("The chargingStation '" + stop.chargingStation + "' is not known" + errorSuffix);
     941              :         }
     942         3517 :     } else if (stop.overheadWireSegment != "") {
     943              :         // ok, we have an overhead wire segment
     944            0 :         toStop = myNet.getStoppingPlace(stop.overheadWireSegment, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
     945              :         id = stop.overheadWireSegment;
     946            0 :         if (toStop == nullptr) {
     947            0 :             WRITE_ERROR("The overhead wire segment '" + stop.overheadWireSegment + "' is not known" + errorSuffix);
     948              :         }
     949              :     }
     950         6410 :     return toStop;
     951         6410 : }
     952              : 
     953              : Parameterised*
     954         2931 : RORouteHandler::addStop(const SUMOSAXAttributes& attrs) {
     955              :     Parameterised* result = nullptr;
     956         2931 :     if (myActiveContainerPlan != nullptr) {
     957           16 :         myActiveContainerPlan->openTag(SUMO_TAG_STOP);
     958           16 :         (*myActiveContainerPlan) << attrs;
     959           16 :         myActiveContainerPlan->closeTag();
     960           16 :         myActiveContainerPlanSize++;
     961           16 :         return result;
     962              :     }
     963              :     std::string errorSuffix;
     964         2915 :     if (myActivePlan != nullptr) {
     965          384 :         errorSuffix = " in person '" + myVehicleParameter->id + "'.";
     966              :     } else if (myActiveContainerPlan != nullptr) {
     967              :         errorSuffix = " in container '" + myVehicleParameter->id + "'.";
     968         2787 :     } else if (myVehicleParameter != nullptr) {
     969         2241 :         errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
     970              :     } else {
     971         6120 :         errorSuffix = " in route '" + myActiveRouteID + "'.";
     972              :     }
     973         2915 :     SUMOVehicleParameter::Stop stop;
     974         5830 :     bool ok = parseStop(stop, attrs, errorSuffix, myErrorOutput);
     975         2915 :     if (!ok) {
     976              :         return result;
     977              :     }
     978              :     // try to parse the assigned bus stop
     979         2915 :     const ROEdge* edge = nullptr;
     980              :     std::string stoppingPlaceID;
     981         2915 :     const SUMOVehicleParameter::Stop* stoppingPlace = retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID, &stop);
     982              :     bool hasPos = false;
     983         2915 :     if (stoppingPlace != nullptr) {
     984         2709 :         stop.lane = stoppingPlace->lane;
     985         2709 :         stop.endPos = stoppingPlace->endPos;
     986         2709 :         stop.startPos = stoppingPlace->startPos;
     987        10836 :         edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
     988              :     } else {
     989              :         // no, the lane and the position should be given
     990          206 :         stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
     991          206 :         stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
     992          206 :         if (ok && stop.edge != "") {
     993           54 :             edge = myNet.getEdge(stop.edge);
     994           54 :             if (edge == nullptr) {
     995            0 :                 myErrorOutput->inform("The edge '" + stop.edge + "' for a stop is not known" + errorSuffix);
     996            0 :                 return result;
     997              :             }
     998          152 :         } else if (ok && stop.lane != "") {
     999          360 :             edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
    1000          120 :             if (edge == nullptr) {
    1001            0 :                 myErrorOutput->inform("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
    1002            0 :                 return result;
    1003              :             }
    1004           32 :         } else if (ok && ((attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y))
    1005           20 :                           || (attrs.hasAttribute(SUMO_ATTR_LON) && attrs.hasAttribute(SUMO_ATTR_LAT)))) {
    1006              :             Position pos;
    1007              :             bool geo = false;
    1008           32 :             if (attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y)) {
    1009           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));
    1010              :             } else {
    1011           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));
    1012              :                 geo = true;
    1013              :             }
    1014           32 :             PositionVector positions;
    1015           32 :             positions.push_back(pos);
    1016              :             ConstROEdgeVector geoEdges;
    1017              :             SUMOVehicleClass vClass = SVC_PASSENGER;
    1018           32 :             if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
    1019           32 :                 SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
    1020           32 :                 if (type != nullptr) {
    1021           32 :                     vClass = type->vehicleClass;
    1022              :                 }
    1023              :             }
    1024           32 :             parseGeoEdges(positions, geo, vClass, geoEdges, myVehicleParameter->id, true, ok, true);
    1025           32 :             if (ok) {
    1026           32 :                 edge = geoEdges.front();
    1027              :                 hasPos = true;
    1028           32 :                 if (geo) {
    1029           20 :                     GeoConvHelper::getFinal().x2cartesian_const(pos);
    1030              :                 }
    1031           32 :                 stop.parametersSet |= STOP_END_SET;
    1032           32 :                 stop.endPos = edge->getLanes()[0]->getShape().nearest_offset_to_point2D(pos, false);
    1033              :             } else {
    1034              :                 return result;
    1035              :             }
    1036           32 :         } else if (!ok || (stop.lane == "" && stop.edge == "")) {
    1037            0 :             myErrorOutput->inform("A stop must be placed on a bus stop, a container stop, a parking area, an edge or a lane" + errorSuffix);
    1038            0 :             return result;
    1039              :         }
    1040              :         if (!hasPos) {
    1041          174 :             stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
    1042              :         }
    1043          206 :         stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, stop.endPos - 2 * POSITION_EPS);
    1044          266 :         const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS));
    1045          206 :         const double endPosOffset = edge->isInternal() ? edge->getNormalBefore()->getLength() : 0;
    1046          206 :         if (!ok || (checkStopPos(stop.startPos, stop.endPos, edge->getLength() + endPosOffset, POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
    1047            0 :             myErrorOutput->inform("Invalid start or end position for stop" + errorSuffix);
    1048            0 :             return result;
    1049              :         }
    1050              :     }
    1051         2915 :     stop.edge = edge->getID();
    1052         2915 :     if (myActivePlan != nullptr) {
    1053          128 :         ROPerson::addStop(*myActivePlan, stop, edge);
    1054          128 :         result = myActivePlan->back()->getStopParameters();
    1055         2787 :     } else if (myVehicleParameter != nullptr) {
    1056          747 :         myVehicleParameter->stops.push_back(stop);
    1057          747 :         result = &myVehicleParameter->stops.back();
    1058              :     } else {
    1059         2040 :         myActiveRouteStops.push_back(stop);
    1060              :         result = &myActiveRouteStops.back();
    1061              :     }
    1062         2915 :     if (myInsertStopEdgesAt >= 0) {
    1063          514 :         myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
    1064          514 :         myInsertStopEdgesAt++;
    1065              :     }
    1066              :     return result;
    1067         2915 : }
    1068              : 
    1069              : 
    1070              : void
    1071          108 : RORouteHandler::addRide(const SUMOSAXAttributes& attrs) {
    1072          108 :     bool ok = true;
    1073          108 :     std::vector<ROPerson::PlanItem*>& plan = *myActivePlan;
    1074          108 :     const std::string pid = myVehicleParameter->id;
    1075              : 
    1076              :     const ROEdge* from = nullptr;
    1077              :     const ROEdge* to = nullptr;
    1078          108 :     parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
    1079          116 :     if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
    1080          116 :             || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
    1081          104 :         if (ok) {
    1082          104 :             from = myActiveRoute.front();
    1083              :         }
    1084            4 :     } else if (plan.empty()) {
    1085            0 :         myErrorOutput->inform("The start edge for person '" + pid + "' is not known.");
    1086            0 :         return;
    1087              :     }
    1088              :     std::string stoppingPlaceID;
    1089          216 :     const SUMOVehicleParameter::Stop* stop = retrieveStoppingPlace(attrs, " for ride of person '" + myVehicleParameter->id + "'", stoppingPlaceID);
    1090          108 :     if (stop != nullptr) {
    1091           84 :         to = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop->lane));
    1092              :     } else {
    1093           84 :         if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
    1094           84 :                 || attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
    1095           80 :             to = myActiveRoute.back();
    1096              :         } else {
    1097            0 :             myErrorOutput->inform("The to edge is missing within a ride of '" + myVehicleParameter->id + "'.");
    1098            0 :             return;
    1099              :         }
    1100              :     }
    1101          108 :     double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
    1102              :                         stop == nullptr ? std::numeric_limits<double>::infinity() : stop->endPos);
    1103          108 :     const std::string lines = attrs.getOpt<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok, LINE_ANY);
    1104          216 :     const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, pid.c_str(), ok, "");
    1105              : 
    1106          108 :     if (plan.empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
    1107           36 :         StringTokenizer st(lines);
    1108           72 :         if (st.size() != 1 || st.get(0) == LINE_ANY) {
    1109            0 :             myErrorOutput->inform("Triggered departure for person '" + pid + "' requires a unique lines value.");
    1110            0 :             return;
    1111              :         }
    1112           36 :         const std::string vehID = st.front();
    1113           36 :         if (myNet.knowsVehicle(vehID)) {
    1114           20 :             const SUMOTime vehDepart = myNet.getDeparture(vehID);
    1115           20 :             if (vehDepart == -1) {
    1116            0 :                 myErrorOutput->inform("Cannot use triggered vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
    1117            0 :                 return;
    1118              :             }
    1119           20 :             myVehicleParameter->depart = vehDepart + 1; // write person after vehicle
    1120              :         } else {
    1121              :             if (mySkippedVehicles.count(vehID) == 0) {
    1122           16 :                 myErrorOutput->inform("Unknown vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
    1123            8 :                 return;
    1124              :             }
    1125            8 :             myVehicleParameter->departProcedure = DepartDefinition::GIVEN; // make sure the person gets skipped due to depart time
    1126              :         }
    1127           36 :     }
    1128          100 :     ROPerson::addRide(plan, from, to, lines, arrivalPos, stoppingPlaceID, group);
    1129              : }
    1130              : 
    1131              : 
    1132              : void
    1133           32 : RORouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
    1134           32 :     if (myActiveContainerPlan != nullptr && myActiveContainerPlanSize == 0 && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
    1135           12 :         bool ok = true;
    1136              :         const std::string pid = myVehicleParameter->id;
    1137           12 :         const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
    1138           16 :         StringTokenizer st(desc);
    1139           12 :         if (st.size() != 1) {
    1140            0 :             throw ProcessError(TLF("Triggered departure for container '%' requires a unique lines value.", pid));
    1141              :         }
    1142           12 :         const std::string vehID = st.front();
    1143           12 :         if (!myNet.knowsVehicle(vehID)) {
    1144              :             if (mySkippedVehicles.count(vehID) == 0) {
    1145           12 :                 throw ProcessError("Unknown vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
    1146              :             }
    1147              :             return;
    1148              :         }
    1149            8 :         SUMOTime vehDepart = myNet.getDeparture(vehID);
    1150            8 :         if (vehDepart == -1) {
    1151            0 :             throw ProcessError("Cannot use triggered vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
    1152              :         }
    1153            8 :         myVehicleParameter->depart = vehDepart + 1; // write container after vehicle
    1154           12 :     }
    1155              : }
    1156              : 
    1157              : 
    1158              : void
    1159           32 : RORouteHandler::addTranship(const SUMOSAXAttributes& /*attrs*/) {
    1160           32 : }
    1161              : 
    1162              : 
    1163              : void
    1164       780542 : RORouteHandler::parseEdges(const std::string& desc, ConstROEdgeVector& into,
    1165              :                            const std::string& rid, bool& ok) {
    1166      3217915 :     for (StringTokenizer st(desc); st.hasNext();) {
    1167      1656831 :         const std::string id = st.next();
    1168      1656831 :         const ROEdge* edge = myNet.getEdge(id);
    1169      1656831 :         if (edge == nullptr) {
    1170           60 :             myErrorOutput->inform("The edge '" + id + "' within the route " + rid + " is not known.");
    1171           30 :             ok = false;
    1172              :         } else {
    1173      1656801 :             into.push_back(edge);
    1174              :         }
    1175       780542 :     }
    1176       780542 : }
    1177              : 
    1178              : 
    1179              : void
    1180         3315 : RORouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
    1181              :                                    const ROEdge* /*fromEdge*/, const ROEdge*& toEdge,
    1182              :                                    double& departPos, double& arrivalPos, std::string& busStopID,
    1183              :                                    const ROPerson::PlanItem* const lastStage, bool& ok) {
    1184         3315 :     const std::string description = "walk or personTrip of '" + personID + "'.";
    1185         3315 :     if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
    1186            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."));
    1187              :     }
    1188         3315 :     departPos = myVehicleParameter->departPos;
    1189         3315 :     if (lastStage != nullptr) {
    1190          156 :         departPos = lastStage->getDestinationPos();
    1191              :     }
    1192              : 
    1193         3315 :     busStopID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
    1194              : 
    1195         3315 :     const SUMOVehicleParameter::Stop* bs = retrieveStoppingPlace(attrs, description, busStopID);
    1196         3315 :     if (bs != nullptr) {
    1197          396 :         toEdge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(bs->lane));
    1198          132 :         arrivalPos = (bs->startPos + bs->endPos) / 2;
    1199              :     }
    1200         3315 :     if (toEdge != nullptr) {
    1201         3315 :         if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1202          254 :             arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS,
    1203          254 :                          myHardFail, description, toEdge->getLength(),
    1204          508 :                          attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok));
    1205              :         }
    1206              :     } else {
    1207            0 :         throw ProcessError(TLF("No destination edge for %.", description));
    1208              :     }
    1209         3315 : }
    1210              : 
    1211              : 
    1212              : void
    1213         3315 : RORouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
    1214         3315 :     bool ok = true;
    1215         3315 :     const char* const id = myVehicleParameter->id.c_str();
    1216              :     assert(!attrs.hasAttribute(SUMO_ATTR_EDGES));
    1217              :     const ROEdge* from = nullptr;
    1218         3315 :     const ROEdge* to = nullptr;
    1219         3315 :     parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
    1220         3315 :     myInsertStopEdgesAt = -1;
    1221         3963 :     if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
    1222         3483 :             || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
    1223         3163 :         if (ok) {
    1224         3151 :             from = myActiveRoute.front();
    1225              :         }
    1226          152 :     } else if (myActivePlan->empty()) {
    1227            0 :         throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
    1228              :     } else {
    1229          152 :         from = myActivePlan->back()->getDestination();
    1230              :     }
    1231         3935 :     if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
    1232         3463 :             || attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
    1233         3183 :         to = myActiveRoute.back();
    1234              :     } // else, to may also be derived from stopping place
    1235              : 
    1236         3315 :     const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
    1237         3315 :     if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1238            0 :         throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1239              :     }
    1240              : 
    1241         3315 :     double departPos = 0;
    1242         3315 :     double arrivalPos = std::numeric_limits<double>::infinity();
    1243              :     std::string busStopID;
    1244         3315 :     const ROPerson::PlanItem* const lastStage = myActivePlan->empty() ? nullptr : myActivePlan->back();
    1245         3315 :     parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, busStopID, lastStage, ok);
    1246              : 
    1247         3315 :     const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
    1248         6630 :     const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, "");
    1249              :     SVCPermissions modeSet = 0;
    1250         9195 :     for (StringTokenizer st(modes); st.hasNext();) {
    1251         2565 :         const std::string mode = st.next();
    1252         2565 :         if (mode == "car") {
    1253          814 :             modeSet |= SVC_PASSENGER;
    1254         1751 :         } else if (mode == "taxi") {
    1255           93 :             modeSet |= SVC_TAXI;
    1256         1658 :         } else if (mode == "bicycle") {
    1257           12 :             modeSet |= SVC_BICYCLE;
    1258         1646 :         } else if (mode == "public") {
    1259         1646 :             modeSet |= SVC_BUS;
    1260              :         } else {
    1261            0 :             throw InvalidArgument("Unknown person mode '" + mode + "'.");
    1262              :         }
    1263         3315 :     }
    1264         3315 :     const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, "");
    1265         3315 :     double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
    1266         3315 :     if (ok) {
    1267         3303 :         const std::string originStopID = myActivePlan->empty() ?  "" : myActivePlan->back()->getStopDest();
    1268         3303 :         ROPerson::addTrip(*myActivePlan, myVehicleParameter->id, from, to, modeSet, types,
    1269              :                           departPos, originStopID, arrivalPos, busStopID, walkFactor, group);
    1270         3303 :         myParamStack.push_back(myActivePlan->back());
    1271              :     }
    1272         3315 : }
    1273              : 
    1274              : 
    1275              : void
    1276         1262 : RORouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
    1277              :     // parse walks from->to as person trips
    1278         1262 :     if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1279              :         // XXX allow --repair?
    1280           72 :         bool ok = true;
    1281           72 :         if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
    1282           12 :             const std::string routeID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
    1283           12 :             RORouteDef* routeDef = myNet.getRouteDef(routeID);
    1284           12 :             const std::shared_ptr<const RORoute> route = routeDef != nullptr ? routeDef->getFirstRoute() : nullptr;
    1285           12 :             if (route == nullptr) {
    1286            0 :                 throw ProcessError("The route '" + routeID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
    1287              :             }
    1288           12 :             myActiveRoute = route->getEdgeVector();
    1289              :         } else {
    1290              :             myActiveRoute.clear();
    1291          240 :             parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, " walk for person '" + myVehicleParameter->id + "'", ok);
    1292              :         }
    1293           72 :         const char* const objId = myVehicleParameter->id.c_str();
    1294           72 :         const double duration = attrs.getOpt<double>(SUMO_ATTR_DURATION, objId, ok, -1);
    1295           72 :         if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
    1296            0 :             throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
    1297              :         }
    1298           72 :         const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, objId, ok, -1.);
    1299           72 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
    1300            0 :             throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
    1301              :         }
    1302              :         double departPos = 0.;
    1303              :         double arrivalPos = std::numeric_limits<double>::infinity();
    1304           72 :         if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
    1305            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."));
    1306              :         }
    1307           72 :         if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
    1308           32 :             arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, objId, myActiveRoute.back()->getLength(), attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, objId, ok));
    1309              :         }
    1310              :         std::string stoppingPlaceID;
    1311           72 :         const std::string errorSuffix = " for walk of person '" + myVehicleParameter->id + "'";
    1312           72 :         retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID);
    1313           72 :         if (ok) {
    1314           72 :             ROPerson::addWalk(*myActivePlan, myActiveRoute, duration, speed, departPos, arrivalPos, stoppingPlaceID);
    1315           72 :             myParamStack.push_back(myActivePlan->back());
    1316              :         }
    1317              :     } else {
    1318         1190 :         addPersonTrip(attrs);
    1319              :     }
    1320         1262 : }
    1321              : 
    1322              : 
    1323              : void
    1324           77 : RORouteHandler::initLaneTree(NamedRTree* tree) {
    1325         6214 :     for (const auto& edgeItem : myNet.getEdgeMap()) {
    1326        11599 :         for (ROLane* lane : edgeItem.second->getLanes()) {
    1327         5462 :             Boundary b = lane->getShape().getBoxBoundary();
    1328         5462 :             const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
    1329         5462 :             const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
    1330         5462 :             tree->Insert(cmin, cmax, lane);
    1331              :         }
    1332              :     }
    1333           77 : }
    1334              : 
    1335              : 
    1336              : ROEdge*
    1337          512 : RORouteHandler::retrieveEdge(const std::string& id) {
    1338          512 :     return myNet.getEdge(id);
    1339              : }
    1340              : 
    1341              : 
    1342              : const SUMOVTypeParameter*
    1343         2158 : RORouteHandler::getVTypeParameter(const std::string& refid) {
    1344         2158 :     if (refid == "") {
    1345              :         return nullptr;
    1346              :     }
    1347           16 :     if (!myNet.knowsVType(refid)) {
    1348            0 :         myErrorOutput->inform(TLF("Unknown vehicle type '%'.", refid));
    1349            0 :         return nullptr;
    1350              :     }
    1351           16 :     return myNet.getVehicleTypeSecure(refid);
    1352              : }
    1353              : 
    1354              : 
    1355              : bool
    1356       284718 : RORouteHandler::checkLastDepart() {
    1357       284718 :     if (!myUnsortedInput) {
    1358       283897 :         return SUMORouteHandler::checkLastDepart();
    1359              :     }
    1360              :     return true;
    1361              : }
    1362              : 
    1363              : 
    1364              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1