LCOV - code coverage report
Current view: top level - src/router - ROPerson.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 95.7 % 281 269
Test Date: 2026-03-02 16:00:03 Functions: 100.0 % 17 17

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2002-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    ROPerson.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Axel Wegener
      17              : /// @author  Michael Behrisch
      18              : /// @author  Jakob Erdmann
      19              : /// @date    Sept 2002
      20              : ///
      21              : // A vehicle as used by router
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <iostream>
      27              : #include <utils/common/StringTokenizer.h>
      28              : #include <utils/common/ToString.h>
      29              : #include <utils/common/StringUtils.h>
      30              : #include <utils/common/MsgHandler.h>
      31              : #include <utils/geom/GeoConvHelper.h>
      32              : #include <utils/vehicle/SUMOVTypeParameter.h>
      33              : #include <utils/options/OptionsCont.h>
      34              : #include <utils/iodevices/OutputDevice.h>
      35              : #include "RORouteDef.h"
      36              : #include "RORoute.h"
      37              : #include "ROVehicle.h"
      38              : #include "ROHelper.h"
      39              : #include "RONet.h"
      40              : #include "ROLane.h"
      41              : #include "ROPerson.h"
      42              : 
      43              : bool ROPerson::myHaveWarnedPTMissing(false);
      44              : const std::string ROPerson::PlanItem::UNDEFINED_STOPPING_PLACE;
      45              : 
      46              : // ===========================================================================
      47              : // method definitions
      48              : // ===========================================================================
      49         3345 : ROPerson::ROPerson(const SUMOVehicleParameter& pars, const SUMOVTypeParameter* type)
      50         3345 :     : RORoutable(pars, type) {
      51         3345 : }
      52              : 
      53              : 
      54         6690 : ROPerson::~ROPerson() {
      55         6894 :     for (PlanItem* const it : myPlan) {
      56         3549 :         delete it;
      57              :     }
      58         6690 : }
      59              : 
      60              : 
      61              : void
      62         3097 : ROPerson::addTrip(std::vector<PlanItem*>& plan, const std::string& id,
      63              :                   const ROEdge* const from, const ROEdge* const to, const SVCPermissions modeSet,
      64              :                   const std::string& vTypes,
      65              :                   const double departPos, const std::string& stopOrigin,
      66              :                   const double arrivalPos, const std::string& busStop,
      67              :                   double walkFactor, const std::string& group) {
      68         3097 :     PersonTrip* trip = new PersonTrip(from, to, modeSet, departPos, stopOrigin, arrivalPos, busStop, walkFactor, group);
      69         3097 :     RONet* net = RONet::getInstance();
      70         3097 :     SUMOVehicleParameter pars;
      71         3097 :     pars.departProcedure = DepartDefinition::TRIGGERED;
      72         3097 :     if (departPos != 0) {
      73          206 :         pars.departPosProcedure = DepartPosDefinition::GIVEN;
      74          206 :         pars.departPos = departPos;
      75          206 :         pars.parametersSet |= VEHPARS_DEPARTPOS_SET;
      76              :     }
      77         6234 :     for (StringTokenizer st(vTypes); st.hasNext();) {
      78           40 :         pars.vtypeid = st.next();
      79           40 :         pars.parametersSet |= VEHPARS_VTYPE_SET;
      80           40 :         const SUMOVTypeParameter* type = net->getVehicleTypeSecure(pars.vtypeid);
      81           40 :         if (type == nullptr) {
      82            0 :             delete trip;
      83            0 :             throw InvalidArgument("The vehicle type '" + pars.vtypeid + "' in a trip for person '" + id + "' is not known.");
      84              :         }
      85           80 :         pars.id = id + "_" + toString(trip->getVehicles().size());
      86           80 :         trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), type, net));
      87              :         // update modeset with routing-category vClass
      88           40 :         if (type->vehicleClass == SVC_BICYCLE) {
      89              :             trip->updateModes(SVC_BICYCLE);
      90              :         } else {
      91              :             trip->updateModes(SVC_PASSENGER);
      92              :         }
      93         3097 :     }
      94         3097 :     if (trip->getVehicles().empty()) {
      95         3057 :         if ((modeSet & SVC_PASSENGER) != 0) {
      96          614 :             pars.id = id + "_0";
      97         1228 :             trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_VTYPE_ID), net));
      98              :         }
      99         3057 :         if ((modeSet & SVC_BICYCLE) != 0) {
     100           24 :             pars.id = id + "_b0";
     101              :             pars.vtypeid = DEFAULT_BIKETYPE_ID;
     102           12 :             pars.parametersSet |= VEHPARS_VTYPE_SET;
     103           24 :             trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_BIKETYPE_ID), net));
     104              :         }
     105         3057 :         if ((modeSet & SVC_TAXI) != 0) {
     106              :             // add dummy taxi for routing (never added to output)
     107              :             pars.id = "taxi"; // id is written as 'line'
     108              :             pars.vtypeid = DEFAULT_TAXITYPE_ID;
     109          186 :             trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_TAXITYPE_ID), net));
     110              :         }
     111              :     }
     112         3097 :     plan.push_back(trip);
     113         3097 : }
     114              : 
     115              : 
     116              : void
     117          100 : ROPerson::addRide(std::vector<PlanItem*>& plan, const ROEdge* const from, const ROEdge* const to, const std::string& lines,
     118              :                   double arrivalPos, const std::string& destStop, const std::string& group) {
     119          300 :     plan.push_back(new PersonTrip(to, destStop));
     120          100 :     plan.back()->addTripItem(new Ride(-1, from, to, lines, group, -1., arrivalPos, -1., destStop));
     121          100 : }
     122              : 
     123              : 
     124              : void
     125           72 : ROPerson::addWalk(std::vector<PlanItem*>& plan, const ConstROEdgeVector& edges, const double duration, const double speed, const double departPos, const double arrivalPos, const std::string& busStop) {
     126           72 :     if (plan.empty() || plan.back()->isStop()) {
     127          204 :         plan.push_back(new PersonTrip(edges.back(), busStop));
     128              :     }
     129           72 :     plan.back()->addTripItem(new Walk(-1, edges, -1., duration, speed, departPos, arrivalPos, busStop));
     130           72 : }
     131              : 
     132              : 
     133              : void
     134          128 : ROPerson::addStop(std::vector<PlanItem*>& plan, const SUMOVehicleParameter::Stop& stopPar, const ROEdge* const stopEdge) {
     135          128 :     plan.push_back(new Stop(stopPar, stopEdge));
     136          128 : }
     137              : 
     138              : 
     139              : void
     140          873 : ROPerson::Ride::saveAsXML(OutputDevice& os, const bool extended, OptionsCont& options) const {
     141          873 :     os.openTag(SUMO_TAG_RIDE);
     142          873 :     std::string comment = "";
     143         1746 :     if ((extended || options.getBool("write-costs")) && myCost >= 0.) {
     144           20 :         os.writeAttr(SUMO_ATTR_COST, myCost);
     145              :     }
     146          873 :     if (from != nullptr) {
     147          307 :         os.writeAttr(SUMO_ATTR_FROM, from->getID());
     148              :     }
     149          873 :     if (to != nullptr) {
     150          311 :         os.writeAttr(SUMO_ATTR_TO, to->getID());
     151              :     }
     152          873 :     if (destStop != "") {
     153          662 :         const std::string element = RONet::getInstance()->getStoppingPlaceElement(destStop);
     154          662 :         os.writeAttr(element, destStop);
     155          662 :         const std::string name = RONet::getInstance()->getStoppingPlaceName(destStop);
     156          662 :         if (name != "") {
     157         1203 :             comment =  " <!-- " + name + " -->";
     158              :         }
     159          211 :     } else if (arrPos != 0 && arrPos != std::numeric_limits<double>::infinity()) {
     160           36 :         os.writeAttr(SUMO_ATTR_ARRIVALPOS, arrPos);
     161              :     }
     162          873 :     if (lines != LINE_ANY) {
     163              :         // no need to write the default
     164          247 :         os.writeAttr(SUMO_ATTR_LINES, lines);
     165              :     }
     166          873 :     if (group != "") {
     167           12 :         os.writeAttr(SUMO_ATTR_GROUP, group);
     168              :     }
     169          873 :     if (intended != "" && intended != lines) {
     170          562 :         os.writeAttr(SUMO_ATTR_INTENDED, intended);
     171              :     }
     172          873 :     if (depart >= 0) {
     173         1124 :         os.writeAttr(SUMO_ATTR_DEPART, time2string(depart));
     174              :     }
     175         1746 :     if (options.getBool("exit-times")) {
     176           24 :         os.writeAttr("started", time2string(getStart()));
     177           24 :         os.writeAttr("ended", time2string(getStart() + getDuration()));
     178              :     }
     179         1746 :     if (options.getBool("route-length") && length != -1) {
     180           24 :         os.writeAttr("routeLength", length);
     181              :     }
     182          873 :     os.closeTag(comment);
     183          873 : }
     184              : 
     185              : 
     186              : void
     187          192 : ROPerson::Stop::saveAsXML(OutputDevice& os, const bool /*extended*/, const bool /*asTrip*/, OptionsCont& /*options*/) const {
     188          192 :     stopDesc.write(os, false);
     189          192 :     std::string comment = "";
     190          312 :     for (std::string sID : stopDesc.getStoppingPlaceIDs()) {
     191          120 :         const std::string name = RONet::getInstance()->getStoppingPlaceName(sID);
     192          120 :         if (name != "") {
     193           48 :             comment += name + " ";
     194              :         }
     195          192 :     }
     196          192 :     if (comment != "") {
     197           72 :         comment =  " <!-- " + comment + " -->";
     198              :     }
     199          192 :     stopDesc.writeParams(os);
     200          192 :     os.closeTag(comment);
     201          192 : }
     202              : 
     203              : void
     204         2933 : ROPerson::Walk::saveAsXML(OutputDevice& os, const bool extended, OptionsCont& options) const {
     205         2933 :     os.openTag(SUMO_TAG_WALK);
     206         2933 :     std::string comment = "";
     207         5866 :     if ((extended || options.getBool("write-costs")) && myCost >= 0.) {
     208           32 :         os.writeAttr(SUMO_ATTR_COST, myCost);
     209              :     }
     210         2933 :     if (dur > 0) {
     211            0 :         os.writeAttr(SUMO_ATTR_DURATION, dur);
     212              :     }
     213         2933 :     if (v > 0) {
     214            0 :         os.writeAttr(SUMO_ATTR_SPEED, v);
     215              :     }
     216         2933 :     os.writeAttr(SUMO_ATTR_EDGES, edges);
     217         5866 :     if (options.getBool("exit-times")) {
     218           16 :         os.writeAttr("started", time2string(getStart()));
     219           16 :         os.writeAttr("ended", time2string(getStart() + getDuration()));
     220            8 :         if (!exitTimes.empty()) {
     221           16 :             os.writeAttr("exitTimes", exitTimes);
     222              :         }
     223              :     }
     224         5866 :     if (options.getBool("route-length")) {
     225            8 :         double length = 0;
     226           28 :         for (const ROEdge* roe : edges) {
     227           20 :             length += roe->getLength();
     228              :         }
     229           16 :         os.writeAttr("routeLength", length);
     230              :     }
     231         2933 :     if (destStop != "") {
     232          837 :         const std::string element = RONet::getInstance()->getStoppingPlaceElement(destStop);
     233          837 :         os.writeAttr(element, destStop);
     234          837 :         const std::string name = RONet::getInstance()->getStoppingPlaceName(destStop);
     235          837 :         if (name != "") {
     236         1836 :             comment =  " <!-- " + name + " -->";
     237              :         }
     238         2096 :     } else if (arr != 0 && arr != std::numeric_limits<double>::infinity()) {
     239          455 :         os.writeAttr(SUMO_ATTR_ARRIVALPOS, arr);
     240              :     }
     241         2933 :     os.closeTag(comment);
     242         2933 : }
     243              : 
     244              : ROPerson::PlanItem*
     245          350 : ROPerson::PersonTrip::clone() const {
     246          350 :     PersonTrip* result = new PersonTrip(from, to, modes, dep, stopOrigin, arr, stopDest, walkFactor, group);
     247          370 :     for (auto* item : myTripItems) {
     248           20 :         result->myTripItems.push_back(item->clone());
     249              :     }
     250          350 :     return result;
     251              : }
     252              : 
     253              : void
     254         3449 : ROPerson::PersonTrip::saveVehicles(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options) const {
     255         3657 :     for (ROVehicle* veh : myVehicles) {
     256          208 :         if (!RONet::getInstance()->knowsVehicle(veh->getID())) {
     257          204 :             veh->saveAsXML(os, typeos, asAlternatives, options);
     258              :         }
     259              :     }
     260         3449 : }
     261              : 
     262              : void
     263         4497 : ROPerson::PersonTrip::saveAsXML(OutputDevice& os, const bool extended, const bool asTrip, OptionsCont& options) const {
     264         4497 :     if ((asTrip || extended) && from != nullptr) {
     265         2022 :         const bool writeGeoTrip = asTrip && options.getBool("write-trips.geo");
     266         2022 :         os.openTag(SUMO_TAG_PERSONTRIP);
     267         2022 :         if (writeGeoTrip) {
     268            8 :             Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(getDepartPos());
     269            4 :             if (GeoConvHelper::getFinal().usingGeoProjection()) {
     270            0 :                 os.setPrecision(gPrecisionGeo);
     271            0 :                 GeoConvHelper::getFinal().cartesian2geo(fromPos);
     272            0 :                 os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
     273            0 :                 os.setPrecision(gPrecision);
     274              :             } else {
     275            4 :                 os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
     276              :             }
     277              :         } else {
     278         2018 :             os.writeAttr(SUMO_ATTR_FROM, from->getID());
     279              :         }
     280         2022 :         if (writeGeoTrip) {
     281            8 :             Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(MIN2(getArrivalPos(), to->getLanes()[0]->getShape().length2D()));
     282            4 :             if (GeoConvHelper::getFinal().usingGeoProjection()) {
     283            0 :                 os.setPrecision(gPrecisionGeo);
     284            0 :                 GeoConvHelper::getFinal().cartesian2geo(toPos);
     285            0 :                 os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
     286            0 :                 os.setPrecision(gPrecision);
     287              :             } else {
     288            4 :                 os.writeAttr(SUMO_ATTR_TOXY, toPos);
     289              :             }
     290              :         } else {
     291         2018 :             os.writeAttr(SUMO_ATTR_TO, to->getID());
     292              :         }
     293              :         std::vector<std::string> allowedModes;
     294         2022 :         if ((modes & SVC_BUS) != 0) {
     295         1560 :             allowedModes.push_back("public");
     296              :         }
     297         2022 :         if ((modes & SVC_PASSENGER) != 0) {
     298          852 :             allowedModes.push_back("car");
     299              :         }
     300         2022 :         if ((modes & SVC_TAXI) != 0) {
     301          110 :             allowedModes.push_back("taxi");
     302              :         }
     303         2022 :         if ((modes & SVC_BICYCLE) != 0) {
     304           24 :             allowedModes.push_back("bicycle");
     305              :         }
     306         2022 :         if (allowedModes.size() > 0) {
     307         1944 :             os.writeAttr(SUMO_ATTR_MODES, toString(allowedModes));
     308              :         }
     309         2022 :         if (!writeGeoTrip) {
     310         2018 :             if (dep != 0 && dep != std::numeric_limits<double>::infinity()) {
     311          133 :                 os.writeAttr(SUMO_ATTR_DEPARTPOS, dep);
     312              :             }
     313         2018 :             if (arr != 0 && arr != std::numeric_limits<double>::infinity()) {
     314          249 :                 os.writeAttr(SUMO_ATTR_ARRIVALPOS, arr);
     315              :             }
     316              :         }
     317         2022 :         if (getStopDest() != "") {
     318          104 :             os.writeAttr(SUMO_ATTR_BUS_STOP, getStopDest());
     319              :         }
     320         2022 :         if (walkFactor != 1) {
     321         2022 :             os.writeAttr(SUMO_ATTR_WALKFACTOR, walkFactor);
     322              :         }
     323         2022 :         if (extended && myTripItems.size() != 0) {
     324              :             std::vector<double> costs;
     325         2382 :             for (const TripItem* const tripItem : myTripItems) {
     326         1394 :                 costs.push_back(tripItem->getCost());
     327              :             }
     328          988 :             os.writeAttr(SUMO_ATTR_COSTS, costs);
     329          988 :         }
     330         2022 :         os.closeTag();
     331         2022 :     } else {
     332         6281 :         for (const TripItem* const it : myTripItems) {
     333         3806 :             it->saveAsXML(os, extended, options);
     334              :         }
     335              :     }
     336         4497 : }
     337              : 
     338              : SUMOTime
     339         3412 : ROPerson::PersonTrip::getDuration() const {
     340              :     SUMOTime result = 0;
     341         8633 :     for (TripItem* tItem : myTripItems) {
     342         5221 :         result += tItem->getDuration();
     343              :     }
     344         3412 :     return result;
     345              : }
     346              : 
     347              : bool
     348         3244 : ROPerson::computeIntermodal(SUMOTime time, const RORouterProvider& provider,
     349              :                             const PersonTrip* const trip, const ROVehicle* const veh,
     350              :                             std::vector<TripItem*>& resultItems, MsgHandler* const errorHandler) {
     351         3244 :     const double speed = getMaxSpeed() * trip->getWalkFactor();
     352              :     std::vector<ROIntermodalRouter::TripItem> result;
     353         3244 :     provider.getIntermodalRouter().compute(trip->getOrigin(), trip->getDestination(),
     354              :                                            trip->getDepartPos(), trip->getStopOrigin(),
     355         3244 :                                            trip->getArrivalPos(), trip->getStopDest(),
     356              :                                            speed, veh, *getType(), trip->getModes(), time, result);
     357              :     bool carUsed = false;
     358              :     SUMOTime start = time;
     359              :     int index = 0;
     360         3244 :     const int lastIndex = (int)result.size() - 1;
     361         8377 :     for (const ROIntermodalRouter::TripItem& item : result) {
     362         5133 :         if (!item.edges.empty()) {
     363         5049 :             if (item.line == "") {
     364              :                 double depPos = trip->getDepartPos(false);
     365              :                 double arrPos = trip->getArrivalPos(false);
     366         4097 :                 if (trip->getOrigin()->isTazConnector()) {
     367              :                     // walk the whole length of the first edge
     368          480 :                     const ROEdge* first = item.edges.front();
     369          480 :                     if (std::find(first->getPredecessors().begin(), first->getPredecessors().end(), trip->getOrigin()) != first->getPredecessors().end()) {
     370              :                         depPos = 0;
     371              :                     } else {
     372              :                         depPos = first->getLength();
     373              :                     }
     374              :                 }
     375         4097 :                 if (trip->getDestination()->isTazConnector()) {
     376              :                     // walk the whole length of the last edge
     377          476 :                     const ROEdge* last = item.edges.back();
     378          476 :                     if (std::find(last->getSuccessors().begin(), last->getSuccessors().end(), trip->getDestination()) != last->getSuccessors().end()) {
     379              :                         arrPos = last->getLength();
     380              :                     } else {
     381              :                         arrPos = 0;
     382              :                     }
     383              :                 }
     384         4097 :                 if (&item == &result.back() && trip->getStopDest() == "") {
     385         5894 :                     resultItems.push_back(new Walk(start, item.edges, item.cost, item.exitTimes, depPos, arrPos));
     386              :                 } else {
     387         1150 :                     resultItems.push_back(new Walk(start, item.edges, item.cost, item.exitTimes, depPos, arrPos, item.destStop));
     388              :                 }
     389          952 :             } else if (veh != nullptr && item.line == veh->getID()) {
     390          174 :                 double cost = item.cost;
     391          174 :                 if (veh->getVClass() != SVC_TAXI) {
     392          116 :                     RORoute* route = new RORoute(veh->getID() + "_RouteDef", item.edges);
     393          116 :                     route->setProbability(1);
     394          116 :                     veh->getRouteDefinition()->addLoadedAlternative(route);
     395              :                     carUsed = true;
     396           58 :                 } else if (resultItems.empty()) {
     397              :                     // if this is the first plan item the initial taxi waiting time wasn't added yet
     398           38 :                     const double taxiWait = STEPS2TIME(string2time(OptionsCont::getOptions().getString("persontrip.taxi.waiting-time")));
     399           38 :                     cost += taxiWait;
     400              :                 }
     401          174 :                 double arrPos = index == lastIndex ? trip->getArrivalPos(false) : item.arrivalPos;
     402          348 :                 resultItems.push_back(new Ride(start, item.edges.front(), item.edges.back(), veh->getID(), trip->getGroup(), cost, arrPos, item.length, item.destStop));
     403              :             } else {
     404              :                 // write origin for first element of the plan
     405          778 :                 const ROEdge* origin = trip == myPlan.front() && resultItems.empty() ? trip->getOrigin() : nullptr;
     406          778 :                 const std::string line = OptionsCont::getOptions().getBool("persontrip.ride-public-line") ? item.line : LINE_ANY;
     407          778 :                 resultItems.push_back(new Ride(start, origin, nullptr, line, trip->getGroup(), item.cost, item.arrivalPos, item.length, item.destStop, item.intended, TIME2STEPS(item.depart)));
     408              :             }
     409              :         }
     410         5133 :         start += TIME2STEPS(item.cost);
     411         5133 :         index++;
     412              :     }
     413         3244 :     if (result.empty()) {
     414          134 :         errorHandler->inform("No route for trip in person '" + getID() + "'.");
     415           67 :         myRoutingSuccess = false;
     416              :     }
     417         3244 :     return carUsed;
     418         3244 : }
     419              : 
     420              : 
     421              : void
     422         3336 : ROPerson::computeRoute(const RORouterProvider& provider,
     423              :                        const bool /* removeLoops */, MsgHandler* errorHandler) {
     424         3336 :     myRoutingSuccess = true;
     425         3336 :     SUMOTime time = getParameter().depart;
     426         6876 :     for (PlanItem* const it : myPlan) {
     427         3540 :         if (it->needsRouting()) {
     428              :             PersonTrip* trip = static_cast<PersonTrip*>(it);
     429              :             const std::vector<ROVehicle*>& vehicles = trip->getVehicles();
     430              :             std::vector<TripItem*> resultItems;
     431              :             std::vector<TripItem*> best;
     432              :             const ROVehicle* bestVeh = nullptr;
     433         3236 :             if (vehicles.empty()) {
     434         2526 :                 computeIntermodal(time, provider, trip, nullptr, best, errorHandler);
     435              :             } else {
     436              :                 double bestCost = std::numeric_limits<double>::infinity();
     437         1428 :                 for (const ROVehicle* const v : vehicles) {
     438          718 :                     const bool carUsed = computeIntermodal(time, provider, trip, v, resultItems, errorHandler);
     439              :                     double cost = 0.;
     440         1546 :                     for (const TripItem* const tripIt : resultItems) {
     441          828 :                         cost += tripIt->getCost();
     442              :                     }
     443          718 :                     if (cost < bestCost) {
     444              :                         bestCost = cost;
     445          710 :                         bestVeh = carUsed ? v : nullptr;
     446              :                         best.swap(resultItems);
     447              :                     }
     448          726 :                     for (const TripItem* const tripIt : resultItems) {
     449            8 :                         delete tripIt;
     450              :                     }
     451              :                     resultItems.clear();
     452              :                 }
     453              :             }
     454         3236 :             trip->setItems(best, bestVeh);
     455              : 
     456              :             // test after routing to ensuer network was initialized
     457         3236 :             if (!myHaveWarnedPTMissing && (trip->getModes() & SVC_BUS) != 0) {
     458          223 :                 myHaveWarnedPTMissing = true;
     459          223 :                 if (!provider.getIntermodalRouter().getNetwork()->hasPTSchedules()) {
     460          162 :                     WRITE_WARNINGF("Person '%' is configured to use public transport but no schedules are loaded.", getID());
     461              :                 }
     462              :             }
     463         3236 :         }
     464         3540 :         time += it->getDuration();
     465              :     }
     466         3336 :     if (RONet::getInstance()->getMaxTraveltime() > 0) {
     467            8 :         double costs = STEPS2TIME(time - getParameter().depart);
     468            8 :         if (costs > RONet::getInstance()->getMaxTraveltime()) {
     469           16 :             errorHandler->inform("Person '" + getID() + "' has no valid route (traveltime " + time2string(TIME2STEPS(costs)) + " exceeds max-traveltime)");
     470            4 :             myRoutingSuccess = false;
     471            4 :             return;
     472              :         }
     473              :     }
     474              : }
     475              : 
     476              : 
     477              : void
     478         4409 : ROPerson::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
     479              :     // write the person's vehicles
     480         8818 :     const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
     481         8818 :     const bool writeFlow = options.exists("keep-flows") && options.getBool("keep-flows") && isPartOfFlow();
     482         4409 :     if (!writeTrip) {
     483         7008 :         for (const PlanItem* const it : myPlan) {
     484         3613 :             it->saveVehicles(os, typeos, asAlternatives, options);
     485              :         }
     486              :     }
     487              : 
     488         4409 :     if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
     489           74 :         getType()->write(*typeos);
     490           74 :         getType()->saved = true;
     491              :     }
     492         4409 :     if (getType() != nullptr && !getType()->saved) {
     493         1170 :         getType()->write(os);
     494         1170 :         getType()->saved = asAlternatives;
     495              :     }
     496         4409 :     const SumoXMLTag tag = writeFlow ? SUMO_TAG_PERSONFLOW : SUMO_TAG_PERSON;
     497              :     // write the person
     498         4409 :     if (cloneIndex == 0) {
     499         8754 :         getParameter().write(os, options, tag);
     500              :     } else {
     501           32 :         SUMOVehicleParameter p = getParameter();
     502              :         // @note id collisions may occur if scale-suffic occurs in other vehicle ids
     503           64 :         p.id += options.getString("scale-suffix") + toString(cloneIndex);
     504           32 :         p.write(os, options, tag);
     505           32 :     }
     506              : 
     507         9098 :     for (const PlanItem* const it : myPlan) {
     508         4689 :         it->saveAsXML(os, asAlternatives, writeTrip, options);
     509              :     }
     510              : 
     511              :     // write params
     512         4409 :     getParameter().writeParams(os);
     513         4409 :     os.closeTag();
     514         4409 : }
     515              : 
     516              : 
     517              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1