LCOV - code coverage report
Current view: top level - src/router - RORouteHandler.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 676 769 87.9 %
Date: 2024-05-07 15:28:01 Functions: 36 36 100.0 %

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

Generated by: LCOV version 1.14