Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/router/ROVehicle.cpp
193674 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 ROVehicle.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/StringUtils.h>
28
#include <utils/common/ToString.h>
29
#include <utils/common/MsgHandler.h>
30
#include <utils/geom/GeoConvHelper.h>
31
#include <utils/vehicle/SUMOVTypeParameter.h>
32
#include <utils/options/OptionsCont.h>
33
#include <utils/iodevices/OutputDevice.h>
34
#include "RORouteDef.h"
35
#include "RORoute.h"
36
#include "ROHelper.h"
37
#include "RONet.h"
38
#include "ROLane.h"
39
#include "ROVehicle.h"
40
41
#define INVALID_STOP_POS -1
42
43
// ===========================================================================
44
// static members
45
// ===========================================================================
46
std::map<ConstROEdgeVector, std::string> ROVehicle::mySavedRoutes;
47
48
// ===========================================================================
49
// method definitions
50
// ===========================================================================
51
ROVehicle::ROVehicle(const SUMOVehicleParameter& pars,
52
RORouteDef* route, const SUMOVTypeParameter* type,
53
const RONet* net, MsgHandler* errorHandler):
54
RORoutable(pars, type),
55
myRoute(route),
56
myJumpTime(-1) {
57
getParameter().stops.clear();
58
if (route != nullptr && route->getFirstRoute() != nullptr) {
59
for (StopParVector::const_iterator s = route->getFirstRoute()->getStops().begin(); s != route->getFirstRoute()->getStops().end(); ++s) {
60
addStop(*s, net, errorHandler);
61
}
62
}
63
for (StopParVector::const_iterator s = pars.stops.begin(); s != pars.stops.end(); ++s) {
64
addStop(*s, net, errorHandler);
65
}
66
}
67
68
69
void
70
ROVehicle::addStop(const SUMOVehicleParameter::Stop& stopPar, const RONet* net, MsgHandler* errorHandler) {
71
const ROEdge* stopEdge = net->getEdge(stopPar.edge);
72
assert(stopEdge != 0); // was checked when parsing the stop
73
if (stopEdge->prohibits(this)) {
74
if (errorHandler != nullptr) {
75
errorHandler->inform("Stop edge '" + stopEdge->getID() + "' does not allow vehicle '" + getID() + "'.");
76
}
77
return;
78
}
79
// where to insert the stop
80
StopParVector::iterator iter = getParameter().stops.begin();
81
if (stopPar.index == STOP_INDEX_END || stopPar.index >= static_cast<int>(getParameter().stops.size())) {
82
if (getParameter().stops.size() > 0) {
83
iter = getParameter().stops.end();
84
}
85
} else {
86
if (stopPar.index == STOP_INDEX_FIT) {
87
iter = getParameter().stops.end();
88
} else {
89
iter += stopPar.index;
90
}
91
}
92
getParameter().stops.insert(iter, stopPar);
93
if (stopPar.jump >= 0) {
94
if (stopEdge->isInternal()) {
95
if (errorHandler != nullptr) {
96
errorHandler->inform("Jumps are not supported from internal stop edge '" + stopEdge->getID() + "'.");
97
}
98
} else {
99
if (myJumpTime < 0) {
100
myJumpTime = 0;
101
}
102
myJumpTime += stopPar.jump;
103
}
104
}
105
}
106
107
108
ROVehicle::~ROVehicle() {}
109
110
111
const ROEdge*
112
ROVehicle:: getDepartEdge() const {
113
return myRoute->getFirstRoute()->getFirst();
114
}
115
116
117
void
118
ROVehicle::computeRoute(const RORouterProvider& provider,
119
const bool removeLoops, MsgHandler* errorHandler) {
120
SUMOAbstractRouter<ROEdge, ROVehicle>& router = provider.getVehicleRouter(getVClass());
121
std::string noRouteMsg = "The vehicle '" + getID() + "' has no valid route.";
122
RORouteDef* const routeDef = getRouteDefinition();
123
// check if the route definition is valid
124
if (routeDef == nullptr) {
125
errorHandler->inform(noRouteMsg);
126
myRoutingSuccess = false;
127
return;
128
}
129
routeDef->validateAlternatives(this, errorHandler);
130
if (routeDef->getFirstRoute() == nullptr) {
131
// everything is invalid and no new routes will be computed
132
myRoutingSuccess = false;
133
return;
134
}
135
RORoute* current = routeDef->buildCurrentRoute(router, getDepartureTime(), *this);
136
if (current == nullptr || current->size() == 0) {
137
delete current;
138
if (current == nullptr || !routeDef->discardSilent()) {
139
errorHandler->inform(noRouteMsg);
140
}
141
myRoutingSuccess = false;
142
return;
143
}
144
// check whether we have to evaluate the route for not containing loops
145
if (removeLoops) {
146
const ROEdge* requiredStart = (getParameter().departPosProcedure == DepartPosDefinition::GIVEN
147
|| getParameter().departLaneProcedure == DepartLaneDefinition::GIVEN ? current->getEdgeVector().front() : 0);
148
const ROEdge* requiredEnd = (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN
149
|| getParameter().arrivalLaneProcedure == ArrivalLaneDefinition::GIVEN ? current->getEdgeVector().back() : 0);
150
ConstROEdgeVector mandatory;
151
for (auto m : getMandatoryEdges(requiredStart, requiredEnd)) {
152
mandatory.push_back(m.edge);
153
}
154
current->recheckForLoops(mandatory);
155
// check whether the route is still valid
156
if (current->size() == 0) {
157
delete current;
158
errorHandler->inform(noRouteMsg + " (after removing loops)");
159
myRoutingSuccess = false;
160
return;
161
}
162
}
163
if (RONet::getInstance()->getMaxTraveltime() > 0) {
164
double costs = router.recomputeCosts(current->getEdgeVector(), this, getDepartureTime());
165
if (costs > RONet::getInstance()->getMaxTraveltime()) {
166
errorHandler->inform(noRouteMsg + " (traveltime " + time2string(TIME2STEPS(costs)) + " exceeds max-traveltime)");
167
delete current;
168
myRoutingSuccess = false;
169
return;
170
}
171
}
172
// add built route
173
routeDef->addAlternative(router, this, current, getDepartureTime(), errorHandler);
174
myRoutingSuccess = true;
175
}
176
177
178
std::vector<ROVehicle::Mandatory>
179
ROVehicle::getMandatoryEdges(const ROEdge* requiredStart, const ROEdge* requiredEnd) const {
180
const RONet* net = RONet::getInstance();
181
std::vector<Mandatory> mandatory;
182
if (requiredStart) {
183
double departPos = 0;
184
if (getParameter().departPosProcedure == DepartPosDefinition::GIVEN) {
185
departPos = SUMOVehicleParameter::interpretEdgePos(getParameter().departPos, requiredStart->getLength(), SUMO_ATTR_DEPARTPOS, "vehicle '" + getID() + "'");
186
}
187
mandatory.push_back(Mandatory(requiredStart, departPos));
188
}
189
if (getParameter().via.size() != 0) {
190
// via takes precedence over stop edges
191
for (const std::string& via : getParameter().via) {
192
mandatory.push_back(Mandatory(net->getEdge(via), INVALID_STOP_POS));
193
}
194
} else {
195
for (const auto& stop : getParameter().stops) {
196
const ROEdge* e = net->getEdge(stop.edge);
197
if (e->isInternal()) {
198
// the edges before and after the internal edge are mandatory
199
const ROEdge* before = e->getNormalBefore();
200
const ROEdge* after = e->getNormalAfter();
201
mandatory.push_back(Mandatory(before, INVALID_STOP_POS));
202
mandatory.push_back(Mandatory(after, INVALID_STOP_POS, stop.jump));
203
} else {
204
double endPos = SUMOVehicleParameter::interpretEdgePos(stop.endPos, e->getLength(), SUMO_ATTR_ENDPOS, "stop of vehicle '" + getID() + "' on edge '" + e->getID() + "'");
205
mandatory.push_back(Mandatory(e, endPos, stop.jump));
206
}
207
}
208
}
209
if (requiredEnd) {
210
double arrivalPos = INVALID_STOP_POS;
211
if (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN) {
212
arrivalPos = SUMOVehicleParameter::interpretEdgePos(getParameter().arrivalPos, requiredEnd->getLength(), SUMO_ATTR_ARRIVALPOS, "vehicle '" + getID() + "'");
213
}
214
mandatory.push_back(Mandatory(requiredEnd, arrivalPos));
215
}
216
return mandatory;
217
}
218
219
220
void
221
ROVehicle::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
222
if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
223
getType()->write(*typeos);
224
getType()->saved = true;
225
}
226
if (getType() != nullptr && !getType()->saved) {
227
getType()->write(os);
228
getType()->saved = asAlternatives;
229
}
230
231
const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
232
const bool writeGeoTrip = writeTrip && options.getBool("write-trips.geo");
233
const bool writeJunctions = writeTrip && options.getBool("write-trips.junctions");
234
const bool writeNamedRoute = !asAlternatives && options.getBool("named-routes");
235
const bool writeCosts = options.exists("write-costs") && options.getBool("write-costs");
236
const bool writeExit = options.exists("exit-times") && options.getBool("exit-times");
237
const bool writeLength = options.exists("route-length") && options.getBool("route-length");
238
const bool writeFlow = options.exists("keep-flows") && options.getBool("keep-flows") && isPartOfFlow();
239
240
std::string routeID;
241
if (writeNamedRoute) {
242
ConstROEdgeVector edges = myRoute->getUsedRoute()->getNormalEdges();
243
auto it = mySavedRoutes.find(edges);
244
if (it == mySavedRoutes.end()) {
245
routeID = "r" + toString(mySavedRoutes.size());
246
myRoute->getUsedRoute()->writeXMLDefinition(os, this, writeCosts, false, writeExit,
247
writeLength, routeID);
248
mySavedRoutes[edges] = routeID;
249
} else {
250
routeID = it->second;
251
}
252
}
253
const SumoXMLTag tag = writeFlow ? SUMO_TAG_FLOW : (writeTrip ? SUMO_TAG_TRIP : SUMO_TAG_VEHICLE);
254
// write the vehicle (new style, with included routes)
255
if (cloneIndex == 0) {
256
getParameter().write(os, options, tag);
257
} else {
258
SUMOVehicleParameter p = getParameter();
259
// @note id collisions may occur if scale-suffic occurs in other vehicle ids
260
p.id += options.getString("scale-suffix") + toString(cloneIndex);
261
p.write(os, options, tag);
262
}
263
// save the route
264
if (writeTrip) {
265
const ConstROEdgeVector edges = myRoute->getFirstRoute()->getEdgeVector();
266
const ROEdge* from = nullptr;
267
const ROEdge* to = nullptr;
268
if (edges.size() > 0) {
269
if (edges.front()->isTazConnector()) {
270
if (edges.size() > 1) {
271
from = edges[1];
272
if (from->isTazConnector() && writeJunctions && edges.front()->getSuccessors().size() > 0) {
273
// routing was skipped
274
from = edges.front()->getSuccessors(getVClass()).front();
275
}
276
}
277
} else {
278
from = edges[0];
279
}
280
if (edges.back()->isTazConnector()) {
281
if (edges.size() > 1) {
282
to = edges[edges.size() - 2];
283
if (to->isTazConnector() && writeJunctions && edges.back()->getPredecessors().size() > 0) {
284
// routing was skipped
285
to = edges.back()->getPredecessors().front();
286
}
287
}
288
} else {
289
to = edges[edges.size() - 1];
290
}
291
}
292
if (from != nullptr) {
293
if (writeGeoTrip) {
294
Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(0);
295
if (GeoConvHelper::getFinal().usingGeoProjection()) {
296
os.setPrecision(gPrecisionGeo);
297
GeoConvHelper::getFinal().cartesian2geo(fromPos);
298
os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
299
os.setPrecision(gPrecision);
300
} else {
301
os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
302
}
303
} else if (writeJunctions) {
304
os.writeAttr(SUMO_ATTR_FROM_JUNCTION, from->getFromJunction()->getID());
305
} else {
306
os.writeAttr(SUMO_ATTR_FROM, from->getID());
307
}
308
}
309
if (to != nullptr) {
310
if (writeGeoTrip) {
311
Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(to->getLanes()[0]->getShape().length2D());
312
if (GeoConvHelper::getFinal().usingGeoProjection()) {
313
os.setPrecision(gPrecisionGeo);
314
GeoConvHelper::getFinal().cartesian2geo(toPos);
315
os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
316
os.setPrecision(gPrecision);
317
} else {
318
os.writeAttr(SUMO_ATTR_TOXY, toPos);
319
}
320
} else if (writeJunctions) {
321
os.writeAttr(SUMO_ATTR_TO_JUNCTION, to->getToJunction()->getID());
322
} else {
323
os.writeAttr(SUMO_ATTR_TO, to->getID());
324
}
325
}
326
if (getParameter().via.size() > 0) {
327
std::vector<std::string> viaOut;
328
SumoXMLAttr viaAttr = (writeGeoTrip
329
? (GeoConvHelper::getFinal().usingGeoProjection() ? SUMO_ATTR_VIALONLAT : SUMO_ATTR_VIAXY)
330
: (writeJunctions ? SUMO_ATTR_VIAJUNCTIONS : SUMO_ATTR_VIA));
331
for (const std::string& viaID : getParameter().via) {
332
const ROEdge* viaEdge = RONet::getInstance()->getEdge(viaID);
333
if (viaEdge->isTazConnector()) {
334
if (viaEdge->getPredecessors().size() == 0) {
335
continue;
336
}
337
// XXX used edge that was used in route
338
viaEdge = viaEdge->getPredecessors().front();
339
}
340
assert(viaEdge != nullptr);
341
if (writeGeoTrip) {
342
Position viaPos = viaEdge->getLanes()[0]->getShape().positionAtOffset2D(viaEdge->getLanes()[0]->getShape().length2D() / 2);
343
if (GeoConvHelper::getFinal().usingGeoProjection()) {
344
GeoConvHelper::getFinal().cartesian2geo(viaPos);
345
viaOut.push_back(toString(viaPos, gPrecisionGeo));
346
} else {
347
viaOut.push_back(toString(viaPos, gPrecision));
348
}
349
} else if (writeJunctions) {
350
viaOut.push_back(viaEdge->getToJunction()->getID());
351
} else {
352
viaOut.push_back(viaEdge->getID());
353
}
354
}
355
os.writeAttr(viaAttr, viaOut);
356
}
357
} else if (writeNamedRoute) {
358
os.writeAttr(SUMO_ATTR_ROUTE, routeID);
359
} else {
360
myRoute->writeXMLDefinition(os, this, asAlternatives, writeExit, writeCosts, writeLength);
361
}
362
for (StopParVector::const_iterator stop = getParameter().stops.begin(); stop != getParameter().stops.end(); ++stop) {
363
stop->write(os);
364
}
365
getParameter().writeParams(os);
366
os.closeTag();
367
}
368
369
370
/****************************************************************************/
371
372