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 RORouteDef.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @author Jakob Erdmann
18 : /// @date Sept 2002
19 : ///
20 : // Base class for a vehicle's route definition
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include <string>
25 : #include <iterator>
26 : #include <algorithm>
27 : #include <utils/common/StringUtils.h>
28 : #include <utils/common/ToString.h>
29 : #include <utils/common/Named.h>
30 : #include <utils/common/StringUtils.h>
31 : #include <utils/common/MsgHandler.h>
32 : #include <utils/common/RandHelper.h>
33 : #include <utils/iodevices/OutputDevice.h>
34 : #include <utils/options/OptionsCont.h>
35 : #include "ROEdge.h"
36 : #include "RORoute.h"
37 : #include <utils/router/SUMOAbstractRouter.h>
38 : #include <utils/router/RouteCostCalculator.h>
39 : #include "RORouteDef.h"
40 : #include "ROVehicle.h"
41 :
42 : // ===========================================================================
43 : // static members
44 : // ===========================================================================
45 : bool RORouteDef::myUsingJTRR(false);
46 : bool RORouteDef::mySkipNewRoutes(false);
47 :
48 : // ===========================================================================
49 : // method definitions
50 : // ===========================================================================
51 302235 : RORouteDef::RORouteDef(const std::string& id, const int lastUsed,
52 302235 : const bool tryRepair, const bool mayBeDisconnected) :
53 302235 : Named(StringUtils::convertUmlaute(id)),
54 302235 : myPrecomputed(nullptr), myLastUsed(lastUsed), myTryRepair(tryRepair),
55 302235 : myMayBeDisconnected(mayBeDisconnected),
56 302235 : myDiscardSilent(false) {
57 302235 : }
58 :
59 :
60 : void
61 4 : RORouteDef::addAlternativeDef(const RORouteDef* alt) {
62 : std::copy(alt->myAlternatives.begin(), alt->myAlternatives.end(),
63 4 : back_inserter(myAlternatives));
64 4 : }
65 :
66 :
67 : std::shared_ptr<RORoute>
68 296980 : RORouteDef::buildCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
69 : SUMOTime begin, const ROVehicle& veh) const {
70 296980 : if (myPrecomputed == nullptr) {
71 296980 : preComputeCurrentRoute(router, begin, veh);
72 : }
73 296980 : return myPrecomputed;
74 : }
75 :
76 :
77 : void
78 296983 : RORouteDef::validateAlternatives(const ROVehicle* veh, MsgHandler* errorHandler) {
79 736492 : for (int i = 0; i < (int)myAlternatives.size();) {
80 439509 : if ((i != myLastUsed || mySkipNewRoutes) && !myAlternatives[i]->isPermitted(veh, errorHandler)) {
81 5 : myAlternatives.erase(myAlternatives.begin() + i);
82 5 : if (myLastUsed > i) {
83 0 : myLastUsed--;
84 : }
85 : } else {
86 439504 : i++;
87 : }
88 : }
89 296983 : }
90 :
91 :
92 : void
93 296980 : RORouteDef::preComputeCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
94 : SUMOTime begin, const ROVehicle& veh) const {
95 296980 : myNewRoute = false;
96 296980 : const OptionsCont& oc = OptionsCont::getOptions();
97 296980 : const bool ignoreErrors = oc.getBool("ignore-errors");
98 296980 : const bool hasRestrictions = RONet::getInstance()->hasParamRestrictions();
99 : assert(myAlternatives[0]->getEdgeVector().size() > 0);
100 296980 : MsgHandler* mh = ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance();
101 297062 : if (myAlternatives[0]->getFirst()->prohibits(&veh, hasRestrictions) && (!oc.getBool("repair.from")
102 : // do not try to reassign starting edge for trip input
103 9 : || myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
104 70 : mh->inform("Vehicle '" + veh.getID() + "' is not allowed to depart on edge '" +
105 35 : myAlternatives[0]->getFirst()->getID() + "'.");
106 35 : return;
107 296987 : } else if (myAlternatives[0]->getLast()->prohibits(&veh, hasRestrictions) && (!oc.getBool("repair.to")
108 : // do not try to reassign destination edge for trip input
109 9 : || myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
110 : // this check is not strictly necessary unless myTryRepair is set.
111 : // However, the error message is more helpful than "no connection found"
112 30 : mh->inform("Vehicle '" + veh.getID() + "' is not allowed to arrive on edge '" +
113 15 : myAlternatives[0]->getLast()->getID() + "'.");
114 15 : return;
115 : }
116 976301 : const bool skipTripRouting = (oc.exists("write-trips") && oc.getBool("write-trips")
117 388632 : && RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation());
118 296930 : if ((myTryRepair && !skipTripRouting) || myUsingJTRR) {
119 : ConstROEdgeVector newEdges;
120 202508 : if (repairCurrentRoute(router, begin, veh, myAlternatives[0]->getEdgeVector(), newEdges)) {
121 185280 : if (myAlternatives[0]->getEdgeVector() != newEdges) {
122 156897 : if (!myMayBeDisconnected) {
123 192 : WRITE_WARNINGF(TL("Repaired route of vehicle '%'."), veh.getID());
124 : }
125 156897 : myNewRoute = true;
126 156897 : RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
127 313794 : myPrecomputed = std::make_shared<RORoute>(myID, 0, myAlternatives[0]->getProbability(), newEdges, col, myAlternatives[0]->getStops());
128 : } else {
129 : myPrecomputed = myAlternatives[0];
130 : }
131 : }
132 : return;
133 202508 : }
134 94422 : if (RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation()
135 187940 : || OptionsCont::getOptions().getBool("remove-loops")) {
136 960 : if (skipTripRouting || myAlternatives[myLastUsed]->isValid(veh, ignoreErrors)) {
137 954 : myPrecomputed = myAlternatives[myLastUsed];
138 : }
139 : } else {
140 : // build a new route to test whether it is better
141 93462 : ConstROEdgeVector oldEdges({getOrigin(), getDestination()});
142 : ConstROEdgeVector edges;
143 93462 : if (repairCurrentRoute(router, begin, veh, oldEdges, edges, true)) {
144 93459 : if (edges.front()->isTazConnector()) {
145 : edges.erase(edges.begin());
146 : }
147 93459 : if (edges.back()->isTazConnector()) {
148 : edges.pop_back();
149 : }
150 : // check whether the same route was already used
151 : int existing = -1;
152 135160 : for (int i = 0; i < (int)myAlternatives.size(); i++) {
153 120035 : if (edges == myAlternatives[i]->getEdgeVector()) {
154 : existing = i;
155 : break;
156 : }
157 : }
158 93459 : if (existing >= 0) {
159 78334 : myPrecomputed = myAlternatives[existing];
160 : } else {
161 15125 : RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
162 15125 : myPrecomputed = std::make_shared<RORoute>(myID, 0, 1, edges, col, myAlternatives[0]->getStops());
163 15125 : myNewRoute = true;
164 : }
165 : }
166 93462 : }
167 : }
168 :
169 :
170 : bool
171 295970 : RORouteDef::repairCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
172 : SUMOTime begin, const ROVehicle& veh,
173 : ConstROEdgeVector oldEdges, ConstROEdgeVector& newEdges,
174 : bool isTrip) const {
175 295970 : MsgHandler* mh = (OptionsCont::getOptions().getBool("ignore-errors") ?
176 295970 : MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance());
177 295970 : RONet* net = RONet::getInstance();
178 295970 : const int initialSize = (int)oldEdges.size();
179 295970 : const bool hasRestrictions = RONet::getInstance()->hasParamRestrictions();
180 40 : if (net->hasProhibitions() && router.supportsProhibitions()) {
181 20 : if (net->getProhibitions().size() > 0 && !router.hasProhibitions()) {
182 2 : router.prohibit(net->getProhibitions());
183 : }
184 20 : net->updateLaneProhibitions(begin);
185 : }
186 295970 : if (initialSize == 1) {
187 6413 : if (myUsingJTRR) {
188 : /// only ROJTRRouter is supposed to handle this type of input
189 6134 : bool ok = router.compute(oldEdges.front(), nullptr, &veh, begin, newEdges);
190 10343 : myDiscardSilent = ok && newEdges.size() == 0;
191 : } else {
192 279 : newEdges = oldEdges;
193 : }
194 : } else {
195 289557 : if (oldEdges.front()->prohibits(&veh, hasRestrictions)) {
196 : // option repair.from is in effect
197 : const std::string& frontID = oldEdges.front()->getID();
198 18 : for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
199 15 : if ((*i)->prohibits(&veh, hasRestrictions) || (*i)->isInternal()) {
200 : i = oldEdges.erase(i);
201 : } else {
202 6 : WRITE_MESSAGE("Changing invalid starting edge '" + frontID
203 : + "' to '" + (*i)->getID() + "' for vehicle '" + veh.getID() + "'.");
204 3 : break;
205 : }
206 : }
207 : }
208 289557 : if (oldEdges.size() == 0) {
209 6 : mh->inform("Could not find new starting edge for vehicle '" + veh.getID() + "'.");
210 17231 : return false;
211 : }
212 289554 : if (oldEdges.back()->prohibits(&veh, hasRestrictions)) {
213 : // option repair.to is in effect
214 : const std::string& backID = oldEdges.back()->getID();
215 : // oldEdges cannot get empty here, otherwise we would have left the stage when checking "from"
216 6 : while (oldEdges.back()->prohibits(&veh, hasRestrictions) || oldEdges.back()->isInternal()) {
217 : oldEdges.pop_back();
218 : }
219 9 : WRITE_MESSAGE("Changing invalid destination edge '" + backID
220 : + "' to edge '" + oldEdges.back()->getID() + "' for vehicle '" + veh.getID() + "'.");
221 : }
222 289554 : std::vector<ROVehicle::Mandatory> mandatory = veh.getMandatoryEdges(oldEdges.front(), oldEdges.back());
223 : assert(mandatory.size() >= 2);
224 : // removed prohibited
225 902502 : for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
226 612948 : if ((*i)->prohibits(&veh, hasRestrictions) || (*i)->isInternal()) {
227 : // no need to check the mandatories here, this was done before
228 144 : WRITE_MESSAGEF(TL("Removing invalid edge '%' from route for vehicle '%'."), (*i)->getID(), veh.getID());
229 : i = oldEdges.erase(i);
230 : } else {
231 : ++i;
232 : }
233 : }
234 : // reconnect remaining edges
235 289554 : if (mandatory.size() > oldEdges.size() && initialSize > 2) {
236 513 : WRITE_MESSAGEF(TL("There are stop edges which were not part of the original route for vehicle '%'."), veh.getID());
237 : }
238 : ConstROEdgeVector targets;
239 : bool checkPositions = false;
240 289554 : if (mandatory.size() >= oldEdges.size()) {
241 902832 : for (auto m : mandatory) {
242 613334 : targets.push_back(m.edge);
243 613334 : checkPositions |= m.pos >= 0;
244 : }
245 : } else {
246 56 : targets = oldEdges;
247 : }
248 289554 : newEdges.push_back(targets.front());
249 : auto nextMandatory = mandatory.begin() + 1;
250 289554 : while (nextMandatory != mandatory.end()
251 292066 : && targets.front() == nextMandatory->edge
252 326870 : && (nextMandatory->edge != (nextMandatory - 1)->edge
253 18661 : || nextMandatory->pos >= (nextMandatory - 1)->pos
254 : // ignore invalid via stop pos
255 18019 : || nextMandatory->pos < 0)) {
256 : nextMandatory++;
257 : }
258 : int lastMandatory = 0;
259 289554 : for (ConstROEdgeVector::const_iterator i = targets.begin() + 1;
260 578059 : i != targets.end() && nextMandatory != mandatory.end(); ++i) {
261 305729 : const ROEdge* prev = *(i - 1);
262 305729 : const ROEdge* cur = *i;
263 348553 : if (prev->isConnectedTo(*cur, veh.getVClass()) && (!isRailway(veh.getVClass()) || prev->getBidiEdge() != cur)) {
264 42820 : newEdges.push_back(cur);
265 : } else {
266 262909 : if (initialSize > 2) {
267 : // only inform if the input is (probably) not a trip
268 165280 : WRITE_MESSAGEF(TL("Edge '%' not connected to edge '%' for vehicle '%'."), (*(i - 1))->getID(), (*i)->getID(), veh.getID());
269 : }
270 262909 : const ROEdge* last = newEdges.back();
271 : newEdges.pop_back();
272 262909 : if (last->isTazConnector() && newEdges.size() > 1) {
273 : // assume this was a viaTaz
274 28 : last = newEdges.back();
275 : newEdges.pop_back();
276 : }
277 262909 : if (veh.hasJumps() && (nextMandatory - 1)->isJump) {
278 32 : while (*i != nextMandatory->edge) {
279 : ++i;
280 : }
281 24 : newEdges.push_back(last);
282 24 : newEdges.push_back(*i);
283 : //std::cout << " skipJump mIndex=" << (nextMandatory - 1 - mandatory.begin()) << " last=" << last->getID() << " next=" << (*i)->getID() << " newEdges=" << toString(newEdges) << "\n";
284 : } else {
285 262885 : int numEdgesBefore = (int)newEdges.size();
286 : // router.setHint(targets.begin(), i, &veh, begin);
287 262885 : if (myTryRepair && lastMandatory < (int)newEdges.size() && last != newEdges[lastMandatory]) {
288 26 : router.setMsgHandler(MsgHandler::getWarningInstance());
289 : }
290 : bool ok;
291 : if (checkPositions
292 262846 : && mandatory[i - targets.begin() - 1].pos >= 0
293 506072 : && mandatory[i - targets.begin()].pos >= 0) {
294 572 : ok = router.compute(
295 : last, mandatory[i - targets.begin() - 1].pos,
296 : *i, mandatory[i - targets.begin()].pos,
297 : &veh, begin, newEdges);
298 : } else {
299 262313 : ok = router.compute(last, *i, &veh, begin, newEdges);
300 : }
301 262885 : router.setMsgHandler(mh);
302 262885 : if (!ok) {
303 : // backtrack: try to route from last mandatory edge to next mandatory edge
304 : // XXX add option for backtracking in smaller increments
305 : // (i.e. previous edge to edge after *i)
306 : // we would then need to decide whether we have found a good
307 : // tradeoff between faithfulness to the input data and detour-length
308 17233 : if (lastMandatory >= (int)newEdges.size() || last == newEdges[lastMandatory] || !backTrack(router, i, lastMandatory, nextMandatory->edge, newEdges, veh, begin)) {
309 34448 : mh->inform("Mandatory edge '" + (*i)->getID() + "' not reachable by vehicle '" + veh.getID() + "'.");
310 17224 : return false;
311 : }
312 245652 : } else if (!myMayBeDisconnected && !isTrip && last != (*i)) {
313 46 : double airDist = last->getToJunction()->getPosition().distanceTo(
314 : (*i)->getFromJunction()->getPosition());
315 : double repairDist = 0;
316 115 : for (auto it2 = (newEdges.begin() + numEdgesBefore + 1); it2 != newEdges.end() && it2 != newEdges.end() - 1; it2++) {
317 69 : repairDist += (*it2)->getLength();
318 : }
319 46 : const double detourFactor = repairDist / MAX2(airDist, 1.0);
320 46 : const double detour = MAX2(0.0, repairDist - airDist);
321 46 : const double maxDetourFactor = OptionsCont::getOptions().getFloat("repair.max-detour-factor");
322 46 : if (detourFactor > maxDetourFactor) {
323 3 : WRITE_MESSAGEF(" Backtracking to avoid detour of %m for gap of %m)", detour, airDist);
324 3 : backTrack(router, i, lastMandatory, nextMandatory->edge, newEdges, veh, begin);
325 43 : } else if (detourFactor > 1.1) {
326 14 : WRITE_MESSAGEF(" Taking detour of %m to avoid gap of %m)", detour, airDist);
327 : }
328 : }
329 : }
330 : }
331 : while (nextMandatory != mandatory.end()
332 320287 : && *i == nextMandatory->edge
333 864437 : && (nextMandatory->edge != (nextMandatory - 1)->edge
334 4986 : || nextMandatory->pos >= (nextMandatory - 1)->pos
335 : // ignore invalid via stop pos
336 60 : || nextMandatory->pos < 0)) {
337 : nextMandatory++;
338 287959 : lastMandatory = (int)newEdges.size() - 1;
339 : }
340 : }
341 272330 : if (veh.getParameter().via.size() > 0 && veh.getParameter().stops.size() > 0) {
342 : // check consistency of stops and vias
343 : auto it = newEdges.begin();
344 12 : for (const auto& stop : veh.getParameter().stops) {
345 16 : const ROEdge* e = net->getEdge(stop.edge);
346 8 : it = std::find(it, newEdges.end(), e);
347 8 : if (it == newEdges.end()) {
348 8 : mh->inform("Stop edge '" + e->getID() + "' is inconsistent with via edges for vehicle '" + veh.getID() + "'.");
349 4 : return false;
350 : }
351 : }
352 : }
353 289554 : }
354 : return true;
355 : }
356 :
357 :
358 : bool
359 12 : RORouteDef::backTrack(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
360 : ConstROEdgeVector::const_iterator& i, int lastMandatory, const ROEdge* nextMandatory,
361 : ConstROEdgeVector& newEdges, const ROVehicle& veh, SUMOTime begin) {
362 : ConstROEdgeVector edges;
363 12 : bool ok = router.compute(newEdges[lastMandatory], nextMandatory, &veh, begin, edges, true);
364 12 : if (!ok) {
365 : return false;
366 : }
367 :
368 21 : while (*i != nextMandatory) {
369 : ++i;
370 : }
371 : newEdges.erase(newEdges.begin() + lastMandatory + 1, newEdges.end());
372 : std::copy(edges.begin() + 1, edges.end(), back_inserter(newEdges));
373 : return true;
374 12 : }
375 :
376 :
377 : std::shared_ptr<RORoute>
378 277760 : RORouteDef::addAlternative(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
379 : const ROVehicle* const veh, std::shared_ptr<RORoute> current, SUMOTime begin,
380 : MsgHandler* errorHandler) {
381 : std::shared_ptr<RORoute> replaced = nullptr;
382 277760 : if (myTryRepair || myUsingJTRR) {
383 183355 : if (myNewRoute) {
384 : replaced = myAlternatives[0];
385 : myAlternatives[0] = current;
386 : }
387 366710 : if (!router.isValid(current->getEdgeVector(), veh, STEPS2TIME(begin))) {
388 0 : throw ProcessError("Route '" + getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
389 : }
390 183355 : double costs = router.recomputeCosts(current->getEdgeVector(), veh, begin);
391 183355 : if (veh->hasJumps()) {
392 : // @todo: jumpTime should be applied in recomputeCost to ensure the
393 : // correctness of time-dependent traveltimes
394 20 : costs += STEPS2TIME(veh->getJumpTime());
395 : }
396 183355 : current->setCosts(costs);
397 : return replaced;
398 : }
399 : // add the route when it's new
400 94405 : if (myAlternatives.back()->getProbability() < 0 || !myAlternatives.back()->isPermitted(veh, errorHandler)) {
401 178 : if (myAlternatives.back()->getProbability() >= 0 && errorHandler == MsgHandler::getErrorInstance()) {
402 48 : throw ProcessError("Route '" + current->getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
403 : }
404 : myAlternatives.pop_back();
405 : }
406 94389 : if (myNewRoute) {
407 15109 : myAlternatives.push_back(current);
408 : }
409 : // recompute the costs and (when a new route was added) scale the probabilities
410 94389 : const double scale = double(myAlternatives.size() - 1) / double(myAlternatives.size());
411 346246 : for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
412 503714 : if (!router.isValid(alt->getEdgeVector(), veh, STEPS2TIME(begin))) {
413 0 : throw ProcessError("Route '" + current->getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
414 : }
415 : // recompute the costs for all routes
416 251857 : const double newCosts = router.recomputeCosts(alt->getEdgeVector(), veh, begin);
417 : assert(myAlternatives.size() != 0);
418 251857 : if (myNewRoute) {
419 36606 : if (alt == current) {
420 : // set initial probability and costs
421 15109 : alt->setProbability(1. / (double) myAlternatives.size());
422 15109 : alt->setCosts(newCosts);
423 : } else {
424 : // rescale probs for all others
425 21497 : alt->setProbability(alt->getProbability() * scale);
426 : }
427 : }
428 503730 : RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().setCosts(alt, newCosts, alt == myAlternatives[myLastUsed]);
429 : }
430 : assert(myAlternatives.size() != 0);
431 94389 : RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().calculateProbabilities(myAlternatives, veh, veh->getDepartureTime());
432 94389 : const bool keepRoute = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepRoute();
433 94389 : if (!RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepAllRoutes() && !keepRoute) {
434 : // remove with probability of 0 (not mentioned in Gawron)
435 330894 : for (std::vector<std::shared_ptr<RORoute>>::iterator i = myAlternatives.begin(); i != myAlternatives.end();) {
436 240689 : if ((*i)->getProbability() == 0) {
437 : i = myAlternatives.erase(i);
438 : } else {
439 : i++;
440 : }
441 : }
442 : }
443 94389 : int maxNumber = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().getMaxRouteNumber();
444 94389 : if ((int)myAlternatives.size() > maxNumber) {
445 56 : std::shared_ptr<const RORoute> last = myAlternatives[myLastUsed];
446 : // only keep the routes with highest probability
447 56 : sort(myAlternatives.begin(), myAlternatives.end(), [](const std::shared_ptr<const RORoute> a, const std::shared_ptr<const RORoute> b) {
448 70 : return a->getProbability() > b->getProbability();
449 : });
450 56 : if (keepRoute) {
451 18 : for (int i = 0; i < (int)myAlternatives.size(); i++) {
452 18 : if (myAlternatives[i] == last) {
453 12 : myLastUsed = i;
454 12 : break;
455 : }
456 : }
457 12 : if (myLastUsed >= maxNumber) {
458 6 : std::swap(myAlternatives[maxNumber - 1], myAlternatives[myLastUsed]);
459 6 : myLastUsed = maxNumber - 1;
460 : }
461 : }
462 : myAlternatives.erase(myAlternatives.begin() + maxNumber, myAlternatives.end());
463 : }
464 : // rescale probabilities
465 : double newSum = 0.;
466 346190 : for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
467 251801 : newSum += alt->getProbability();
468 : }
469 : assert(newSum > 0);
470 : // @note newSum may be larger than 1 for numerical reasons
471 346190 : for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
472 251801 : alt->setProbability(alt->getProbability() / newSum);
473 : }
474 :
475 : // find the route to use
476 94389 : if (!keepRoute) {
477 90205 : double chosen = RandHelper::rand();
478 90205 : myLastUsed = 0;
479 156009 : for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
480 156009 : chosen -= alt->getProbability();
481 156009 : if (chosen <= 0) {
482 : return nullptr;
483 : }
484 65804 : myLastUsed++;
485 : }
486 : }
487 : return nullptr;
488 : }
489 :
490 :
491 : const ROEdge*
492 93462 : RORouteDef::getOrigin() const {
493 93462 : return myAlternatives.back()->getFirst();
494 : }
495 :
496 :
497 : const ROEdge*
498 93462 : RORouteDef::getDestination() const {
499 93462 : return myAlternatives.back()->getLast();
500 : }
501 :
502 :
503 : OutputDevice&
504 311562 : RORouteDef::writeXMLDefinition(OutputDevice& dev, const ROVehicle* const veh,
505 : bool asAlternatives, bool withExitTimes, bool withCost, bool withLength) const {
506 311562 : if (asAlternatives) {
507 113033 : dev.openTag(SUMO_TAG_ROUTE_DISTRIBUTION).writeAttr(SUMO_ATTR_LAST, myLastUsed);
508 383481 : for (int i = 0; i != (int)myAlternatives.size(); i++) {
509 540896 : myAlternatives[i]->writeXMLDefinition(dev, veh, true, true, withExitTimes, withLength);
510 : }
511 113033 : dev.closeTag();
512 113033 : return dev;
513 : } else {
514 397058 : return myAlternatives[myLastUsed]->writeXMLDefinition(dev, veh, withCost, false, withExitTimes, withLength);
515 : }
516 : }
517 :
518 :
519 : RORouteDef*
520 20008 : RORouteDef::copy(const std::string& id, const SUMOTime stopOffset) const {
521 20008 : RORouteDef* result = new RORouteDef(id, 0, myTryRepair, myMayBeDisconnected);
522 40896 : for (const std::shared_ptr<const RORoute> route : myAlternatives) {
523 20888 : RGBColor* col = route->getColor() != nullptr ? new RGBColor(*route->getColor()) : nullptr;
524 20888 : std::shared_ptr<RORoute> newRoute = std::make_shared<RORoute>(id, route->getCosts(), route->getProbability(), route->getEdgeVector(), col, route->getStops());
525 : newRoute->addStopOffset(stopOffset);
526 20888 : result->addLoadedAlternative(newRoute);
527 : }
528 20008 : return result;
529 : }
530 :
531 :
532 : double
533 91101 : RORouteDef::getOverallProb() const {
534 : double sum = 0.;
535 323700 : for (const std::shared_ptr<const RORoute> r : myAlternatives) {
536 232599 : sum += r->getProbability();
537 : }
538 91101 : return sum;
539 : }
540 :
541 :
542 : /****************************************************************************/
|