LCOV - code coverage report
Current view: top level - src/router - RORouteDef.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 97.9 % 234 229
Test Date: 2026-01-01 15:49:29 Functions: 100.0 % 16 16

            Line data    Source code
       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    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       301471 : RORouteDef::RORouteDef(const std::string& id, const int lastUsed,
      52       301471 :                        const bool tryRepair, const bool mayBeDisconnected) :
      53       301471 :     Named(StringUtils::convertUmlaute(id)),
      54       301471 :     myPrecomputed(nullptr), myLastUsed(lastUsed), myTryRepair(tryRepair),
      55       301471 :     myMayBeDisconnected(mayBeDisconnected),
      56       301471 :     myDiscardSilent(false) {
      57       301471 : }
      58              : 
      59              : 
      60       602942 : RORouteDef::~RORouteDef() {
      61       759719 :     for (std::vector<RORoute*>::iterator i = myAlternatives.begin(); i != myAlternatives.end(); i++) {
      62              :         if (myRouteRefs.count(*i) == 0) {
      63       458244 :             delete *i;
      64              :         }
      65              :     }
      66       602942 : }
      67              : 
      68              : 
      69              : void
      70       443354 : RORouteDef::addLoadedAlternative(RORoute* alt) {
      71       443354 :     myAlternatives.push_back(alt);
      72       443354 : }
      73              : 
      74              : 
      75              : void
      76            4 : RORouteDef::addAlternativeDef(const RORouteDef* alt) {
      77              :     std::copy(alt->myAlternatives.begin(), alt->myAlternatives.end(),
      78            4 :               back_inserter(myAlternatives));
      79              :     std::copy(alt->myAlternatives.begin(), alt->myAlternatives.end(),
      80            4 :               std::inserter(myRouteRefs, myRouteRefs.end()));
      81            4 : }
      82              : 
      83              : 
      84              : RORoute*
      85       296690 : RORouteDef::buildCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
      86              :                               SUMOTime begin, const ROVehicle& veh) const {
      87       296690 :     if (myPrecomputed == nullptr) {
      88       296690 :         preComputeCurrentRoute(router, begin, veh);
      89              :     }
      90       296690 :     return myPrecomputed;
      91              : }
      92              : 
      93              : 
      94              : void
      95       296693 : RORouteDef::validateAlternatives(const ROVehicle* veh, MsgHandler* errorHandler) {
      96       735912 :     for (int i = 0; i < (int)myAlternatives.size();) {
      97       439219 :         if (i != myLastUsed || mySkipNewRoutes) {
      98       143437 :             if (myAlternatives[i]->isPermitted(veh, errorHandler)) {
      99       143432 :                 i++;
     100              :             } else {
     101            5 :                 myAlternatives.erase(myAlternatives.begin() + i); 
     102            5 :                 if (myLastUsed > i) {
     103            0 :                     myLastUsed--;
     104              :                 }
     105              :             }
     106              :         } else {
     107       295782 :             i++;
     108              :         }
     109              :     }
     110       296693 : }
     111              : 
     112              : 
     113              : void
     114       296690 : RORouteDef::preComputeCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
     115              :                                    SUMOTime begin, const ROVehicle& veh) const {
     116       296690 :     myNewRoute = false;
     117       296690 :     const OptionsCont& oc = OptionsCont::getOptions();
     118       296690 :     const bool ignoreErrors = oc.getBool("ignore-errors");
     119       296690 :     const bool hasRestrictions = RONet::getInstance()->hasParamRestrictions();
     120              :     assert(myAlternatives[0]->getEdgeVector().size() > 0);
     121       296690 :     MsgHandler* mh = ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance();
     122       296772 :     if (myAlternatives[0]->getFirst()->prohibits(&veh, hasRestrictions) && (!oc.getBool("repair.from")
     123              :             // do not try to reassign starting edge for trip input
     124            9 :             || myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
     125           70 :         mh->inform("Vehicle '" + veh.getID() + "' is not allowed to depart on edge '" +
     126           35 :                    myAlternatives[0]->getFirst()->getID() + "'.");
     127           35 :         return;
     128       296697 :     } else if (myAlternatives[0]->getLast()->prohibits(&veh, hasRestrictions) && (!oc.getBool("repair.to")
     129              :                // do not try to reassign destination edge for trip input
     130            9 :                || myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
     131              :         // this check is not strictly necessary unless myTryRepair is set.
     132              :         // However, the error message is more helpful than "no connection found"
     133           30 :         mh->inform("Vehicle '" + veh.getID() + "' is not allowed to arrive on edge '" +
     134           15 :                    myAlternatives[0]->getLast()->getID() + "'.");
     135           15 :         return;
     136              :     }
     137       975426 :     const bool skipTripRouting = (oc.exists("write-trips") && oc.getBool("write-trips")
     138       388338 :                                   && RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation());
     139       296640 :     if ((myTryRepair && !skipTripRouting) || myUsingJTRR) {
     140              :         ConstROEdgeVector newEdges;
     141       202226 :         if (repairCurrentRoute(router, begin, veh, myAlternatives[0]->getEdgeVector(), newEdges)) {
     142       185087 :             if (myAlternatives[0]->getEdgeVector() != newEdges) {
     143       156717 :                 if (!myMayBeDisconnected) {
     144          156 :                     WRITE_WARNINGF(TL("Repaired route of vehicle '%'."), veh.getID());
     145              :                 }
     146       156717 :                 myNewRoute = true;
     147       156717 :                 RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
     148       156717 :                 myPrecomputed = new RORoute(myID, 0, myAlternatives[0]->getProbability(), newEdges, col, myAlternatives[0]->getStops());
     149              :             } else {
     150        28370 :                 myPrecomputed = myAlternatives[0];
     151              :             }
     152              :         }
     153              :         return;
     154       202226 :     }
     155        94414 :     if ((RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation()
     156       187980 :             || OptionsCont::getOptions().getBool("remove-loops"))
     157        94470 :             && (skipTripRouting || myAlternatives[myLastUsed]->isValid(veh, ignoreErrors))) {
     158          956 :         myPrecomputed = myAlternatives[myLastUsed];
     159              :     } else {
     160              :         // build a new route to test whether it is better
     161        93458 :         ConstROEdgeVector oldEdges({getOrigin(), getDestination()});
     162              :         ConstROEdgeVector edges;
     163        93458 :         if (repairCurrentRoute(router, begin, veh, oldEdges, edges, true)) {
     164        93455 :             if (edges.front()->isTazConnector()) {
     165              :                 edges.erase(edges.begin());
     166              :             }
     167        93455 :             if (edges.back()->isTazConnector()) {
     168              :                 edges.pop_back();
     169              :             }
     170              :             // check whether the same route was already used
     171              :             int existing = -1;
     172       135130 :             for (int i = 0; i < (int)myAlternatives.size(); i++) {
     173       120001 :                 if (edges == myAlternatives[i]->getEdgeVector()) {
     174              :                     existing = i;
     175              :                     break;
     176              :                 }
     177              :             }
     178        93455 :             if (existing >= 0) {
     179        78326 :                 myPrecomputed = myAlternatives[existing];
     180              :             } else {
     181        15129 :                 RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
     182        15129 :                 myPrecomputed = new RORoute(myID, 0, 1, edges, col, myAlternatives[0]->getStops());
     183        15129 :                 myNewRoute = true;
     184              :             }
     185              :         }
     186        93458 :     }
     187              : }
     188              : 
     189              : 
     190              : bool
     191       295684 : RORouteDef::repairCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
     192              :                                SUMOTime begin, const ROVehicle& veh,
     193              :                                ConstROEdgeVector oldEdges, ConstROEdgeVector& newEdges,
     194              :                                bool isTrip) const {
     195       591368 :     MsgHandler* mh = (OptionsCont::getOptions().getBool("ignore-errors") ?
     196       295684 :                       MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance());
     197       295684 :     const int initialSize = (int)oldEdges.size();
     198       295684 :     const bool hasRestrictions = RONet::getInstance()->hasParamRestrictions();
     199       295684 :     if (RONet::getInstance()->getProhibitions().size() > 0 && !router.hasProhibitions()) {
     200           12 :         router.prohibit(RONet::getInstance()->getProhibitions());
     201              :     }
     202       295684 :     if (initialSize == 1) {
     203         6414 :         if (myUsingJTRR) {
     204              :             /// only ROJTRRouter is supposed to handle this type of input
     205         6135 :             bool ok = router.compute(oldEdges.front(), nullptr, &veh, begin, newEdges);
     206        10345 :             myDiscardSilent = ok && newEdges.size() == 0;
     207              :         } else {
     208          279 :             newEdges = oldEdges;
     209              :         }
     210              :     } else {
     211       289270 :         if (oldEdges.front()->prohibits(&veh, hasRestrictions)) {
     212              :             // option repair.from is in effect
     213              :             const std::string& frontID = oldEdges.front()->getID();
     214           18 :             for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
     215           15 :                 if ((*i)->prohibits(&veh, hasRestrictions) || (*i)->isInternal()) {
     216              :                     i = oldEdges.erase(i);
     217              :                 } else {
     218            6 :                     WRITE_MESSAGE("Changing invalid starting edge '" + frontID
     219              :                                   + "' to '" + (*i)->getID() + "' for vehicle '" + veh.getID() + "'.");
     220            3 :                     break;
     221              :                 }
     222              :             }
     223              :         }
     224       289270 :         if (oldEdges.size() == 0) {
     225            6 :             mh->inform("Could not find new starting edge for vehicle '" + veh.getID() + "'.");
     226        17142 :             return false;
     227              :         }
     228       289267 :         if (oldEdges.back()->prohibits(&veh, hasRestrictions)) {
     229              :             // option repair.to is in effect
     230              :             const std::string& backID = oldEdges.back()->getID();
     231              :             // oldEdges cannot get empty here, otherwise we would have left the stage when checking "from"
     232            6 :             while (oldEdges.back()->prohibits(&veh, hasRestrictions) || oldEdges.back()->isInternal()) {
     233              :                 oldEdges.pop_back();
     234              :             }
     235            9 :             WRITE_MESSAGE("Changing invalid destination edge '" + backID
     236              :                           + "' to edge '" + oldEdges.back()->getID() + "' for vehicle '" + veh.getID() + "'.");
     237              :         }
     238       289267 :         ConstROEdgeVector mandatory = veh.getMandatoryEdges(oldEdges.front(), oldEdges.back());
     239              :         std::set<ConstROEdgeVector::const_iterator> jumpStarts;
     240       289267 :         veh.collectJumps(mandatory, jumpStarts);
     241              :         assert(mandatory.size() >= 2);
     242              :         // removed prohibited
     243       901447 :         for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
     244       612180 :             if ((*i)->prohibits(&veh, hasRestrictions) || (*i)->isInternal()) {
     245              :                 // no need to check the mandatories here, this was done before
     246          132 :                 WRITE_MESSAGEF(TL("Removing invalid edge '%' from route for vehicle '%'."), (*i)->getID(), veh.getID());
     247              :                 i = oldEdges.erase(i);
     248              :             } else {
     249              :                 ++i;
     250              :             }
     251              :         }
     252              :         // reconnect remaining edges
     253       289267 :         if (mandatory.size() > oldEdges.size() && initialSize > 2) {
     254            0 :             WRITE_MESSAGEF(TL("There are stop edges which were not part of the original route for vehicle '%'."), veh.getID());
     255              :         }
     256       289267 :         const ConstROEdgeVector& targets = mandatory.size() > oldEdges.size() ? mandatory : oldEdges;
     257       289267 :         newEdges.push_back(targets.front());
     258              :         ConstROEdgeVector::iterator nextMandatory = mandatory.begin() + 1;
     259              :         int lastMandatory = 0;
     260       289267 :         for (ConstROEdgeVector::const_iterator i = targets.begin() + 1;
     261       593315 :                 i != targets.end() && nextMandatory != mandatory.end(); ++i) {
     262       321187 :             const ROEdge* prev = *(i - 1);
     263       321187 :             const ROEdge* cur = *i;
     264       363849 :             if (prev->isConnectedTo(*cur, veh.getVClass()) && (!isRailway(veh.getVClass()) || prev->getBidiEdge() != cur)) {
     265        42658 :                 newEdges.push_back(cur);
     266              :             } else {
     267       278529 :                 if (initialSize > 2) {
     268              :                     // only inform if the input is (probably) not a trip
     269       164175 :                     WRITE_MESSAGEF(TL("Edge '%' not connected to edge '%' for vehicle '%'."), (*(i - 1))->getID(), (*i)->getID(), veh.getID());
     270              :                 }
     271       278529 :                 const ROEdge* last = newEdges.back();
     272              :                 newEdges.pop_back();
     273       278529 :                 if (last->isTazConnector() && newEdges.size() > 1) {
     274              :                     // assume this was a viaTaz
     275           28 :                     last = newEdges.back();
     276              :                     newEdges.pop_back();
     277              :                 }
     278       278529 :                 if (veh.hasJumps() && jumpStarts.count(nextMandatory - 1) != 0) {
     279           24 :                     while (*i != *nextMandatory) {
     280              :                         ++i;
     281              :                     }
     282           24 :                     newEdges.push_back(last);
     283           24 :                     newEdges.push_back(*i);
     284              :                     //std::cout << " skipJump mIndex=" << (nextMandatory - 1 - mandatory.begin()) << " last=" << last->getID() << " next=" << (*i)->getID() << " newEdges=" << toString(newEdges) << "\n";
     285              :                 } else {
     286              : 
     287       278505 :                     int numEdgesBefore = (int)newEdges.size();
     288              :                     //                router.setHint(targets.begin(), i, &veh, begin);
     289       278505 :                     if (myTryRepair && lastMandatory < (int)newEdges.size() && last != newEdges[lastMandatory]) {
     290           20 :                         router.setMsgHandler(MsgHandler::getWarningInstance());
     291              :                     }
     292       278505 :                     bool ok = router.compute(last, *i, &veh, begin, newEdges);
     293              :                     router.setMsgHandler(mh);
     294       278505 :                     if (!ok) {
     295              :                         // backtrack: try to route from last mandatory edge to next mandatory edge
     296              :                         // XXX add option for backtracking in smaller increments
     297              :                         // (i.e. previous edge to edge after *i)
     298              :                         // we would then need to decide whether we have found a good
     299              :                         // tradeoff between faithfulness to the input data and detour-length
     300        17145 :                         if (lastMandatory >= (int)newEdges.size() || last == newEdges[lastMandatory] || !backTrack(router, i, lastMandatory, nextMandatory, newEdges, veh, begin)) {
     301        34278 :                             mh->inform("Mandatory edge '" + (*i)->getID() + "' not reachable by vehicle '" + veh.getID() + "'.");
     302        17139 :                             return false;
     303              :                         }
     304       261360 :                     } else if (!myMayBeDisconnected && !isTrip && last != (*i)) {
     305           31 :                         double airDist = last->getToJunction()->getPosition().distanceTo(
     306              :                                              (*i)->getFromJunction()->getPosition());
     307              :                         double repairDist = 0;
     308           82 :                         for (auto it2 = (newEdges.begin() + numEdgesBefore + 1); it2 != newEdges.end() && it2 != newEdges.end() - 1; it2++) {
     309           51 :                             repairDist += (*it2)->getLength();
     310              :                         }
     311           31 :                         const double detourFactor = repairDist / MAX2(airDist, 1.0);
     312           31 :                         const double detour = MAX2(0.0, repairDist - airDist);
     313           31 :                         const double maxDetourFactor = OptionsCont::getOptions().getFloat("repair.max-detour-factor");
     314           31 :                         if (detourFactor > maxDetourFactor) {
     315            3 :                             WRITE_MESSAGEF("    Backtracking to avoid detour of %m for gap of %m)", detour, airDist);
     316            3 :                             backTrack(router, i, lastMandatory, nextMandatory, newEdges, veh, begin);
     317           28 :                         } else if (detourFactor > 1.1) {
     318            8 :                             WRITE_MESSAGEF("    Taking detour of %m to avoid gap of %m)", detour, airDist);
     319              :                         }
     320              :                     }
     321              :                 }
     322              :             }
     323       304048 :             if (*i == *nextMandatory) {
     324              :                 nextMandatory++;
     325       298856 :                 lastMandatory = (int)newEdges.size() - 1;
     326              :             }
     327              :         }
     328       289267 :     }
     329              :     return true;
     330              : }
     331              : 
     332              : 
     333              : bool
     334            9 : RORouteDef::backTrack(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
     335              :                       ConstROEdgeVector::const_iterator& i, int lastMandatory, ConstROEdgeVector::iterator nextMandatory,
     336              :                       ConstROEdgeVector& newEdges, const ROVehicle& veh, SUMOTime begin) {
     337              :     ConstROEdgeVector edges;
     338            9 :     bool ok = router.compute(newEdges[lastMandatory], *nextMandatory, &veh, begin, edges, true);
     339            9 :     if (!ok) {
     340              :         return false;
     341              :     }
     342              : 
     343           18 :     while (*i != *nextMandatory) {
     344              :         ++i;
     345              :     }
     346              :     newEdges.erase(newEdges.begin() + lastMandatory + 1, newEdges.end());
     347              :     std::copy(edges.begin() + 1, edges.end(), back_inserter(newEdges));
     348              :     return true;
     349            9 : }
     350              : 
     351              : 
     352              : void
     353       277565 : RORouteDef::addAlternative(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
     354              :                            const ROVehicle* const veh, RORoute* current, SUMOTime begin,
     355              :                            MsgHandler* errorHandler) {
     356       277565 :     if (myTryRepair || myUsingJTRR) {
     357       183162 :         if (myNewRoute) {
     358       154784 :             delete myAlternatives[0];
     359       154784 :             myAlternatives[0] = current;
     360              :         }
     361       366324 :         if (!router.isValid(current->getEdgeVector(), veh, STEPS2TIME(begin))) {
     362            0 :             throw ProcessError("Route '" + getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
     363              :         }
     364       183162 :         double costs = router.recomputeCosts(current->getEdgeVector(), veh, begin);
     365       183162 :         if (veh->hasJumps()) {
     366              :             // @todo: jumpTime should be applied in recomputeCost to ensure the
     367              :             // correctness of time-dependent traveltimes
     368           20 :             costs += STEPS2TIME(veh->getJumpTime());
     369              :         }
     370       183162 :         current->setCosts(costs);
     371       183162 :         return;
     372              :     }
     373              :     // add the route when it's new
     374        94403 :     if (myAlternatives.back()->getProbability() < 0 || !myAlternatives.back()->isPermitted(veh, errorHandler)) {
     375          178 :         if (myAlternatives.back()->getProbability() >= 0 && errorHandler == MsgHandler::getErrorInstance()) {
     376           48 :             throw ProcessError("Route '" + current->getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
     377              :         }
     378          162 :         delete myAlternatives.back();
     379              :         myAlternatives.pop_back();
     380              :     }
     381        94387 :     if (myNewRoute) {
     382        15113 :         myAlternatives.push_back(current);
     383              :     }
     384              :     // recompute the costs and (when a new route was added) scale the probabilities
     385        94387 :     const double scale = double(myAlternatives.size() - 1) / double(myAlternatives.size());
     386       346246 :     for (RORoute* const alt : myAlternatives) {
     387       503718 :         if (!router.isValid(alt->getEdgeVector(), veh, STEPS2TIME(begin))) {
     388            0 :             throw ProcessError("Route '" + current->getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
     389              :         }
     390              :         // recompute the costs for all routes
     391       251859 :         const double newCosts = router.recomputeCosts(alt->getEdgeVector(), veh, begin);
     392              :         assert(myAlternatives.size() != 0);
     393       251859 :         if (myNewRoute) {
     394        36614 :             if (alt == current) {
     395              :                 // set initial probability and costs
     396        15113 :                 alt->setProbability(1. / (double) myAlternatives.size());
     397        15113 :                 alt->setCosts(newCosts);
     398              :             } else {
     399              :                 // rescale probs for all others
     400        21501 :                 alt->setProbability(alt->getProbability() * scale);
     401              :             }
     402              :         }
     403       251859 :         RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().setCosts(alt, newCosts, alt == myAlternatives[myLastUsed]);
     404              :     }
     405              :     assert(myAlternatives.size() != 0);
     406        94387 :     RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().calculateProbabilities(myAlternatives, veh, veh->getDepartureTime());
     407        94387 :     const bool keepRoute = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepRoute();
     408        94387 :     if (!RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepAllRoutes() && !keepRoute) {
     409              :         // remove with probability of 0 (not mentioned in Gawron)
     410       330894 :         for (std::vector<RORoute*>::iterator i = myAlternatives.begin(); i != myAlternatives.end();) {
     411       240691 :             if ((*i)->getProbability() == 0) {
     412            0 :                 delete *i;
     413              :                 i = myAlternatives.erase(i);
     414              :             } else {
     415              :                 i++;
     416              :             }
     417              :         }
     418              :     }
     419        94387 :     int maxNumber = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().getMaxRouteNumber();
     420        94387 :     if ((int)myAlternatives.size() > maxNumber) {
     421           56 :         const RORoute* last = myAlternatives[myLastUsed];
     422              :         // only keep the routes with highest probability
     423           56 :         sort(myAlternatives.begin(), myAlternatives.end(), [](const RORoute * const a, const RORoute * const b) {
     424              :             return a->getProbability() > b->getProbability();
     425              :         });
     426           56 :         if (keepRoute) {
     427           18 :             for (int i = 0; i < (int)myAlternatives.size(); i++) {
     428           18 :                 if (myAlternatives[i] == last) {
     429           12 :                     myLastUsed = i;
     430           12 :                     break;
     431              :                 }
     432              :             }
     433           12 :             if (myLastUsed >= maxNumber) {
     434            6 :                 std::swap(myAlternatives[maxNumber - 1], myAlternatives[myLastUsed]);
     435            6 :                 myLastUsed = maxNumber - 1;
     436              :             }
     437              :         }
     438          112 :         for (std::vector<RORoute*>::iterator i = myAlternatives.begin() + maxNumber; i != myAlternatives.end(); i++) {
     439           56 :             delete *i;
     440              :         }
     441              :         myAlternatives.erase(myAlternatives.begin() + maxNumber, myAlternatives.end());
     442              :     }
     443              :     // rescale probabilities
     444              :     double newSum = 0.;
     445       346190 :     for (const RORoute* const alt : myAlternatives) {
     446       251803 :         newSum += alt->getProbability();
     447              :     }
     448              :     assert(newSum > 0);
     449              :     // @note newSum may be larger than 1 for numerical reasons
     450       346190 :     for (RORoute* const alt : myAlternatives) {
     451       251803 :         alt->setProbability(alt->getProbability() / newSum);
     452              :     }
     453              : 
     454              :     // find the route to use
     455        94387 :     if (!keepRoute) {
     456        90203 :         double chosen = RandHelper::rand();
     457        90203 :         myLastUsed = 0;
     458       156009 :         for (const RORoute* const alt : myAlternatives) {
     459       156009 :             chosen -= alt->getProbability();
     460       156009 :             if (chosen <= 0) {
     461              :                 return;
     462              :             }
     463        65806 :             myLastUsed++;
     464              :         }
     465              :     }
     466              : }
     467              : 
     468              : const ROEdge*
     469        93458 : RORouteDef::getOrigin() const {
     470        93458 :     return myAlternatives.back()->getFirst();
     471              : }
     472              : 
     473              : 
     474              : const ROEdge*
     475        93458 : RORouteDef::getDestination() const {
     476        93458 :     return myAlternatives.back()->getLast();
     477              : }
     478              : 
     479              : 
     480              : OutputDevice&
     481       311259 : RORouteDef::writeXMLDefinition(OutputDevice& dev, const ROVehicle* const veh,
     482              :                                bool asAlternatives, bool withExitTimes, bool withCost, bool withLength) const {
     483       311259 :     if (asAlternatives) {
     484       112917 :         dev.openTag(SUMO_TAG_ROUTE_DISTRIBUTION).writeAttr(SUMO_ATTR_LAST, myLastUsed);
     485       383253 :         for (int i = 0; i != (int)myAlternatives.size(); i++) {
     486       540672 :             myAlternatives[i]->writeXMLDefinition(dev, veh, true, true, withExitTimes, withLength);
     487              :         }
     488       112917 :         dev.closeTag();
     489       112917 :         return dev;
     490              :     } else {
     491       396684 :         return myAlternatives[myLastUsed]->writeXMLDefinition(dev, veh, withCost, false, withExitTimes, withLength);
     492              :     }
     493              : }
     494              : 
     495              : 
     496              : RORouteDef*
     497        19965 : RORouteDef::copy(const std::string& id, const SUMOTime stopOffset) const {
     498        19965 :     RORouteDef* result = new RORouteDef(id, 0, myTryRepair, myMayBeDisconnected);
     499        40810 :     for (const RORoute* const route : myAlternatives) {
     500        20845 :         RGBColor* col = route->getColor() != nullptr ? new RGBColor(*route->getColor()) : nullptr;
     501        20845 :         RORoute* newRoute = new RORoute(id, route->getCosts(), route->getProbability(), route->getEdgeVector(), col, route->getStops());
     502              :         newRoute->addStopOffset(stopOffset);
     503        20845 :         result->addLoadedAlternative(newRoute);
     504              :     }
     505        19965 :     return result;
     506              : }
     507              : 
     508              : 
     509              : double
     510        91101 : RORouteDef::getOverallProb() const {
     511              :     double sum = 0.;
     512       323700 :     for (std::vector<RORoute*>::const_iterator i = myAlternatives.begin(); i != myAlternatives.end(); i++) {
     513       232599 :         sum += (*i)->getProbability();
     514              :     }
     515        91101 :     return sum;
     516              : }
     517              : 
     518              : 
     519              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1