LCOV - code coverage report
Current view: top level - src/router - RORouteDef.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 98.8 % 240 237
Test Date: 2026-09-23 15:43:02 Functions: 100.0 % 13 13

            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       308839 : RORouteDef::RORouteDef(const std::string& id, const int lastUsed,
      52       308839 :                        const bool tryRepair, const bool mayBeDisconnected) :
      53       308839 :     Named(StringUtils::convertUmlaute(id)),
      54       308839 :     myPrecomputed(nullptr), myLastUsed(lastUsed), myTryRepair(tryRepair),
      55       308839 :     myMayBeDisconnected(mayBeDisconnected),
      56       308839 :     myDiscardSilent(false) {
      57       308839 : }
      58              : 
      59              : 
      60              : void
      61            5 : RORouteDef::addAlternativeDef(const RORouteDef* alt) {
      62              :     std::copy(alt->myAlternatives.begin(), alt->myAlternatives.end(),
      63            5 :               back_inserter(myAlternatives));
      64            5 : }
      65              : 
      66              : 
      67              : std::shared_ptr<RORoute>
      68       302855 : RORouteDef::buildCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
      69              :                               SUMOTime begin, const ROVehicle& veh) const {
      70       302855 :     if (myPrecomputed == nullptr) {
      71       302855 :         preComputeCurrentRoute(router, begin, veh);
      72              :     }
      73       302855 :     return myPrecomputed;
      74              : }
      75              : 
      76              : 
      77              : void
      78       302861 : RORouteDef::validateAlternatives(const ROVehicle* veh, MsgHandler* errorHandler) {
      79       748895 :     for (int i = 0; i < (int)myAlternatives.size();) {
      80       446034 :         if ((i != myLastUsed || mySkipNewRoutes) && !myAlternatives[i]->isPermitted(veh, errorHandler)) {
      81           10 :             myAlternatives.erase(myAlternatives.begin() + i);
      82           10 :             if (myLastUsed > i) {
      83            0 :                 myLastUsed--;
      84              :             }
      85              :         } else {
      86       446024 :             i++;
      87              :         }
      88              :     }
      89       302861 : }
      90              : 
      91              : 
      92              : void
      93       302855 : RORouteDef::preComputeCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
      94              :                                    SUMOTime begin, const ROVehicle& veh) const {
      95       302855 :     myNewRoute = false;
      96       302855 :     const OptionsCont& oc = OptionsCont::getOptions();
      97       302855 :     const bool ignoreErrors = oc.getBool("ignore-errors");
      98       302855 :     const bool hasRestrictions = RONet::getInstance()->hasParamRestrictions();
      99              :     assert(myAlternatives[0]->getEdgeVector().size() > 0);
     100       302855 :     MsgHandler* mh = ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance();
     101       302947 :     if (myAlternatives[0]->getFirst()->prohibits(&veh, hasRestrictions) && (!oc.getBool("repair.from")
     102              :             // do not try to reassign starting edge for trip input
     103           12 :             || myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
     104           76 :         mh->informf(TL("Vehicle '%' is not allowed to depart on edge '%'."), veh.getID(), myAlternatives[0]->getFirst()->getID());
     105           38 :         return;
     106       302871 :     } else if (myAlternatives[0]->getLast()->prohibits(&veh, hasRestrictions) && (!oc.getBool("repair.to")
     107              :                // do not try to reassign destination edge for trip input
     108           12 :                || myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
     109              :         // this check is not strictly necessary unless myTryRepair is set.
     110              :         // However, the error message is more helpful than "no connection found"
     111           38 :         mh->informf(TL("Vehicle '%' is not allowed to arrive on edge '%'."), veh.getID(), myAlternatives[0]->getLast()->getID());
     112           19 :         return;
     113              :     }
     114       993938 :     const bool skipTripRouting = (oc.exists("write-trips") && oc.getBool("write-trips")
     115       394533 :                                   && RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation());
     116       302798 :     if ((myTryRepair && !skipTripRouting) || myUsingJTRR) {
     117              :         ConstROEdgeVector newEdges;
     118       205436 :         if (repairCurrentRoute(router, begin, veh, myAlternatives[0]->getEdgeVector(), newEdges)) {
     119       188091 :             if (myAlternatives[0]->getEdgeVector() != newEdges) {
     120       159685 :                 if (!myMayBeDisconnected) {
     121          258 :                     WRITE_WARNINGF(TL("Repaired route of vehicle '%'."), veh.getID());
     122              :                 }
     123       159685 :                 myNewRoute = true;
     124       159685 :                 RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
     125       319370 :                 myPrecomputed = std::make_shared<RORoute>(myID, 0, myAlternatives[0]->getProbability(), newEdges, col, myAlternatives[0]->getStops());
     126              :             } else {
     127              :                 myPrecomputed = myAlternatives[0];
     128              :             }
     129              :         }
     130              :         return;
     131       205436 :     }
     132        97362 :     if (RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation()
     133       193582 :             || OptionsCont::getOptions().getBool("remove-loops")) {
     134         1212 :         if (skipTripRouting || myAlternatives[myLastUsed]->isValid(veh, ignoreErrors)) {
     135         1200 :             myPrecomputed = myAlternatives[myLastUsed];
     136              :         }
     137              :     } else {
     138              :         // build a new route to test whether it is better
     139        96150 :         ConstROEdgeVector oldEdges({getOrigin(), getDestination()});
     140              :         ConstROEdgeVector edges;
     141        96150 :         if (repairCurrentRoute(router, begin, veh, oldEdges, edges, true)) {
     142        96144 :             if (edges.front()->isTazConnector()) {
     143              :                 edges.erase(edges.begin());
     144              :             }
     145        96144 :             if (edges.back()->isTazConnector()) {
     146              :                 edges.pop_back();
     147              :             }
     148              :             // check whether the same route was already used
     149              :             int existing = -1;
     150       138546 :             for (int i = 0; i < (int)myAlternatives.size(); i++) {
     151       122874 :                 if (edges == myAlternatives[i]->getEdgeVector()) {
     152              :                     existing = i;
     153              :                     break;
     154              :                 }
     155              :             }
     156        96144 :             if (existing >= 0) {
     157        80472 :                 myPrecomputed = myAlternatives[existing];
     158              :             } else {
     159        15672 :                 RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
     160        15672 :                 myPrecomputed = std::make_shared<RORoute>(myID, 0, 1, edges, col, myAlternatives[0]->getStops());
     161        15672 :                 myNewRoute = true;
     162              :             }
     163              :         }
     164        96150 :     }
     165              : }
     166              : 
     167              : 
     168              : bool
     169       301586 : RORouteDef::repairCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
     170              :                                SUMOTime begin, const ROVehicle& veh,
     171              :                                ConstROEdgeVector oldEdges, ConstROEdgeVector& newEdges,
     172              :                                bool isTrip) const {
     173       301586 :     MsgHandler* mh = (OptionsCont::getOptions().getBool("ignore-errors") ?
     174       301586 :                       MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance());
     175       301586 :     RONet* net = RONet::getInstance();
     176       301586 :     const int initialSize = (int)oldEdges.size();
     177       301586 :     const bool hasRestrictions = RONet::getInstance()->hasParamRestrictions();
     178           50 :     if (net->hasProhibitions() && router.supportsProhibitions()) {
     179           30 :         if (net->getProhibitions().size() > 0 && !router.hasProhibitions()) {
     180            7 :             router.prohibit(net->getProhibitions());
     181              :         }
     182           30 :         net->updateLaneProhibitions(begin);
     183              :     }
     184       301586 :     if (initialSize == 1) {
     185         6415 :         if (myUsingJTRR) {
     186              :             /// only ROJTRRouter is supposed to handle this type of input
     187         6134 :             bool ok = router.compute(oldEdges.front(), nullptr, &veh, begin, newEdges);
     188        10343 :             myDiscardSilent = ok && newEdges.size() == 0;
     189              :         } else {
     190          281 :             newEdges = oldEdges;
     191              :         }
     192              :     } else {
     193       295171 :         if (oldEdges.front()->prohibits(&veh, hasRestrictions)) {
     194              :             // option repair.from is in effect
     195              :             const std::string& frontID = oldEdges.front()->getID();
     196           24 :             for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
     197           20 :                 if ((*i)->prohibits(&veh, hasRestrictions) || (*i)->isInternal()) {
     198              :                     i = oldEdges.erase(i);
     199              :                 } else {
     200            8 :                     WRITE_MESSAGE("Changing invalid starting edge '" + frontID
     201              :                                   + "' to '" + (*i)->getID() + "' for vehicle '" + veh.getID() + "'.");
     202            4 :                     break;
     203              :                 }
     204              :             }
     205              :         }
     206       295171 :         if (oldEdges.size() == 0) {
     207            8 :             mh->informf(TL("Could not find new starting edge for vehicle '%'."), veh.getID());
     208        17351 :             return false;
     209              :         }
     210       295167 :         if (oldEdges.back()->prohibits(&veh, hasRestrictions)) {
     211              :             // option repair.to is in effect
     212              :             const std::string& backID = oldEdges.back()->getID();
     213              :             // oldEdges cannot get empty here, otherwise we would have left the stage when checking "from"
     214            8 :             while (oldEdges.back()->prohibits(&veh, hasRestrictions) || oldEdges.back()->isInternal()) {
     215              :                 oldEdges.pop_back();
     216              :             }
     217           12 :             WRITE_MESSAGE("Changing invalid destination edge '" + backID
     218              :                           + "' to edge '" + oldEdges.back()->getID() + "' for vehicle '" + veh.getID() + "'.");
     219              :         }
     220       295167 :         std::vector<ROVehicle::Mandatory> mandatory = veh.getMandatoryEdges(oldEdges.front(), oldEdges.back());
     221              :         assert(mandatory.size() >= 2);
     222              :         // removed prohibited
     223       919534 :         for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
     224       624367 :             if ((*i)->prohibits(&veh, hasRestrictions) || (*i)->isInternal()) {
     225              :                 // no need to check the mandatories here, this was done before
     226          188 :                 WRITE_MESSAGEF(TL("Removing invalid edge '%' from route for vehicle '%'."), (*i)->getID(), veh.getID());
     227              :                 i = oldEdges.erase(i);
     228              :             } else {
     229              :                 ++i;
     230              :             }
     231              :         }
     232              :         // reconnect remaining edges
     233       295167 :         if (mandatory.size() > oldEdges.size() && initialSize > 2) {
     234          525 :             WRITE_MESSAGEF(TL("There are stop edges which were not part of the original route for vehicle '%'."), veh.getID());
     235              :         }
     236              :         ConstROEdgeVector targets;
     237              :         bool checkPositions = false;
     238       295167 :         if (mandatory.size() >= oldEdges.size()) {
     239       919823 :             for (auto m : mandatory) {
     240       624733 :                 targets.push_back(m.edge);
     241       624733 :                 checkPositions |= m.pos >= 0;
     242              :             }
     243              :         } else {
     244           77 :             targets = oldEdges;
     245              :         }
     246       295167 :         newEdges.push_back(targets.front());
     247              :         auto nextMandatory = mandatory.begin() + 1;
     248       295167 :         while (nextMandatory != mandatory.end()
     249       297695 :                 && targets.front() == nextMandatory->edge
     250       332548 :                 && (nextMandatory->edge != (nextMandatory - 1)->edge
     251        18694 :                     || nextMandatory->pos >= (nextMandatory - 1)->pos
     252              :                     // ignore invalid via stop pos
     253        18036 :                     || nextMandatory->pos < 0)) {
     254              :             nextMandatory++;
     255              :         }
     256              :         int lastMandatory = 0;
     257       295167 :         for (ConstROEdgeVector::const_iterator i = targets.begin() + 1;
     258       589412 :                 i != targets.end() && nextMandatory != mandatory.end(); ++i) {
     259       311587 :             const ROEdge* prev = *(i - 1);
     260       311587 :             const ROEdge* cur = *i;
     261       354565 :             if (prev->isConnectedTo(*cur, veh.getVClass()) && (!isRailway(veh.getVClass()) || prev->getBidiEdge() != cur)) {
     262        42973 :                 newEdges.push_back(cur);
     263              :             } else {
     264       268614 :                 if (initialSize > 2) {
     265              :                     // only inform if the input is (probably) not a trip
     266       166350 :                     WRITE_MESSAGEF(TL("Edge '%' not connected to edge '%' for vehicle '%'."), (*(i - 1))->getID(), (*i)->getID(), veh.getID());
     267              :                 }
     268       268614 :                 const ROEdge* last = newEdges.back();
     269              :                 newEdges.pop_back();
     270       268614 :                 if (last->isTazConnector() && newEdges.size() > 1) {
     271              :                     // assume this was a viaTaz
     272           35 :                     last = newEdges.back();
     273              :                     newEdges.pop_back();
     274              :                 }
     275       268614 :                 if (veh.hasJumps() && (nextMandatory - 1)->isJump) {
     276           40 :                     while (*i != nextMandatory->edge) {
     277              :                         ++i;
     278              :                     }
     279           30 :                     newEdges.push_back(last);
     280           30 :                     newEdges.push_back(*i);
     281              :                     //std::cout << " skipJump mIndex=" << (nextMandatory - 1 - mandatory.begin()) << " last=" << last->getID() << " next=" << (*i)->getID() << " newEdges=" << toString(newEdges) << "\n";
     282              :                 } else {
     283       268584 :                     int numEdgesBefore = (int)newEdges.size();
     284              :                     //                router.setHint(targets.begin(), i, &veh, begin);
     285       268584 :                     if (myTryRepair && lastMandatory < (int)newEdges.size() && last != newEdges[lastMandatory]) {
     286           36 :                         router.setMsgHandler(MsgHandler::getWarningInstance());
     287              :                     }
     288              :                     bool ok;
     289              :                     if (checkPositions
     290       268531 :                             && mandatory[i - targets.begin() - 1].pos >= 0
     291       517425 :                             && mandatory[i - targets.begin()].pos >= 0) {
     292          712 :                         ok = router.compute(
     293              :                                  last, mandatory[i - targets.begin() - 1].pos,
     294              :                                  *i, mandatory[i - targets.begin()].pos,
     295              :                                  &veh, begin, newEdges);
     296              :                     } else {
     297       267872 :                         ok = router.compute(last, *i, &veh, begin, newEdges);
     298              :                     }
     299       268584 :                     router.setMsgHandler(mh);
     300       268584 :                     if (!ok) {
     301              :                         // backtrack: try to route from last mandatory edge to next mandatory edge
     302              :                         // XXX add option for backtracking in smaller increments
     303              :                         // (i.e. previous edge to edge after *i)
     304              :                         // we would then need to decide whether we have found a good
     305              :                         // tradeoff between faithfulness to the input data and detour-length
     306        17354 :                         if (lastMandatory >= (int)newEdges.size() || last == newEdges[lastMandatory] || !backTrack(router, i, lastMandatory, nextMandatory->edge, newEdges, veh, begin)) {
     307        52026 :                             mh->informf(TL("Mandatory edge '%' not reachable by vehicle '%'."), (*i)->getID(), veh.getID());
     308        17342 :                             return false;
     309              :                         }
     310       251230 :                     } else if (!myMayBeDisconnected && !isTrip && last != (*i)) {
     311           62 :                         double airDist = last->getToJunction()->getPosition().distanceTo(
     312              :                                              (*i)->getFromJunction()->getPosition());
     313              :                         double repairDist = 0;
     314          154 :                         for (auto it2 = (newEdges.begin() + numEdgesBefore + 1); it2 != newEdges.end() && it2 != newEdges.end() - 1; it2++) {
     315           92 :                             repairDist += (*it2)->getLength();
     316              :                         }
     317           62 :                         const double detourFactor = repairDist / MAX2(airDist, 1.0);
     318           62 :                         const double detour = MAX2(0.0, repairDist - airDist);
     319           62 :                         const double maxDetourFactor = OptionsCont::getOptions().getFloat("repair.max-detour-factor");
     320           62 :                         if (detourFactor > maxDetourFactor) {
     321            4 :                             WRITE_MESSAGEF("    Backtracking to avoid detour of %m for gap of %m)", detour, airDist);
     322            4 :                             backTrack(router, i, lastMandatory, nextMandatory->edge, newEdges, veh, begin);
     323           58 :                         } else if (detourFactor > 1.1) {
     324           18 :                             WRITE_MESSAGEF("    Taking detour of %m to avoid gap of %m)", detour, airDist);
     325              :                         }
     326              :                     }
     327              :                 }
     328              :             }
     329              :             while (nextMandatory != mandatory.end()
     330       326223 :                     && *i == nextMandatory->edge
     331       881526 :                     && (nextMandatory->edge != (nextMandatory - 1)->edge
     332         5003 :                         || nextMandatory->pos >= (nextMandatory - 1)->pos
     333              :                         // ignore invalid via stop pos
     334           74 :                         || nextMandatory->pos < 0)) {
     335              :                 nextMandatory++;
     336       293632 :                 lastMandatory = (int)newEdges.size() - 1;
     337              :             }
     338              :         }
     339       277825 :         if (veh.getParameter().via.size() > 0 && veh.getParameter().stops.size() > 0) {
     340              :             // check consistency of stops and vias
     341              :             auto it = newEdges.begin();
     342           15 :             for (const auto& stop : veh.getParameter().stops) {
     343           20 :                 const ROEdge* e = net->getEdge(stop.edge);
     344           10 :                 it = std::find(it, newEdges.end(), e);
     345           10 :                 if (it == newEdges.end()) {
     346           10 :                     mh->informf(TL("Stop edge '%' is inconsistent with via edges for vehicle '%'."), e->getID(), veh.getID());
     347            5 :                     return false;
     348              :                 }
     349              :             }
     350              :         }
     351       295167 :     }
     352              :     return true;
     353              : }
     354              : 
     355              : 
     356              : bool
     357           16 : RORouteDef::backTrack(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
     358              :                       ConstROEdgeVector::const_iterator& i, int lastMandatory, const ROEdge* nextMandatory,
     359              :                       ConstROEdgeVector& newEdges, const ROVehicle& veh, SUMOTime begin) {
     360              :     ConstROEdgeVector edges;
     361           16 :     bool ok = router.compute(newEdges[lastMandatory], nextMandatory, &veh, begin, edges, true);
     362           16 :     if (!ok) {
     363              :         return false;
     364              :     }
     365              : 
     366           28 :     while (*i != nextMandatory) {
     367              :         ++i;
     368              :     }
     369              :     newEdges.erase(newEdges.begin() + lastMandatory + 1, newEdges.end());
     370              :     std::copy(edges.begin() + 1, edges.end(), back_inserter(newEdges));
     371              :     return true;
     372           16 : }
     373              : 
     374              : 
     375              : std::shared_ptr<RORoute>
     376       283500 : RORouteDef::addAlternative(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
     377              :                            const ROVehicle* const veh, std::shared_ptr<RORoute> current, SUMOTime begin,
     378              :                            MsgHandler* errorHandler) {
     379              :     std::shared_ptr<RORoute> replaced = nullptr;
     380       283500 :     if (myTryRepair || myUsingJTRR) {
     381       186166 :         if (myNewRoute) {
     382              :             replaced = myAlternatives[0];
     383              :             myAlternatives[0] = current;
     384              :         }
     385       372332 :         if (!router.isValid(current->getEdgeVector(), veh, STEPS2TIME(begin))) {
     386            0 :             throw ProcessError("Route '" + getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
     387              :         }
     388       186166 :         double costs = router.recomputeCosts(current->getEdgeVector(), veh, begin);
     389       186166 :         if (veh->hasJumps()) {
     390              :             // @todo: jumpTime should be applied in recomputeCost to ensure the
     391              :             // correctness of time-dependent traveltimes
     392           25 :             costs += STEPS2TIME(veh->getJumpTime());
     393              :         }
     394       186166 :         current->setCosts(costs);
     395              :         return replaced;
     396              :     }
     397              :     // add the route when it's new
     398        97334 :     if (myAlternatives.back()->getProbability() < 0 || !myAlternatives.back()->isPermitted(veh, errorHandler)) {
     399          214 :         if (myAlternatives.back()->getProbability() >= 0 && errorHandler == MsgHandler::getErrorInstance()) {
     400           60 :             throw ProcessError("Route '" + current->getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
     401              :         }
     402              :         myAlternatives.pop_back();
     403              :     }
     404        97314 :     if (myNewRoute) {
     405        15652 :         myAlternatives.push_back(current);
     406              :     }
     407              :     // recompute the costs and (when a new route was added) scale the probabilities
     408        97314 :     const double scale = double(myAlternatives.size() - 1) / double(myAlternatives.size());
     409       353251 :     for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
     410       511874 :         if (!router.isValid(alt->getEdgeVector(), veh, STEPS2TIME(begin))) {
     411            0 :             throw ProcessError("Route '" + current->getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
     412              :         }
     413              :         // recompute the costs for all routes
     414       255937 :         const double newCosts = router.recomputeCosts(alt->getEdgeVector(), veh, begin);
     415              :         assert(myAlternatives.size() != 0);
     416       255937 :         if (myNewRoute) {
     417        37791 :             if (alt == current) {
     418              :                 // set initial probability and costs
     419        15652 :                 alt->setProbability(1. / (double) myAlternatives.size());
     420        15652 :                 alt->setCosts(newCosts);
     421              :             } else {
     422              :                 // rescale probs for all others
     423        22139 :                 alt->setProbability(alt->getProbability() * scale);
     424              :             }
     425              :         }
     426       511894 :         RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().setCosts(alt, newCosts, alt == myAlternatives[myLastUsed]);
     427              :     }
     428              :     assert(myAlternatives.size() != 0);
     429        97314 :     RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().calculateProbabilities(myAlternatives, veh, veh->getDepartureTime());
     430        97314 :     const bool keepRoute = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepRoute();
     431        97314 :     if (!RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepAllRoutes() && !keepRoute) {
     432              :         // remove with probability of 0 (not mentioned in Gawron)
     433       337857 :         for (std::vector<std::shared_ptr<RORoute>>::iterator i = myAlternatives.begin(); i != myAlternatives.end();) {
     434       244741 :             if ((*i)->getProbability() == 0) {
     435              :                 i = myAlternatives.erase(i);
     436              :             } else {
     437              :                 i++;
     438              :             }
     439              :         }
     440              :     }
     441        97314 :     int maxNumber = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().getMaxRouteNumber();
     442        97314 :     if ((int)myAlternatives.size() > maxNumber) {
     443           61 :         std::shared_ptr<const RORoute> last = myAlternatives[myLastUsed];
     444              :         // only keep the routes with highest probability
     445           61 :         sort(myAlternatives.begin(), myAlternatives.end(), [](const std::shared_ptr<const RORoute> a, const std::shared_ptr<const RORoute> b) {
     446           79 :             return a->getProbability() > b->getProbability();
     447              :         });
     448           61 :         if (keepRoute) {
     449           24 :             for (int i = 0; i < (int)myAlternatives.size(); i++) {
     450           24 :                 if (myAlternatives[i] == last) {
     451           16 :                     myLastUsed = i;
     452           16 :                     break;
     453              :                 }
     454              :             }
     455           16 :             if (myLastUsed >= maxNumber) {
     456            8 :                 std::swap(myAlternatives[maxNumber - 1], myAlternatives[myLastUsed]);
     457            8 :                 myLastUsed = maxNumber - 1;
     458              :             }
     459              :         }
     460              :         myAlternatives.erase(myAlternatives.begin() + maxNumber, myAlternatives.end());
     461              :     }
     462              :     // rescale probabilities
     463              :     double newSum = 0.;
     464       353190 :     for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
     465       255876 :         newSum += alt->getProbability();
     466              :     }
     467              :     assert(newSum > 0);
     468              :     // @note newSum may be larger than 1 for numerical reasons
     469       353190 :     for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
     470       255876 :         alt->setProbability(alt->getProbability() / newSum);
     471              :     }
     472              : 
     473              :     // find the route to use
     474        97314 :     if (!keepRoute) {
     475        93116 :         double chosen = RandHelper::rand();
     476        93116 :         myLastUsed = 0;
     477       159461 :         for (const std::shared_ptr<RORoute>& alt : myAlternatives) {
     478       159461 :             chosen -= alt->getProbability();
     479       159461 :             if (chosen <= 0) {
     480              :                 return nullptr;
     481              :             }
     482        66345 :             myLastUsed++;
     483              :         }
     484              :     }
     485              :     return nullptr;
     486              : }
     487              : 
     488              : 
     489              : const ROEdge*
     490        96150 : RORouteDef::getOrigin() const {
     491        96150 :     return myAlternatives.back()->getFirst();
     492              : }
     493              : 
     494              : 
     495              : const ROEdge*
     496        96150 : RORouteDef::getDestination() const {
     497        96150 :     return myAlternatives.back()->getLast();
     498              : }
     499              : 
     500              : 
     501              : OutputDevice&
     502       321818 : RORouteDef::writeXMLDefinition(OutputDevice& dev, const ROVehicle* const veh,
     503              :                                bool asAlternatives, bool withExitTimes, bool withCost, bool withLength) const {
     504       321818 :     if (asAlternatives) {
     505       117553 :         dev.openTag(SUMO_TAG_ROUTE_DISTRIBUTION).writeAttr(SUMO_ATTR_LAST, myLastUsed);
     506       393672 :         for (int i = 0; i != (int)myAlternatives.size(); i++) {
     507       552238 :             myAlternatives[i]->writeXMLDefinition(dev, veh, true, true, withExitTimes, withLength);
     508              :         }
     509       117553 :         dev.closeTag();
     510       117553 :         return dev;
     511              :     } else {
     512       408530 :         return myAlternatives[myLastUsed]->writeXMLDefinition(dev, veh, withCost, false, withExitTimes, withLength);
     513              :     }
     514              : }
     515              : 
     516              : 
     517              : RORouteDef*
     518        22752 : RORouteDef::copy(const std::string& id, const SUMOTime stopOffset) const {
     519        22752 :     RORouteDef* result = new RORouteDef(id, 0, myTryRepair, myMayBeDisconnected);
     520        46604 :     for (const std::shared_ptr<const RORoute> route : myAlternatives) {
     521        23852 :         RGBColor* col = route->getColor() != nullptr ? new RGBColor(*route->getColor()) : nullptr;
     522        23852 :         std::shared_ptr<RORoute> newRoute = std::make_shared<RORoute>(id, route->getCosts(), route->getProbability(), route->getEdgeVector(), col, route->getStops());
     523              :         newRoute->addStopOffset(stopOffset);
     524        23852 :         result->addLoadedAlternative(newRoute);
     525              :     }
     526        22752 :     return result;
     527              : }
     528              : 
     529              : 
     530              : double
     531        92000 : RORouteDef::getOverallProb() const {
     532              :     double sum = 0.;
     533       325898 :     for (const std::shared_ptr<const RORoute> r : myAlternatives) {
     534       233898 :         sum += r->getProbability();
     535              :     }
     536        92000 :     return sum;
     537              : }
     538              : 
     539              : 
     540              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1