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