Line data Source code
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 297964 : ROVehicle::ROVehicle(const SUMOVehicleParameter& pars,
52 : RORouteDef* route, const SUMOVTypeParameter* type,
53 297964 : const RONet* net, MsgHandler* errorHandler):
54 : RORoutable(pars, type),
55 297964 : myRoute(route),
56 297964 : myJumpTime(-1) {
57 : getParameter().stops.clear();
58 595003 : if (route != nullptr && route->getFirstRoute() != nullptr) {
59 594410 : for (StopParVector::const_iterator s = route->getFirstRoute()->getStops().begin(); s != route->getFirstRoute()->getStops().end(); ++s) {
60 332 : addStop(*s, net, errorHandler);
61 : }
62 : }
63 298488 : for (StopParVector::const_iterator s = pars.stops.begin(); s != pars.stops.end(); ++s) {
64 524 : addStop(*s, net, errorHandler);
65 : }
66 297964 : }
67 :
68 :
69 : void
70 856 : ROVehicle::addStop(const SUMOVehicleParameter::Stop& stopPar, const RONet* net, MsgHandler* errorHandler) {
71 856 : const ROEdge* stopEdge = net->getEdge(stopPar.edge);
72 : assert(stopEdge != 0); // was checked when parsing the stop
73 856 : if (stopEdge->prohibits(this)) {
74 0 : if (errorHandler != nullptr) {
75 0 : 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 856 : 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 0 : if (stopPar.index == STOP_INDEX_FIT) {
87 : iter = getParameter().stops.end();
88 : } else {
89 : iter += stopPar.index;
90 : }
91 : }
92 856 : getParameter().stops.insert(iter, stopPar);
93 856 : if (stopPar.jump >= 0) {
94 24 : if (stopEdge->isInternal()) {
95 0 : if (errorHandler != nullptr) {
96 0 : errorHandler->inform("Jumps are not supported from internal stop edge '" + stopEdge->getID() + "'.");
97 : }
98 : } else {
99 24 : if (myJumpTime < 0) {
100 20 : myJumpTime = 0;
101 : }
102 24 : myJumpTime += stopPar.jump;
103 : }
104 : }
105 : }
106 :
107 :
108 595835 : ROVehicle::~ROVehicle() {}
109 :
110 :
111 : const ROEdge*
112 511 : ROVehicle:: getDepartEdge() const {
113 511 : return myRoute->getFirstRoute()->getFirst();
114 : }
115 :
116 :
117 : void
118 296811 : ROVehicle::computeRoute(const RORouterProvider& provider,
119 : const bool removeLoops, MsgHandler* errorHandler) {
120 : SUMOAbstractRouter<ROEdge, ROVehicle>& router = provider.getVehicleRouter(getVClass());
121 593622 : std::string noRouteMsg = "The vehicle '" + getID() + "' has no valid route.";
122 : RORouteDef* const routeDef = getRouteDefinition();
123 : // check if the route definition is valid
124 296811 : if (routeDef == nullptr) {
125 0 : errorHandler->inform(noRouteMsg);
126 0 : myRoutingSuccess = false;
127 0 : return;
128 : }
129 296811 : routeDef->validateAlternatives(this, errorHandler);
130 296808 : if (routeDef->getFirstRoute() == nullptr) {
131 : // everything is invalid and no new routes will be computed
132 3 : myRoutingSuccess = false;
133 3 : return;
134 : }
135 296808 : RORoute* current = routeDef->buildCurrentRoute(router, getDepartureTime(), *this);
136 296808 : if (current == nullptr || current->size() == 0) {
137 19134 : delete current;
138 1925 : if (current == nullptr || !routeDef->discardSilent()) {
139 51627 : errorHandler->inform(noRouteMsg);
140 : }
141 19134 : myRoutingSuccess = false;
142 19134 : return;
143 : }
144 : // check whether we have to evaluate the route for not containing loops
145 277674 : if (removeLoops) {
146 5215 : const ROEdge* requiredStart = (getParameter().departPosProcedure == DepartPosDefinition::GIVEN
147 5215 : || getParameter().departLaneProcedure == DepartLaneDefinition::GIVEN ? current->getEdgeVector().front() : 0);
148 5215 : const ROEdge* requiredEnd = (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN
149 5215 : || getParameter().arrivalLaneProcedure == ArrivalLaneDefinition::GIVEN ? current->getEdgeVector().back() : 0);
150 : ConstROEdgeVector mandatory;
151 5243 : for (auto m : getMandatoryEdges(requiredStart, requiredEnd)) {
152 28 : mandatory.push_back(m.edge);
153 5215 : }
154 5215 : current->recheckForLoops(mandatory);
155 : // check whether the route is still valid
156 5215 : if (current->size() == 0) {
157 0 : delete current;
158 0 : errorHandler->inform(noRouteMsg + " (after removing loops)");
159 0 : myRoutingSuccess = false;
160 : return;
161 : }
162 5215 : }
163 277674 : if (RONet::getInstance()->getMaxTraveltime() > 0) {
164 12 : double costs = router.recomputeCosts(current->getEdgeVector(), this, getDepartureTime());
165 12 : if (costs > RONet::getInstance()->getMaxTraveltime()) {
166 40 : errorHandler->inform(noRouteMsg + " (traveltime " + time2string(TIME2STEPS(costs)) + " exceeds max-traveltime)");
167 8 : delete current;
168 8 : myRoutingSuccess = false;
169 8 : return;
170 : }
171 : }
172 : // add built route
173 277666 : RORoute* replaced = routeDef->addAlternative(router, this, current, getDepartureTime(), errorHandler);
174 277650 : if (replaced != nullptr) {
175 154870 : if (getParameter().departEdge > 0) {
176 6 : updateIndex(replaced, current, getParameterMutable().departEdge);
177 : }
178 154870 : if (getParameter().arrivalEdge >= 0) {
179 6 : updateIndex(replaced, current, getParameterMutable().arrivalEdge);
180 : }
181 154870 : delete replaced;
182 : }
183 277650 : myRoutingSuccess = true;
184 : }
185 :
186 :
187 : void
188 12 : ROVehicle::updateIndex(const RORoute* replaced, const RORoute* current, int& attr) {
189 : const ConstROEdgeVector& oldRoute = replaced->getEdgeVector();
190 12 : if ((int)oldRoute.size() > attr) {
191 12 : const ROEdge* old = oldRoute[attr];
192 : int skips = 0;
193 63 : for (int i = 0; i < attr; i++) {
194 51 : if (oldRoute[i] == old) {
195 0 : skips++;
196 : }
197 : }
198 : int i = 0;
199 120 : for (const ROEdge* cand : current->getEdgeVector()) {
200 108 : if (cand == old) {
201 12 : if (skips > 0) {
202 0 : skips--;
203 : } else {
204 12 : attr = i;
205 : }
206 : }
207 108 : i++;
208 : }
209 : }
210 12 : }
211 :
212 :
213 : std::vector<ROVehicle::Mandatory>
214 294597 : ROVehicle::getMandatoryEdges(const ROEdge* requiredStart, const ROEdge* requiredEnd) const {
215 294597 : const RONet* net = RONet::getInstance();
216 : std::vector<Mandatory> mandatory;
217 294597 : if (requiredStart) {
218 : double departPos = 0;
219 289390 : if (getParameter().departPosProcedure == DepartPosDefinition::GIVEN) {
220 705 : departPos = SUMOVehicleParameter::interpretEdgePos(getParameter().departPos, requiredStart->getLength(), SUMO_ATTR_DEPARTPOS, "vehicle '" + getID() + "'");
221 : }
222 289390 : mandatory.push_back(Mandatory(requiredStart, departPos));
223 : }
224 294597 : if (getParameter().via.size() != 0) {
225 : // via takes precedence over stop edges
226 48297 : for (const std::string& via : getParameter().via) {
227 33624 : mandatory.push_back(Mandatory(net->getEdge(via), INVALID_STOP_POS));
228 : }
229 : } else {
230 280636 : for (const auto& stop : getParameter().stops) {
231 712 : const ROEdge* e = net->getEdge(stop.edge);
232 712 : if (e->isInternal()) {
233 : // the edges before and after the internal edge are mandatory
234 8 : const ROEdge* before = e->getNormalBefore();
235 8 : const ROEdge* after = e->getNormalAfter();
236 8 : mandatory.push_back(Mandatory(before, INVALID_STOP_POS));
237 8 : mandatory.push_back(Mandatory(after, INVALID_STOP_POS, stop.jump));
238 : } else {
239 1408 : double endPos = SUMOVehicleParameter::interpretEdgePos(stop.endPos, e->getLength(), SUMO_ATTR_ENDPOS, "stop of vehicle '" + getID() + "' on edge '" + e->getID() + "'");
240 704 : mandatory.push_back(Mandatory(e, endPos, stop.jump));
241 : }
242 : }
243 : }
244 294597 : if (requiredEnd) {
245 : double arrivalPos = INVALID_STOP_POS;
246 289390 : if (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN) {
247 414 : arrivalPos = SUMOVehicleParameter::interpretEdgePos(getParameter().arrivalPos, requiredEnd->getLength(), SUMO_ATTR_ARRIVALPOS, "vehicle '" + getID() + "'");
248 : }
249 289390 : mandatory.push_back(Mandatory(requiredEnd, arrivalPos));
250 : }
251 294597 : return mandatory;
252 0 : }
253 :
254 :
255 : void
256 390797 : ROVehicle::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
257 390797 : if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
258 393 : getType()->write(*typeos);
259 393 : getType()->saved = true;
260 : }
261 390797 : if (getType() != nullptr && !getType()->saved) {
262 4100 : getType()->write(os);
263 4100 : getType()->saved = asAlternatives;
264 : }
265 :
266 777328 : const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
267 390797 : const bool writeGeoTrip = writeTrip && options.getBool("write-trips.geo");
268 390797 : const bool writeJunctions = writeTrip && options.getBool("write-trips.junctions");
269 390797 : const bool writeNamedRoute = !asAlternatives && options.getBool("named-routes");
270 777328 : const bool writeCosts = options.exists("write-costs") && options.getBool("write-costs");
271 781594 : const bool writeExit = options.exists("exit-times") && options.getBool("exit-times");
272 777328 : const bool writeLength = options.exists("route-length") && options.getBool("route-length");
273 777328 : const bool writeFlow = options.exists("keep-flows") && options.getBool("keep-flows") && isPartOfFlow();
274 :
275 : std::string routeID;
276 390797 : if (writeNamedRoute) {
277 29 : ConstROEdgeVector edges = myRoute->getUsedRoute()->getNormalEdges();
278 : auto it = mySavedRoutes.find(edges);
279 29 : if (it == mySavedRoutes.end()) {
280 22 : routeID = "r" + toString(mySavedRoutes.size());
281 11 : myRoute->getUsedRoute()->writeXMLDefinition(os, this, writeCosts, false, writeExit,
282 : writeLength, routeID);
283 11 : mySavedRoutes[edges] = routeID;
284 : } else {
285 18 : routeID = it->second;
286 : }
287 29 : }
288 390797 : const SumoXMLTag tag = writeFlow ? SUMO_TAG_FLOW : (writeTrip ? SUMO_TAG_TRIP : SUMO_TAG_VEHICLE);
289 : // write the vehicle (new style, with included routes)
290 390797 : if (cloneIndex == 0) {
291 781354 : getParameter().write(os, options, tag);
292 : } else {
293 120 : SUMOVehicleParameter p = getParameter();
294 : // @note id collisions may occur if scale-suffic occurs in other vehicle ids
295 240 : p.id += options.getString("scale-suffix") + toString(cloneIndex);
296 120 : p.write(os, options, tag);
297 120 : }
298 : // save the route
299 390797 : if (writeTrip) {
300 158652 : const ConstROEdgeVector edges = myRoute->getFirstRoute()->getEdgeVector();
301 : const ROEdge* from = nullptr;
302 : const ROEdge* to = nullptr;
303 79326 : if (edges.size() > 0) {
304 79326 : if (edges.front()->isTazConnector()) {
305 7284 : if (edges.size() > 1) {
306 7284 : from = edges[1];
307 7284 : if (from->isTazConnector() && writeJunctions && edges.front()->getSuccessors().size() > 0) {
308 : // routing was skipped
309 8 : from = edges.front()->getSuccessors(getVClass()).front();
310 : }
311 : }
312 : } else {
313 : from = edges[0];
314 : }
315 79326 : if (edges.back()->isTazConnector()) {
316 7284 : if (edges.size() > 1) {
317 7284 : to = edges[edges.size() - 2];
318 7284 : if (to->isTazConnector() && writeJunctions && edges.back()->getPredecessors().size() > 0) {
319 : // routing was skipped
320 4 : to = edges.back()->getPredecessors().front();
321 : }
322 : }
323 : } else {
324 72042 : to = edges[edges.size() - 1];
325 : }
326 : }
327 79326 : if (from != nullptr) {
328 79326 : if (writeGeoTrip) {
329 16 : Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(0);
330 16 : if (GeoConvHelper::getFinal().usingGeoProjection()) {
331 0 : os.setPrecision(gPrecisionGeo);
332 0 : GeoConvHelper::getFinal().cartesian2geo(fromPos);
333 0 : os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
334 0 : os.setPrecision(gPrecision);
335 : } else {
336 16 : os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
337 : }
338 79310 : } else if (writeJunctions) {
339 7244 : os.writeAttr(SUMO_ATTR_FROM_JUNCTION, from->getFromJunction()->getID());
340 : } else {
341 72066 : os.writeAttr(SUMO_ATTR_FROM, from->getID());
342 : }
343 : }
344 79326 : if (to != nullptr) {
345 79326 : if (writeGeoTrip) {
346 16 : Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(to->getLanes()[0]->getShape().length2D());
347 16 : if (GeoConvHelper::getFinal().usingGeoProjection()) {
348 0 : os.setPrecision(gPrecisionGeo);
349 0 : GeoConvHelper::getFinal().cartesian2geo(toPos);
350 0 : os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
351 0 : os.setPrecision(gPrecision);
352 : } else {
353 16 : os.writeAttr(SUMO_ATTR_TOXY, toPos);
354 : }
355 79310 : } else if (writeJunctions) {
356 7244 : os.writeAttr(SUMO_ATTR_TO_JUNCTION, to->getToJunction()->getID());
357 : } else {
358 72066 : os.writeAttr(SUMO_ATTR_TO, to->getID());
359 : }
360 : }
361 79326 : if (getParameter().via.size() > 0) {
362 : std::vector<std::string> viaOut;
363 : SumoXMLAttr viaAttr = (writeGeoTrip
364 7236 : ? (GeoConvHelper::getFinal().usingGeoProjection() ? SUMO_ATTR_VIALONLAT : SUMO_ATTR_VIAXY)
365 14456 : : (writeJunctions ? SUMO_ATTR_VIAJUNCTIONS : SUMO_ATTR_VIA));
366 21672 : for (const std::string& viaID : getParameter().via) {
367 14436 : const ROEdge* viaEdge = RONet::getInstance()->getEdge(viaID);
368 14436 : if (viaEdge->isTazConnector()) {
369 20 : if (viaEdge->getPredecessors().size() == 0) {
370 0 : continue;
371 : }
372 : // XXX used edge that was used in route
373 20 : viaEdge = viaEdge->getPredecessors().front();
374 : }
375 : assert(viaEdge != nullptr);
376 14436 : if (writeGeoTrip) {
377 8 : Position viaPos = viaEdge->getLanes()[0]->getShape().positionAtOffset2D(viaEdge->getLanes()[0]->getShape().length2D() / 2);
378 8 : if (GeoConvHelper::getFinal().usingGeoProjection()) {
379 0 : GeoConvHelper::getFinal().cartesian2geo(viaPos);
380 0 : viaOut.push_back(toString(viaPos, gPrecisionGeo));
381 : } else {
382 16 : viaOut.push_back(toString(viaPos, gPrecision));
383 : }
384 14428 : } else if (writeJunctions) {
385 8 : viaOut.push_back(viaEdge->getToJunction()->getID());
386 : } else {
387 14420 : viaOut.push_back(viaEdge->getID());
388 : }
389 : }
390 7236 : os.writeAttr(viaAttr, viaOut);
391 7236 : }
392 390797 : } else if (writeNamedRoute) {
393 29 : os.writeAttr(SUMO_ATTR_ROUTE, routeID);
394 : } else {
395 311442 : myRoute->writeXMLDefinition(os, this, asAlternatives, writeExit, writeCosts, writeLength);
396 : }
397 392193 : for (StopParVector::const_iterator stop = getParameter().stops.begin(); stop != getParameter().stops.end(); ++stop) {
398 1396 : stop->write(os);
399 : }
400 390797 : getParameter().writeParams(os);
401 781594 : os.closeTag();
402 390797 : }
403 :
404 :
405 : /****************************************************************************/
|