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