Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2002-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 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 : const std::string ROPerson::PlanItem::UNDEFINED_STOPPING_PLACE;
44 :
45 : // ===========================================================================
46 : // method definitions
47 : // ===========================================================================
48 1703 : ROPerson::ROPerson(const SUMOVehicleParameter& pars, const SUMOVTypeParameter* type)
49 1703 : : RORoutable(pars, type) {
50 1703 : }
51 :
52 :
53 3406 : ROPerson::~ROPerson() {
54 3526 : for (PlanItem* const it : myPlan) {
55 1823 : delete it;
56 : }
57 3406 : }
58 :
59 :
60 : void
61 1247 : ROPerson::addTrip(std::vector<PlanItem*>& plan, const std::string& id,
62 : const ROEdge* const from, const ROEdge* const to, const SVCPermissions modeSet,
63 : const std::string& vTypes,
64 : const double departPos, const std::string& stopOrigin,
65 : const double arrivalPos, const std::string& busStop,
66 : double walkFactor, const std::string& group) {
67 1247 : PersonTrip* trip = new PersonTrip(from, to, modeSet, departPos, stopOrigin, arrivalPos, busStop, walkFactor, group);
68 1247 : RONet* net = RONet::getInstance();
69 1247 : SUMOVehicleParameter pars;
70 1247 : pars.departProcedure = DepartDefinition::TRIGGERED;
71 1247 : if (departPos != 0) {
72 96 : pars.departPosProcedure = DepartPosDefinition::GIVEN;
73 96 : pars.departPos = departPos;
74 96 : pars.parametersSet |= VEHPARS_DEPARTPOS_SET;
75 : }
76 2534 : for (StringTokenizer st(vTypes); st.hasNext();) {
77 40 : pars.vtypeid = st.next();
78 40 : pars.parametersSet |= VEHPARS_VTYPE_SET;
79 40 : const SUMOVTypeParameter* type = net->getVehicleTypeSecure(pars.vtypeid);
80 40 : if (type == nullptr) {
81 0 : delete trip;
82 0 : throw InvalidArgument("The vehicle type '" + pars.vtypeid + "' in a trip for person '" + id + "' is not known.");
83 : }
84 80 : pars.id = id + "_" + toString(trip->getVehicles().size());
85 80 : trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), type, net));
86 : // update modeset with routing-category vClass
87 40 : if (type->vehicleClass == SVC_BICYCLE) {
88 : trip->updateModes(SVC_BICYCLE);
89 : } else {
90 : trip->updateModes(SVC_PASSENGER);
91 : }
92 1247 : }
93 1247 : if (trip->getVehicles().empty()) {
94 1207 : if ((modeSet & SVC_PASSENGER) != 0) {
95 390 : pars.id = id + "_0";
96 780 : trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_VTYPE_ID), net));
97 : }
98 1207 : if ((modeSet & SVC_BICYCLE) != 0) {
99 24 : pars.id = id + "_b0";
100 : pars.vtypeid = DEFAULT_BIKETYPE_ID;
101 12 : pars.parametersSet |= VEHPARS_VTYPE_SET;
102 24 : trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_BIKETYPE_ID), net));
103 : }
104 1207 : if ((modeSet & SVC_TAXI) != 0) {
105 : // add dummy taxi for routing (never added to output)
106 : pars.id = "taxi"; // id is written as 'line'
107 : pars.vtypeid = DEFAULT_TAXITYPE_ID;
108 96 : trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_TAXITYPE_ID), net));
109 : }
110 : }
111 1247 : plan.push_back(trip);
112 1247 : }
113 :
114 :
115 : void
116 56 : ROPerson::addRide(std::vector<PlanItem*>& plan, const ROEdge* const from, const ROEdge* const to, const std::string& lines,
117 : double arrivalPos, const std::string& destStop, const std::string& group) {
118 168 : plan.push_back(new PersonTrip(to, destStop));
119 56 : plan.back()->addTripItem(new Ride(-1, from, to, lines, group, -1., arrivalPos, -1., destStop));
120 56 : }
121 :
122 :
123 : void
124 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) {
125 72 : if (plan.empty() || plan.back()->isStop()) {
126 204 : plan.push_back(new PersonTrip(edges.back(), busStop));
127 : }
128 72 : plan.back()->addTripItem(new Walk(-1, edges, -1., duration, speed, departPos, arrivalPos, busStop));
129 72 : }
130 :
131 :
132 : void
133 108 : ROPerson::addStop(std::vector<PlanItem*>& plan, const SUMOVehicleParameter::Stop& stopPar, const ROEdge* const stopEdge) {
134 108 : plan.push_back(new Stop(stopPar, stopEdge));
135 108 : }
136 :
137 :
138 : void
139 592 : ROPerson::Ride::saveAsXML(OutputDevice& os, const bool extended, OptionsCont& options) const {
140 592 : os.openTag(SUMO_TAG_RIDE);
141 592 : std::string comment = "";
142 1184 : if ((extended || options.getBool("write-costs")) && myCost >= 0.) {
143 20 : os.writeAttr(SUMO_ATTR_COST, myCost);
144 : }
145 592 : if (from != nullptr) {
146 : os.writeAttr(SUMO_ATTR_FROM, from->getID());
147 : }
148 592 : if (to != nullptr) {
149 : os.writeAttr(SUMO_ATTR_TO, to->getID());
150 : }
151 592 : if (destStop != "") {
152 460 : const std::string element = RONet::getInstance()->getStoppingPlaceElement(destStop);
153 : os.writeAttr(element, destStop);
154 460 : const std::string name = RONet::getInstance()->getStoppingPlaceName(destStop);
155 460 : if (name != "") {
156 645 : comment = " <!-- " + name + " -->";
157 : }
158 132 : } else if (arrPos != 0 && arrPos != std::numeric_limits<double>::infinity()) {
159 36 : os.writeAttr(SUMO_ATTR_ARRIVALPOS, arrPos);
160 : }
161 592 : os.writeAttr(SUMO_ATTR_LINES, lines);
162 592 : if (group != "") {
163 : os.writeAttr(SUMO_ATTR_GROUP, group);
164 : }
165 592 : if (intended != "" && intended != lines) {
166 : os.writeAttr(SUMO_ATTR_INTENDED, intended);
167 : }
168 592 : if (depart >= 0) {
169 720 : os.writeAttr(SUMO_ATTR_DEPART, time2string(depart));
170 : }
171 1184 : if (options.getBool("exit-times")) {
172 24 : os.writeAttr("started", time2string(getStart()));
173 24 : os.writeAttr("ended", time2string(getStart() + getDuration()));
174 : }
175 1184 : if (options.getBool("route-length") && length != -1) {
176 24 : os.writeAttr("routeLength", length);
177 : }
178 592 : os.closeTag(comment);
179 592 : }
180 :
181 :
182 : void
183 172 : ROPerson::Stop::saveAsXML(OutputDevice& os, const bool /*extended*/, const bool /*asTrip*/, OptionsCont& /*options*/) const {
184 172 : stopDesc.write(os, false);
185 172 : std::string comment = "";
186 272 : for (std::string sID : stopDesc.getStoppingPlaceIDs()) {
187 100 : const std::string name = RONet::getInstance()->getStoppingPlaceName(sID);
188 100 : if (name != "") {
189 48 : comment += name + " ";
190 : }
191 172 : }
192 172 : if (comment != "") {
193 72 : comment = " <!-- " + comment + " -->";
194 : }
195 172 : stopDesc.writeParams(os);
196 172 : os.closeTag(comment);
197 172 : }
198 :
199 : void
200 2017 : ROPerson::Walk::saveAsXML(OutputDevice& os, const bool extended, OptionsCont& options) const {
201 2017 : os.openTag(SUMO_TAG_WALK);
202 2017 : std::string comment = "";
203 4034 : if ((extended || options.getBool("write-costs")) && myCost >= 0.) {
204 32 : os.writeAttr(SUMO_ATTR_COST, myCost);
205 : }
206 2017 : if (dur > 0) {
207 0 : os.writeAttr(SUMO_ATTR_DURATION, dur);
208 : }
209 2017 : if (v > 0) {
210 0 : os.writeAttr(SUMO_ATTR_SPEED, v);
211 : }
212 2017 : os.writeAttr(SUMO_ATTR_EDGES, edges);
213 4034 : if (options.getBool("exit-times")) {
214 16 : os.writeAttr("started", time2string(getStart()));
215 16 : os.writeAttr("ended", time2string(getStart() + getDuration()));
216 8 : if (!exitTimes.empty()) {
217 16 : os.writeAttr("exitTimes", exitTimes);
218 : }
219 : }
220 4034 : if (options.getBool("route-length")) {
221 8 : double length = 0;
222 28 : for (const ROEdge* roe : edges) {
223 20 : length += roe->getLength();
224 : }
225 16 : os.writeAttr("routeLength", length);
226 : }
227 2017 : if (destStop != "") {
228 522 : const std::string element = RONet::getInstance()->getStoppingPlaceElement(destStop);
229 : os.writeAttr(element, destStop);
230 522 : const std::string name = RONet::getInstance()->getStoppingPlaceName(destStop);
231 522 : if (name != "") {
232 915 : comment = " <!-- " + name + " -->";
233 : }
234 1495 : } else if (arr != 0 && arr != std::numeric_limits<double>::infinity()) {
235 336 : os.writeAttr(SUMO_ATTR_ARRIVALPOS, arr);
236 : }
237 2017 : os.closeTag(comment);
238 2017 : }
239 :
240 : ROPerson::PlanItem*
241 392 : ROPerson::PersonTrip::clone() const {
242 392 : PersonTrip* result = new PersonTrip(from, to, modes, dep, stopOrigin, arr, stopDest, walkFactor, group);
243 412 : for (auto* item : myTripItems) {
244 20 : result->myTripItems.push_back(item->clone());
245 : }
246 392 : return result;
247 : }
248 :
249 : void
250 2699 : ROPerson::PersonTrip::saveVehicles(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options) const {
251 2899 : for (ROVehicle* veh : myVehicles) {
252 200 : if (!RONet::getInstance()->knowsVehicle(veh->getID())) {
253 196 : veh->saveAsXML(os, typeos, asAlternatives, options);
254 : }
255 : }
256 2699 : }
257 :
258 : void
259 2731 : ROPerson::PersonTrip::saveAsXML(OutputDevice& os, const bool extended, const bool asTrip, OptionsCont& options) const {
260 2731 : if ((asTrip || extended) && from != nullptr) {
261 932 : const bool writeGeoTrip = asTrip && options.getBool("write-trips.geo");
262 932 : os.openTag(SUMO_TAG_PERSONTRIP);
263 932 : if (writeGeoTrip) {
264 8 : Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(getDepartPos());
265 4 : if (GeoConvHelper::getFinal().usingGeoProjection()) {
266 0 : os.setPrecision(gPrecisionGeo);
267 0 : GeoConvHelper::getFinal().cartesian2geo(fromPos);
268 : os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
269 0 : os.setPrecision(gPrecision);
270 : } else {
271 : os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
272 : }
273 : } else {
274 928 : os.writeAttr(SUMO_ATTR_FROM, from->getID());
275 : }
276 932 : if (writeGeoTrip) {
277 8 : Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(MIN2(getArrivalPos(), to->getLanes()[0]->getShape().length2D()));
278 4 : if (GeoConvHelper::getFinal().usingGeoProjection()) {
279 0 : os.setPrecision(gPrecisionGeo);
280 0 : GeoConvHelper::getFinal().cartesian2geo(toPos);
281 : os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
282 0 : os.setPrecision(gPrecision);
283 : } else {
284 : os.writeAttr(SUMO_ATTR_TOXY, toPos);
285 : }
286 : } else {
287 928 : os.writeAttr(SUMO_ATTR_TO, to->getID());
288 : }
289 : std::vector<std::string> allowedModes;
290 932 : if ((modes & SVC_BUS) != 0) {
291 572 : allowedModes.push_back("public");
292 : }
293 932 : if ((modes & SVC_PASSENGER) != 0) {
294 404 : allowedModes.push_back("car");
295 : }
296 932 : if ((modes & SVC_TAXI) != 0) {
297 96 : allowedModes.push_back("taxi");
298 : }
299 932 : if ((modes & SVC_BICYCLE) != 0) {
300 24 : allowedModes.push_back("bicycle");
301 : }
302 932 : if (allowedModes.size() > 0) {
303 934 : os.writeAttr(SUMO_ATTR_MODES, toString(allowedModes));
304 : }
305 932 : if (!writeGeoTrip) {
306 928 : if (dep != 0 && dep != std::numeric_limits<double>::infinity()) {
307 68 : os.writeAttr(SUMO_ATTR_DEPARTPOS, dep);
308 : }
309 928 : if (arr != 0 && arr != std::numeric_limits<double>::infinity()) {
310 120 : os.writeAttr(SUMO_ATTR_ARRIVALPOS, arr);
311 : }
312 : }
313 932 : if (getStopDest() != "") {
314 84 : os.writeAttr(SUMO_ATTR_BUS_STOP, getStopDest());
315 : }
316 932 : if (walkFactor != 1) {
317 932 : os.writeAttr(SUMO_ATTR_WALKFACTOR, walkFactor);
318 : }
319 932 : if (extended && myTripItems.size() != 0) {
320 : std::vector<double> costs;
321 2186 : for (const TripItem* const tripItem : myTripItems) {
322 1282 : costs.push_back(tripItem->getCost());
323 : }
324 : os.writeAttr(SUMO_ATTR_COSTS, costs);
325 904 : }
326 932 : os.closeTag();
327 932 : } else {
328 4408 : for (const TripItem* const it : myTripItems) {
329 2609 : it->saveAsXML(os, extended, options);
330 : }
331 : }
332 2731 : }
333 :
334 : SUMOTime
335 1707 : ROPerson::PersonTrip::getDuration() const {
336 : SUMOTime result = 0;
337 4188 : for (TripItem* tItem : myTripItems) {
338 2481 : result += tItem->getDuration();
339 : }
340 1707 : return result;
341 : }
342 :
343 : bool
344 1575 : ROPerson::computeIntermodal(SUMOTime time, const RORouterProvider& provider,
345 : const PersonTrip* const trip, const ROVehicle* const veh,
346 : std::vector<TripItem*>& resultItems, MsgHandler* const errorHandler) {
347 1575 : const double speed = getMaxSpeed() * trip->getWalkFactor();
348 : std::vector<ROIntermodalRouter::TripItem> result;
349 1575 : provider.getIntermodalRouter().compute(trip->getOrigin(), trip->getDestination(),
350 : trip->getDepartPos(), trip->getStopOrigin(),
351 1575 : trip->getArrivalPos(), trip->getStopDest(),
352 : speed, veh, trip->getModes(), time, result);
353 : bool carUsed = false;
354 : SUMOTime start = time;
355 3984 : for (const ROIntermodalRouter::TripItem& item : result) {
356 2409 : if (!item.edges.empty()) {
357 2345 : if (item.line == "") {
358 : double depPos = trip->getDepartPos(false);
359 : double arrPos = trip->getArrivalPos(false);
360 1845 : if (trip->getOrigin()->isTazConnector()) {
361 : // walk the whole length of the first edge
362 256 : const ROEdge* first = item.edges.front();
363 256 : if (std::find(first->getPredecessors().begin(), first->getPredecessors().end(), trip->getOrigin()) != first->getPredecessors().end()) {
364 : depPos = 0;
365 : } else {
366 : depPos = first->getLength();
367 : }
368 : }
369 1845 : if (trip->getDestination()->isTazConnector()) {
370 : // walk the whole length of the last edge
371 256 : const ROEdge* last = item.edges.back();
372 256 : if (std::find(last->getSuccessors().begin(), last->getSuccessors().end(), trip->getDestination()) != last->getSuccessors().end()) {
373 : arrPos = last->getLength();
374 : } else {
375 : arrPos = 0;
376 : }
377 : }
378 1845 : if (&item == &result.back() && trip->getStopDest() == "") {
379 2734 : resultItems.push_back(new Walk(start, item.edges, item.cost, item.exitTimes, depPos, arrPos));
380 : } else {
381 478 : resultItems.push_back(new Walk(start, item.edges, item.cost, item.exitTimes, depPos, arrPos, item.destStop));
382 : }
383 500 : } else if (veh != nullptr && item.line == veh->getID()) {
384 140 : double cost = item.cost;
385 140 : if (veh->getVClass() != SVC_TAXI) {
386 112 : RORoute* route = new RORoute(veh->getID() + "_RouteDef", item.edges);
387 112 : route->setProbability(1);
388 112 : veh->getRouteDefinition()->addLoadedAlternative(route);
389 : carUsed = true;
390 28 : } else if (resultItems.empty()) {
391 : // if this is the first plan item the initial taxi waiting time wasn't added yet
392 12 : const double taxiWait = STEPS2TIME(string2time(OptionsCont::getOptions().getString("persontrip.taxi.waiting-time")));
393 12 : cost += taxiWait;
394 : }
395 280 : resultItems.push_back(new Ride(start, item.edges.front(), item.edges.back(), veh->getID(), trip->getGroup(), cost, item.arrivalPos, item.length, item.destStop));
396 : } else {
397 : // write origin for first element of the plan
398 360 : const ROEdge* origin = trip == myPlan.front() && resultItems.empty() ? trip->getOrigin() : nullptr;
399 360 : resultItems.push_back(new Ride(start, origin, nullptr, item.line, trip->getGroup(), item.cost, item.arrivalPos, item.length, item.destStop, item.intended, TIME2STEPS(item.depart)));
400 : }
401 : }
402 2409 : start += TIME2STEPS(item.cost);
403 : }
404 1575 : if (result.empty()) {
405 64 : errorHandler->inform("No route for trip in person '" + getID() + "'.");
406 32 : myRoutingSuccess = false;
407 : }
408 1575 : return carUsed;
409 1575 : }
410 :
411 :
412 : void
413 1695 : ROPerson::computeRoute(const RORouterProvider& provider,
414 : const bool /* removeLoops */, MsgHandler* errorHandler) {
415 1695 : myRoutingSuccess = true;
416 1695 : SUMOTime time = getParameter().depart;
417 3510 : for (PlanItem* const it : myPlan) {
418 1815 : if (it->needsRouting()) {
419 : PersonTrip* trip = static_cast<PersonTrip*>(it);
420 : const std::vector<ROVehicle*>& vehicles = trip->getVehicles();
421 : std::vector<TripItem*> resultItems;
422 : std::vector<TripItem*> best;
423 : const ROVehicle* bestVeh = nullptr;
424 1567 : if (vehicles.empty()) {
425 1105 : computeIntermodal(time, provider, trip, nullptr, best, errorHandler);
426 : } else {
427 : double bestCost = std::numeric_limits<double>::infinity();
428 932 : for (const ROVehicle* const v : vehicles) {
429 470 : const bool carUsed = computeIntermodal(time, provider, trip, v, resultItems, errorHandler);
430 : double cost = 0.;
431 1068 : for (const TripItem* const tripIt : resultItems) {
432 598 : cost += tripIt->getCost();
433 : }
434 470 : if (cost < bestCost) {
435 : bestCost = cost;
436 462 : bestVeh = carUsed ? v : nullptr;
437 : best.swap(resultItems);
438 : }
439 478 : for (const TripItem* const tripIt : resultItems) {
440 8 : delete tripIt;
441 : }
442 : resultItems.clear();
443 : }
444 : }
445 1567 : trip->setItems(best, bestVeh);
446 1567 : }
447 1815 : time += it->getDuration();
448 : }
449 1695 : }
450 :
451 :
452 : void
453 2711 : ROPerson::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
454 : // write the person's vehicles
455 5422 : const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
456 2711 : if (!writeTrip) {
457 5546 : for (const PlanItem* const it : myPlan) {
458 2863 : it->saveVehicles(os, typeos, asAlternatives, options);
459 : }
460 : }
461 :
462 2711 : if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
463 29 : getType()->write(*typeos);
464 29 : getType()->saved = true;
465 : }
466 2711 : if (getType() != nullptr && !getType()->saved) {
467 986 : getType()->write(os);
468 986 : getType()->saved = asAlternatives;
469 : }
470 :
471 : // write the person
472 2711 : if (cloneIndex == 0) {
473 5358 : getParameter().write(os, options, SUMO_TAG_PERSON);
474 : } else {
475 32 : SUMOVehicleParameter p = getParameter();
476 : // @note id collisions may occur if scale-suffic occurs in other vehicle ids
477 64 : p.id += options.getString("scale-suffix") + toString(cloneIndex);
478 32 : p.write(os, options, SUMO_TAG_PERSON);
479 32 : }
480 :
481 5614 : for (const PlanItem* const it : myPlan) {
482 2903 : it->saveAsXML(os, asAlternatives, writeTrip, options);
483 : }
484 :
485 : // write params
486 2711 : getParameter().writeParams(os);
487 2711 : os.closeTag();
488 2711 : }
489 :
490 :
491 : /****************************************************************************/
|