Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/router/ROPerson.cpp
193735 views
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
ROPerson::ROPerson(const SUMOVehicleParameter& pars, const SUMOVTypeParameter* type)
50
: RORoutable(pars, type) {
51
}
52
53
54
ROPerson::~ROPerson() {
55
for (PlanItem* const it : myPlan) {
56
delete it;
57
}
58
}
59
60
61
void
62
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
PersonTrip* trip = new PersonTrip(from, to, modeSet, departPos, stopOrigin, arrivalPos, busStop, walkFactor, group);
69
RONet* net = RONet::getInstance();
70
SUMOVehicleParameter pars;
71
pars.departProcedure = DepartDefinition::TRIGGERED;
72
if (departPos != 0) {
73
pars.departPosProcedure = DepartPosDefinition::GIVEN;
74
pars.departPos = departPos;
75
pars.parametersSet |= VEHPARS_DEPARTPOS_SET;
76
}
77
for (StringTokenizer st(vTypes); st.hasNext();) {
78
pars.vtypeid = st.next();
79
pars.parametersSet |= VEHPARS_VTYPE_SET;
80
const SUMOVTypeParameter* type = net->getVehicleTypeSecure(pars.vtypeid);
81
if (type == nullptr) {
82
delete trip;
83
throw InvalidArgument("The vehicle type '" + pars.vtypeid + "' in a trip for person '" + id + "' is not known.");
84
}
85
pars.id = id + "_" + toString(trip->getVehicles().size());
86
trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), type, net));
87
// update modeset with routing-category vClass
88
if (type->vehicleClass == SVC_BICYCLE) {
89
trip->updateModes(SVC_BICYCLE);
90
} else {
91
trip->updateModes(SVC_PASSENGER);
92
}
93
}
94
if (trip->getVehicles().empty()) {
95
if ((modeSet & SVC_PASSENGER) != 0) {
96
pars.id = id + "_0";
97
trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_VTYPE_ID), net));
98
}
99
if ((modeSet & SVC_BICYCLE) != 0) {
100
pars.id = id + "_b0";
101
pars.vtypeid = DEFAULT_BIKETYPE_ID;
102
pars.parametersSet |= VEHPARS_VTYPE_SET;
103
trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_BIKETYPE_ID), net));
104
}
105
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
trip->addVehicle(new ROVehicle(pars, new RORouteDef("!" + pars.id, 0, false, false), net->getVehicleTypeSecure(DEFAULT_TAXITYPE_ID), net));
110
}
111
}
112
plan.push_back(trip);
113
}
114
115
116
void
117
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
plan.push_back(new PersonTrip(to, destStop));
120
plan.back()->addTripItem(new Ride(-1, from, to, lines, group, -1., arrivalPos, -1., destStop));
121
}
122
123
124
void
125
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
if (plan.empty() || plan.back()->isStop()) {
127
plan.push_back(new PersonTrip(edges.back(), busStop));
128
}
129
plan.back()->addTripItem(new Walk(-1, edges, -1., duration, speed, departPos, arrivalPos, busStop));
130
}
131
132
133
void
134
ROPerson::addStop(std::vector<PlanItem*>& plan, const SUMOVehicleParameter::Stop& stopPar, const ROEdge* const stopEdge) {
135
plan.push_back(new Stop(stopPar, stopEdge));
136
}
137
138
139
void
140
ROPerson::Ride::saveAsXML(OutputDevice& os, const bool extended, OptionsCont& options) const {
141
os.openTag(SUMO_TAG_RIDE);
142
std::string comment = "";
143
if ((extended || options.getBool("write-costs")) && myCost >= 0.) {
144
os.writeAttr(SUMO_ATTR_COST, myCost);
145
}
146
if (from != nullptr) {
147
os.writeAttr(SUMO_ATTR_FROM, from->getID());
148
}
149
if (to != nullptr) {
150
os.writeAttr(SUMO_ATTR_TO, to->getID());
151
}
152
if (destStop != "") {
153
const std::string element = RONet::getInstance()->getStoppingPlaceElement(destStop);
154
os.writeAttr(element, destStop);
155
const std::string name = RONet::getInstance()->getStoppingPlaceName(destStop);
156
if (name != "") {
157
comment = " <!-- " + name + " -->";
158
}
159
} else if (arrPos != 0 && arrPos != std::numeric_limits<double>::infinity()) {
160
os.writeAttr(SUMO_ATTR_ARRIVALPOS, arrPos);
161
}
162
if (lines != LINE_ANY) {
163
// no need to write the default
164
os.writeAttr(SUMO_ATTR_LINES, lines);
165
}
166
if (group != "") {
167
os.writeAttr(SUMO_ATTR_GROUP, group);
168
}
169
if (intended != "" && intended != lines) {
170
os.writeAttr(SUMO_ATTR_INTENDED, intended);
171
}
172
if (depart >= 0) {
173
os.writeAttr(SUMO_ATTR_DEPART, time2string(depart));
174
}
175
if (options.getBool("exit-times")) {
176
os.writeAttr("started", time2string(getStart()));
177
os.writeAttr("ended", time2string(getStart() + getDuration()));
178
}
179
if (options.getBool("route-length") && length != -1) {
180
os.writeAttr("routeLength", length);
181
}
182
os.closeTag(comment);
183
}
184
185
186
void
187
ROPerson::Stop::saveAsXML(OutputDevice& os, const bool /*extended*/, const bool /*asTrip*/, OptionsCont& /*options*/) const {
188
stopDesc.write(os, false);
189
std::string comment = "";
190
for (std::string sID : stopDesc.getStoppingPlaceIDs()) {
191
const std::string name = RONet::getInstance()->getStoppingPlaceName(sID);
192
if (name != "") {
193
comment += name + " ";
194
}
195
}
196
if (comment != "") {
197
comment = " <!-- " + comment + " -->";
198
}
199
stopDesc.writeParams(os);
200
os.closeTag(comment);
201
}
202
203
void
204
ROPerson::Walk::saveAsXML(OutputDevice& os, const bool extended, OptionsCont& options) const {
205
os.openTag(SUMO_TAG_WALK);
206
std::string comment = "";
207
if ((extended || options.getBool("write-costs")) && myCost >= 0.) {
208
os.writeAttr(SUMO_ATTR_COST, myCost);
209
}
210
if (dur > 0) {
211
os.writeAttr(SUMO_ATTR_DURATION, dur);
212
}
213
if (v > 0) {
214
os.writeAttr(SUMO_ATTR_SPEED, v);
215
}
216
os.writeAttr(SUMO_ATTR_EDGES, edges);
217
if (options.getBool("exit-times")) {
218
os.writeAttr("started", time2string(getStart()));
219
os.writeAttr("ended", time2string(getStart() + getDuration()));
220
if (!exitTimes.empty()) {
221
os.writeAttr("exitTimes", exitTimes);
222
}
223
}
224
if (options.getBool("route-length")) {
225
double length = 0;
226
for (const ROEdge* roe : edges) {
227
length += roe->getLength();
228
}
229
os.writeAttr("routeLength", length);
230
}
231
if (destStop != "") {
232
const std::string element = RONet::getInstance()->getStoppingPlaceElement(destStop);
233
os.writeAttr(element, destStop);
234
const std::string name = RONet::getInstance()->getStoppingPlaceName(destStop);
235
if (name != "") {
236
comment = " <!-- " + name + " -->";
237
}
238
} else if (arr != 0 && arr != std::numeric_limits<double>::infinity()) {
239
os.writeAttr(SUMO_ATTR_ARRIVALPOS, arr);
240
}
241
os.closeTag(comment);
242
}
243
244
ROPerson::PlanItem*
245
ROPerson::PersonTrip::clone() const {
246
PersonTrip* result = new PersonTrip(from, to, modes, dep, stopOrigin, arr, stopDest, walkFactor, group);
247
for (auto* item : myTripItems) {
248
result->myTripItems.push_back(item->clone());
249
}
250
return result;
251
}
252
253
void
254
ROPerson::PersonTrip::saveVehicles(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options) const {
255
for (ROVehicle* veh : myVehicles) {
256
if (!RONet::getInstance()->knowsVehicle(veh->getID())) {
257
veh->saveAsXML(os, typeos, asAlternatives, options);
258
}
259
}
260
}
261
262
void
263
ROPerson::PersonTrip::saveAsXML(OutputDevice& os, const bool extended, const bool asTrip, OptionsCont& options) const {
264
if ((asTrip || extended) && from != nullptr) {
265
const bool writeGeoTrip = asTrip && options.getBool("write-trips.geo");
266
os.openTag(SUMO_TAG_PERSONTRIP);
267
if (writeGeoTrip) {
268
Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(getDepartPos());
269
if (GeoConvHelper::getFinal().usingGeoProjection()) {
270
os.setPrecision(gPrecisionGeo);
271
GeoConvHelper::getFinal().cartesian2geo(fromPos);
272
os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
273
os.setPrecision(gPrecision);
274
} else {
275
os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
276
}
277
} else {
278
os.writeAttr(SUMO_ATTR_FROM, from->getID());
279
}
280
if (writeGeoTrip) {
281
Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(MIN2(getArrivalPos(), to->getLanes()[0]->getShape().length2D()));
282
if (GeoConvHelper::getFinal().usingGeoProjection()) {
283
os.setPrecision(gPrecisionGeo);
284
GeoConvHelper::getFinal().cartesian2geo(toPos);
285
os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
286
os.setPrecision(gPrecision);
287
} else {
288
os.writeAttr(SUMO_ATTR_TOXY, toPos);
289
}
290
} else {
291
os.writeAttr(SUMO_ATTR_TO, to->getID());
292
}
293
std::vector<std::string> allowedModes;
294
if ((modes & SVC_BUS) != 0) {
295
allowedModes.push_back("public");
296
}
297
if ((modes & SVC_PASSENGER) != 0) {
298
allowedModes.push_back("car");
299
}
300
if ((modes & SVC_TAXI) != 0) {
301
allowedModes.push_back("taxi");
302
}
303
if ((modes & SVC_BICYCLE) != 0) {
304
allowedModes.push_back("bicycle");
305
}
306
if (allowedModes.size() > 0) {
307
os.writeAttr(SUMO_ATTR_MODES, toString(allowedModes));
308
}
309
if (!writeGeoTrip) {
310
if (dep != 0 && dep != std::numeric_limits<double>::infinity()) {
311
os.writeAttr(SUMO_ATTR_DEPARTPOS, dep);
312
}
313
if (arr != 0 && arr != std::numeric_limits<double>::infinity()) {
314
os.writeAttr(SUMO_ATTR_ARRIVALPOS, arr);
315
}
316
}
317
if (getStopDest() != "") {
318
os.writeAttr(SUMO_ATTR_BUS_STOP, getStopDest());
319
}
320
if (walkFactor != 1) {
321
os.writeAttr(SUMO_ATTR_WALKFACTOR, walkFactor);
322
}
323
if (extended && myTripItems.size() != 0) {
324
std::vector<double> costs;
325
for (const TripItem* const tripItem : myTripItems) {
326
costs.push_back(tripItem->getCost());
327
}
328
os.writeAttr(SUMO_ATTR_COSTS, costs);
329
}
330
os.closeTag();
331
} else {
332
for (const TripItem* const it : myTripItems) {
333
it->saveAsXML(os, extended, options);
334
}
335
}
336
}
337
338
SUMOTime
339
ROPerson::PersonTrip::getDuration() const {
340
SUMOTime result = 0;
341
for (TripItem* tItem : myTripItems) {
342
result += tItem->getDuration();
343
}
344
return result;
345
}
346
347
bool
348
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
const double speed = getMaxSpeed() * trip->getWalkFactor();
352
std::vector<ROIntermodalRouter::TripItem> result;
353
provider.getIntermodalRouter().compute(trip->getOrigin(), trip->getDestination(),
354
trip->getDepartPos(), trip->getStopOrigin(),
355
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
const int lastIndex = (int)result.size() - 1;
361
for (const ROIntermodalRouter::TripItem& item : result) {
362
if (!item.edges.empty()) {
363
if (item.line == "") {
364
double depPos = trip->getDepartPos(false);
365
double arrPos = trip->getArrivalPos(false);
366
if (trip->getOrigin()->isTazConnector()) {
367
// walk the whole length of the first edge
368
const ROEdge* first = item.edges.front();
369
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
if (trip->getDestination()->isTazConnector()) {
376
// walk the whole length of the last edge
377
const ROEdge* last = item.edges.back();
378
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
if (&item == &result.back() && trip->getStopDest() == "") {
385
resultItems.push_back(new Walk(start, item.edges, item.cost, item.exitTimes, depPos, arrPos));
386
} else {
387
resultItems.push_back(new Walk(start, item.edges, item.cost, item.exitTimes, depPos, arrPos, item.destStop));
388
}
389
} else if (veh != nullptr && item.line == veh->getID()) {
390
double cost = item.cost;
391
if (veh->getVClass() != SVC_TAXI) {
392
RORoute* route = new RORoute(veh->getID() + "_RouteDef", item.edges);
393
route->setProbability(1);
394
veh->getRouteDefinition()->addLoadedAlternative(route);
395
carUsed = true;
396
} else if (resultItems.empty()) {
397
// if this is the first plan item the initial taxi waiting time wasn't added yet
398
const double taxiWait = STEPS2TIME(string2time(OptionsCont::getOptions().getString("persontrip.taxi.waiting-time")));
399
cost += taxiWait;
400
}
401
double arrPos = index == lastIndex ? trip->getArrivalPos(false) : item.arrivalPos;
402
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
const ROEdge* origin = trip == myPlan.front() && resultItems.empty() ? trip->getOrigin() : nullptr;
406
const std::string line = OptionsCont::getOptions().getBool("persontrip.ride-public-line") ? item.line : LINE_ANY;
407
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
start += TIME2STEPS(item.cost);
411
index++;
412
}
413
if (result.empty()) {
414
errorHandler->inform("No route for trip in person '" + getID() + "'.");
415
myRoutingSuccess = false;
416
}
417
return carUsed;
418
}
419
420
421
void
422
ROPerson::computeRoute(const RORouterProvider& provider,
423
const bool /* removeLoops */, MsgHandler* errorHandler) {
424
myRoutingSuccess = true;
425
SUMOTime time = getParameter().depart;
426
for (PlanItem* const it : myPlan) {
427
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
if (vehicles.empty()) {
434
computeIntermodal(time, provider, trip, nullptr, best, errorHandler);
435
} else {
436
double bestCost = std::numeric_limits<double>::infinity();
437
for (const ROVehicle* const v : vehicles) {
438
const bool carUsed = computeIntermodal(time, provider, trip, v, resultItems, errorHandler);
439
double cost = 0.;
440
for (const TripItem* const tripIt : resultItems) {
441
cost += tripIt->getCost();
442
}
443
if (cost < bestCost) {
444
bestCost = cost;
445
bestVeh = carUsed ? v : nullptr;
446
best.swap(resultItems);
447
}
448
for (const TripItem* const tripIt : resultItems) {
449
delete tripIt;
450
}
451
resultItems.clear();
452
}
453
}
454
trip->setItems(best, bestVeh);
455
456
// test after routing to ensuer network was initialized
457
if (!myHaveWarnedPTMissing && (trip->getModes() & SVC_BUS) != 0) {
458
myHaveWarnedPTMissing = true;
459
if (!provider.getIntermodalRouter().getNetwork()->hasPTSchedules()) {
460
WRITE_WARNINGF("Person '%' is configured to use public transport but no schedules are loaded.", getID());
461
}
462
}
463
}
464
time += it->getDuration();
465
}
466
if (RONet::getInstance()->getMaxTraveltime() > 0) {
467
double costs = STEPS2TIME(time - getParameter().depart);
468
if (costs > RONet::getInstance()->getMaxTraveltime()) {
469
errorHandler->inform("Person '" + getID() + "' has no valid route (traveltime " + time2string(TIME2STEPS(costs)) + " exceeds max-traveltime)");
470
myRoutingSuccess = false;
471
return;
472
}
473
}
474
}
475
476
477
void
478
ROPerson::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
479
// write the person's vehicles
480
const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
481
const bool writeFlow = options.exists("keep-flows") && options.getBool("keep-flows") && isPartOfFlow();
482
if (!writeTrip) {
483
for (const PlanItem* const it : myPlan) {
484
it->saveVehicles(os, typeos, asAlternatives, options);
485
}
486
}
487
488
if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
489
getType()->write(*typeos);
490
getType()->saved = true;
491
}
492
if (getType() != nullptr && !getType()->saved) {
493
getType()->write(os);
494
getType()->saved = asAlternatives;
495
}
496
const SumoXMLTag tag = writeFlow ? SUMO_TAG_PERSONFLOW : SUMO_TAG_PERSON;
497
// write the person
498
if (cloneIndex == 0) {
499
getParameter().write(os, options, tag);
500
} else {
501
SUMOVehicleParameter p = getParameter();
502
// @note id collisions may occur if scale-suffic occurs in other vehicle ids
503
p.id += options.getString("scale-suffix") + toString(cloneIndex);
504
p.write(os, options, tag);
505
}
506
507
for (const PlanItem* const it : myPlan) {
508
it->saveAsXML(os, asAlternatives, writeTrip, options);
509
}
510
511
// write params
512
getParameter().writeParams(os);
513
os.closeTag();
514
}
515
516
517
/****************************************************************************/
518
519