LCOV - code coverage report
Current view: top level - src/utils/router - Node2EdgeRouter.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 0.0 % 90 0
Test Date: 2025-05-18 15:30:03 Functions: 0.0 % 9 0

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2012-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    Node2EdgeRouter.h
      15              : /// @author  Ruediger Ebendt
      16              : /// @date    01.01.2024
      17              : ///
      18              : // Simple extension of edge-based AStarRouter, adding a) a new method computeNode2Edge,
      19              : // which computes a shortest path starting at a given node and ending at a given edge,
      20              : // and b) a new method computeNode2Edges, which computes shortest paths, each starting
      21              : // at the given node and ending at one of the given edges
      22              : /****************************************************************************/
      23              : #pragma once
      24              : #include <config.h>
      25              : #include <unordered_set>
      26              : #include <utils/common/StdDefs.h>
      27              : #include "AStarRouter.h"
      28              : 
      29              : // ===========================================================================
      30              : // class definitions
      31              : // ===========================================================================
      32              : /**
      33              :  * @class Node2EdgeRouter
      34              :  * Computes shortest paths from a node to one edge (using A*) or to all edges
      35              :  * (using Dijkstra's algorithm)
      36              :  *
      37              :  * The template parameters are:
      38              :  * @param E The edge class to use (MSEdge/ROEdge)
      39              :  * @param V The vehicle class to use (MSVehicle/ROVehicle)
      40              :  *
      41              :  * The router is edge-based. It must know the number of edges for internal reasons
      42              :  * and whether a missing connection between two given edges (unbuild route) shall
      43              :  * be reported as an error or as a warning
      44              :  *
      45              :  */
      46              : template<class E, class N, class V, class M>
      47              : class Node2EdgeRouter : public AStarRouter<E, V, M> {
      48              : public:
      49              :     typedef AbstractLookupTable<E, V> LookupTable;
      50              : 
      51              :     /** @brief Returns the edge information for the passed edge
      52              :      * @param[in] edge The edge
      53              :      * @note Non-const version
      54              :      */
      55              :     typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo(const E* const edge) {
      56            0 :         return &(this->myEdgeInfos[edge->getNumericalID()]);
      57              :     }
      58              :     /** @brief Returns the edge information for the passed edge
      59              :      * @param[in] edge The edge
      60              :      * @note Const version
      61              :      */
      62              :     const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo(const E* const edge) const {
      63              :         return &(this->myEdgeInfos[edge->getNumericalID()]);
      64              :     }
      65              : 
      66              :     /** @brief Constructor
      67              :      * @param[in] edges The edges
      68              :      * @param[in] unbuildIsWarning The flag indicating whether network unbuilds should issue warnings or errors
      69              :      * @param[in] operation The operation
      70              :      * @param[in] lookup The lookup table
      71              :      * @param[in] havePermissions The flag indicating whether to respect edge permissions
      72              :      * @param[in] haveRestrictions The flag indicating whether to respect edge restrictions
      73              :      */
      74            0 :     Node2EdgeRouter(const std::vector<E*>& edges, bool unbuildIsWarning, typename SUMOAbstractRouter<E, V>::Operation operation,
      75              :                     const std::shared_ptr<const LookupTable> lookup = nullptr,
      76              :                     const bool havePermissions = false, const bool haveRestrictions = false) :
      77            0 :         AStarRouter<E, V, M>(edges, unbuildIsWarning, operation, lookup, havePermissions, haveRestrictions) {
      78            0 :     }
      79              : 
      80              :     /** @brief Cloning constructor
      81              :      * @param[in] edgeInfos The vector of edge information
      82              :      * @param[in] unbuildIsWarning The flag indicating whether network unbuilds should issue warnings or errors
      83              :      * @param[in] operation The operation
      84              :      * @param[in] lookup The lookup table
      85              :      * @param[in] havePermissions The flag indicating whether to respect edge permissions
      86              :      * @param[in] haveRestrictions The flag indicating whether to respect edge restrictions
      87              :      */
      88            0 :     Node2EdgeRouter(const std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo>& edgeInfos, bool unbuildIsWarning, typename SUMOAbstractRouter<E, V>::Operation operation, const std::shared_ptr<const LookupTable> lookup = nullptr,
      89              :                     const bool havePermissions = false, const bool haveRestrictions = false) :
      90            0 :         AStarRouter<E, V, M>(edgeInfos, unbuildIsWarning, operation, lookup, havePermissions, haveRestrictions) {
      91            0 :     }
      92              : 
      93              :     /// @brief Destructor
      94            0 :     virtual ~Node2EdgeRouter() {
      95            0 :         WRITE_MESSAGE("The following stats for AStarRouter (which might also be empty) belong to Node2EdgeRouter, a derived class:");
      96            0 :     }
      97              : 
      98              :     /// @brief Cloning method
      99            0 :     virtual SUMOAbstractRouter<E, V>* clone() {
     100            0 :         return new Node2EdgeRouter<E, N, V, M>(this->myEdgeInfos, this->myErrorMsgHandler == MsgHandler::getWarningInstance(), this->myOperation, this->myLookupTable,
     101            0 :                                                this->myHavePermissions, this->myHaveRestrictions);
     102              :     }
     103              : 
     104              :     /** @brief Reset method
     105              :      * @param[in] vehicle The vehicle
     106              :      */
     107            0 :     virtual void reset(const V* const vehicle) {
     108              :         UNUSED_PARAMETER(vehicle);
     109            0 :         for (auto& edgeInfo : this->myFrontierList) {
     110            0 :             edgeInfo->reset();
     111              :         }
     112              :         this->myFrontierList.clear();
     113            0 :         for (auto& edgeInfo : this->myFound) {
     114            0 :             edgeInfo->reset();
     115              :         }
     116              :         this->myFound.clear();
     117            0 :     }
     118              : 
     119              :     /** @brief Builds the routes between the given node and all given edges using the minimum travel time
     120              :      * @param[in] fromNode The node which the routes start with
     121              :      * @param[in] toEdges The edges at which the routes end
     122              :      * @param[in] vehicle The vehicle
     123              :      * @param[in] msTime The start time of the routes in milliseconds
     124              :      * @param[in] silent The boolean flag indicating whether the method stays silent or puts out messages
     125              :      */
     126            0 :     bool computeNode2Edges(const N* fromNode, const std::unordered_set<const E*>& toEdges, const V* const vehicle,
     127              :                            SUMOTime msTime, bool silent = false) {
     128              :         assert(fromNode != nullptr);
     129              :         // check whether fromNode and to can be used
     130              :         bool found = false;
     131              :         bool allVisited = true; // we try to disprove this
     132            0 :         for (const E* from : fromNode->getOutgoing()) {
     133            0 :             if (from->isInternal()) {
     134            0 :                 continue;
     135              :             }
     136            0 :             if (this->myEdgeInfos[from->getNumericalID()].prohibited || this->isProhibited(from, vehicle)) {
     137            0 :                 if (!silent) {
     138            0 :                     this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + from->getID() + "'.");
     139              :                 }
     140              :             } else {
     141              :                 found = true;
     142              :                 break;
     143              :             }
     144              :         }
     145            0 :         if (!found) {
     146              :             return false;
     147              :         }
     148            0 :         const std::vector<const E*>& fromEdges = fromNode->getOutgoing();
     149              : 
     150            0 :         if (fromEdges.empty() || toEdges.empty()) { // nothing to do here
     151              :             return false;
     152              :         }
     153              : 
     154            0 :         double length = 0.; // dummy for the via edge cost update
     155              :         this->startQuery();
     156              : #ifdef ASTAR_DEBUG_QUERY
     157              :         if (ASTAR_DEBUG_COND) {
     158              :             std::cout << "DEBUG: starting search for '" << Named::getIDSecure(vehicle) << "' speed: " << MIN2(vehicle->getMaxSpeed(), myMaxSpeed * vehicle->getChosenSpeedFactor()) << " time: " << STEPS2TIME(msTime) << "\n";
     159              :         }
     160              : #endif
     161              : 
     162            0 :         const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
     163            0 :         init(fromEdges, vehicle, msTime);
     164            0 :         this->myAmClean = false;
     165              :         // loop
     166              :         int num_visited = 0;
     167            0 :         while (!this->myFrontierList.empty()) {
     168            0 :             num_visited += 1;
     169              :             // use the edge with the minimal length
     170            0 :             auto* const minimumInfo = this->myFrontierList.front();
     171            0 :             const E* const minEdge = minimumInfo->edge;
     172              :             // check whether the destination edge was already reached
     173              :             if (toEdges.count(minEdge)) {
     174            0 :                 for (const E* to : toEdges) {
     175            0 :                     const auto& toInfo = this->myEdgeInfos[to->getNumericalID()];
     176            0 :                     if (!toInfo.visited) {
     177              :                         allVisited = false;
     178              :                         break;
     179              :                     }
     180              :                 }
     181            0 :                 if (allVisited) {
     182              :                     this->endQuery(num_visited);
     183            0 :                     return true;
     184              :                 }
     185              :             }
     186            0 :             std::pop_heap(this->myFrontierList.begin(), this->myFrontierList.end(), this->myComparator);
     187              :             this->myFrontierList.pop_back();
     188            0 :             this->myFound.push_back(minimumInfo);
     189            0 :             minimumInfo->visited = true;
     190            0 :             const double effortDelta = this->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
     191            0 :             const double leaveTime = minimumInfo->leaveTime + this->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
     192              : 
     193              :             const double heuristic_remaining = 0;
     194              :             if (heuristic_remaining == UNREACHABLE) {
     195              :                 continue;
     196              :             }
     197            0 :             const double heuristicEffort = minimumInfo->effort + effortDelta + heuristic_remaining;
     198              :             // check all ways from the edge with the minimal length
     199            0 :             for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass)) {
     200            0 :                 auto& followerInfo = this->myEdgeInfos[follower.first->getNumericalID()];
     201              :                 // check whether it can be used
     202            0 :                 if (followerInfo.prohibited || this->isProhibited(follower.first, vehicle)) {
     203            0 :                     continue;
     204              :                 }
     205            0 :                 double effort = minimumInfo->effort + effortDelta;
     206            0 :                 double time = leaveTime;
     207            0 :                 this->updateViaEdgeCost(follower.second, vehicle, time, effort, length);
     208            0 :                 const double oldEffort = followerInfo.effort;
     209            0 :                 if ((!followerInfo.visited) && effort < oldEffort) {
     210            0 :                     followerInfo.effort = effort;
     211              :                     // if we use the effort including the via effort below we would count the via twice as shown by the ticket676 test
     212              :                     // but we should never get below the real effort, see #12463
     213            0 :                     followerInfo.heuristicEffort = MAX2(MIN2(heuristicEffort, followerInfo.heuristicEffort), effort);
     214            0 :                     followerInfo.leaveTime = time;
     215            0 :                     followerInfo.prev = minimumInfo;
     216            0 :                     if (oldEffort == std::numeric_limits<double>::max()) {
     217            0 :                         this->myFrontierList.push_back(&followerInfo);
     218              :                         std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), this->myComparator);
     219              :                     } else {
     220            0 :                         auto fi = std::find(this->myFrontierList.begin(), this->myFrontierList.end(), &followerInfo);
     221              :                         assert(fi != this->myFrontierList.end());
     222              :                         std::push_heap(this->myFrontierList.begin(), fi + 1, this->myComparator);
     223              :                     }
     224              :                 }
     225              :             }
     226              :         }
     227              :         this->endQuery(num_visited);
     228            0 :         for (const E* to : toEdges) {
     229            0 :             const auto& toInfo = this->myEdgeInfos[to->getNumericalID()];
     230            0 :             if (toInfo.visited) {
     231            0 :                 if (!silent) {
     232            0 :                     this->myErrorMsgHandler->informf("Only some connections between node '%' and the given edges were found.", fromNode->getID());
     233              :                 }
     234              :                 return true; // at least one connection could be found
     235              :             }
     236              :         }
     237            0 :         if (!silent) {
     238            0 :             this->myErrorMsgHandler->informf("No connections between node '%' and the given edges were found.", fromNode->getID());
     239              :         }
     240              :         return false;
     241              :     }
     242              : 
     243              :     /** @brief Builds the route between the given node and edge using the minimum travel time
     244              :      * @param[in] fromNode The node the route starts with
     245              :      * @param[in] to The edge at which the route ends
     246              :      * @param[in] vehicle The vehicle
     247              :      * @param[in] msTime The start time of the route in milliseconds
     248              :      * @param[out] into The vector of edges, into which the solution route is written
     249              :      * @param[in] silent The boolean flag indicating whether the method stays silent or puts out messages
     250              :      */
     251              :     bool computeNode2Edge(const N* fromNode, const E* to, const V* const vehicle,
     252              :                           SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
     253              :         assert(fromNode != nullptr && to != nullptr);
     254              :         // check whether fromNode and to can be used
     255              :         bool found = false;
     256              :         for (const E* from : fromNode->getOutgoing()) {
     257              :             if (from->isInternal()) {
     258              :                 continue;
     259              :             }
     260              :             if (this->myEdgeInfos[from->getNumericalID()].prohibited || this->isProhibited(from, vehicle)) {
     261              :                 if (!silent) {
     262              :                     this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + from->getID() + "'.");
     263              :                 }
     264              :             } else {
     265              :                 found = true;
     266              :                 break;
     267              :             }
     268              :         }
     269              :         if (!found) {
     270              :             return false;
     271              :         }
     272              :         const std::vector<const E*>& fromEdges = fromNode->getOutgoing();
     273              : 
     274              :         if (fromEdges.empty()) { // nothing to do here
     275              :             return false;
     276              :         }
     277              :         if (this->myEdgeInfos[to->getNumericalID()].prohibited || this->isProhibited(to, vehicle)) {
     278              :             if (!silent) {
     279              :                 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on destination edge '" + to->getID() + "'.");
     280              :             }
     281              :             return false;
     282              :         }
     283              :         double length = 0.; // dummy for the via edge cost update
     284              :         this->startQuery();
     285              : 
     286              :         const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
     287              :         init(fromEdges, vehicle, msTime);
     288              :         this->myAmClean = false;
     289              :         // loop
     290              :         int num_visited = 0;
     291              :         const bool mayRevisit = this->myLookupTable != nullptr && !this->myLookupTable->consistent();
     292              :         const double speed = vehicle == nullptr ? this->myMaxSpeed : MIN2(vehicle->getMaxSpeed(), this->myMaxSpeed * vehicle->getChosenSpeedFactor());
     293              :         while (!this->myFrontierList.empty()) {
     294              :             num_visited += 1;
     295              :             // use the edge with the minimal length
     296              :             auto* const minimumInfo = this->myFrontierList.front();
     297              :             const E* const minEdge = minimumInfo->edge;
     298              :             // check whether the destination edge was already reached
     299              :             if (minEdge == to) {
     300              :                 this->buildPathFrom(minimumInfo, into);
     301              :                 this->endQuery(num_visited);
     302              :                 return true;
     303              :             }
     304              :             std::pop_heap(this->myFrontierList.begin(), this->myFrontierList.end(), this->myComparator);
     305              :             this->myFrontierList.pop_back();
     306              :             this->myFound.push_back(minimumInfo);
     307              :             minimumInfo->visited = true;
     308              :             const double effortDelta = this->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
     309              :             const double leaveTime = minimumInfo->leaveTime + this->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
     310              : 
     311              :             // admissible A* heuristic: straight line distance at maximum speed
     312              :             // this is calculated from the end of minEdge so it possibly includes via efforts to the followers
     313              :             const double heuristic_remaining = (this->myLookupTable == nullptr ? minEdge->getDistanceTo(to) / speed :
     314              :                                                 this->myLookupTable->lowerBound(minEdge, to, speed, vehicle->getChosenSpeedFactor(),
     315              :                                                         minEdge->getMinimumTravelTime(nullptr), to->getMinimumTravelTime(nullptr)));
     316              :             //const double heuristic_remaining = 0.;
     317              :             if (heuristic_remaining == UNREACHABLE) {
     318              :                 continue;
     319              :             }
     320              :             const double heuristicEffort = minimumInfo->effort + effortDelta + heuristic_remaining;
     321              :             // check all ways from the edge with the minimal length
     322              :             for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass)) {
     323              :                 auto& followerInfo = this->myEdgeInfos[follower.first->getNumericalID()];
     324              :                 // check whether it can be used
     325              :                 if (followerInfo.prohibited || this->isProhibited(follower.first, vehicle)) {
     326              :                     continue;
     327              :                 }
     328              :                 double effort = minimumInfo->effort + effortDelta;
     329              :                 double time = leaveTime;
     330              :                 this->updateViaEdgeCost(follower.second, vehicle, time, effort, length);
     331              :                 const double oldEffort = followerInfo.effort;
     332              :                 if ((!followerInfo.visited || mayRevisit) && effort < oldEffort) {
     333              :                     followerInfo.effort = effort;
     334              :                     // if we use the effort including the via effort below we would count the via twice as shown by the ticket676 test
     335              :                     // but we should never get below the real effort, see #12463
     336              :                     followerInfo.heuristicEffort = MAX2(MIN2(heuristicEffort, followerInfo.heuristicEffort), effort);
     337              :                     followerInfo.leaveTime = time;
     338              :                     followerInfo.prev = minimumInfo;
     339              :                     if (oldEffort == std::numeric_limits<double>::max()) {
     340              :                         this->myFrontierList.push_back(&followerInfo);
     341              :                         std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), this->myComparator);
     342              :                     } else {
     343              :                         auto fi = std::find(this->myFrontierList.begin(), this->myFrontierList.end(), &followerInfo);
     344              :                         if (fi == this->myFrontierList.end()) {
     345              :                             assert(mayRevisit);
     346              :                             this->myFrontierList.push_back(&followerInfo);
     347              :                             std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), this->myComparator);
     348              :                         } else {
     349              :                             std::push_heap(this->myFrontierList.begin(), fi + 1, this->myComparator);
     350              :                         }
     351              :                     }
     352              :                 }
     353              :             }
     354              :         }
     355              :         this->endQuery(num_visited);
     356              :         if (!silent) {
     357              :             this->myErrorMsgHandler->informf("No connection between node '%' and edge '%' found.", fromNode->getID(), to->getID());
     358              :         }
     359              :         return false;
     360              :     }
     361              : 
     362              :     /** @brief Updates the via cost up to a given edge
     363              :      * @param[in] prev The previous edge
     364              :      * @param[in] e The given edge
     365              :      * @param[in] v The vehicle
     366              :      * @param[in,out] time The passed and updated start time in seconds
     367              :      * @param[in,out] effort The passed and updated effort
     368              :      * @param[in,out] length The passed and updated length
     369              :      */
     370              :     void updateViaCostUpToEdge(const E* const prev, const E* const e, const V* const v, double& time, double& effort, double& length) const;
     371              : 
     372              :     /** @brief Returns the recomputed cost for traversing the given edges, excluding the last one
     373              :      * @param[in] edges The vector of edges
     374              :      * @param[in] v The vehicle
     375              :      * @param[in] msTime The start time in milliseconds
     376              :      * @param[in,out] lengthp The passed and updated length
     377              :      */
     378              :     double recomputeCostsNoLastEdge(const std::vector<const E*>& edges, const V* const v, SUMOTime msTime, double* lengthp = nullptr) const {
     379              :         const E* lastEdge = edges.back();
     380              :         double time = STEPS2TIME(msTime);
     381              :         double effort = 0.;
     382              :         double length = 0.;
     383              :         if (lengthp == nullptr) {
     384              :             lengthp = &length;
     385              :         } else {
     386              :             *lengthp = 0.;
     387              :         }
     388              :         const E* prev = nullptr;
     389              :         for (const E* const e : edges) {
     390              :             if (e == lastEdge) {
     391              :                 break;
     392              :             }
     393              :             if (isProhibited(e, v)) {
     394              :                 return -1;
     395              :             }
     396              :             updateViaCost(prev, e, v, time, effort, *lengthp);
     397              :             prev = e;
     398              :         }
     399              :         updateViaCostUpToEdge(prev, lastEdge, v, time, effort, *lengthp);
     400              :         return effort;
     401              :     }
     402              : 
     403              :     /// @brief Bulk mode is not supported
     404            0 :     virtual void setBulkMode(const bool mode) {
     405              :         UNUSED_PARAMETER(mode);
     406            0 :         throw std::runtime_error("Bulk mode is not supported by the node-to-edge router.");
     407              :     }
     408              : 
     409              : protected:
     410              :     /** @brief Initialize the node-to-edge router
     411              :      * @param[in] fromEdges The vector of start edges
     412              :      * @param[in] vehicle The vehicle
     413              :      * @param[in] msTime The start time in milliseconds
     414              :      */
     415              :     void init(std::vector<const E*> fromEdges, const V* const vehicle, const SUMOTime msTime);
     416              : };
     417              : 
     418              : // ===========================================================================
     419              : // method definitions
     420              : // ===========================================================================
     421              : 
     422              : template<class E, class N, class V, class M>
     423              : void Node2EdgeRouter<E, N, V, M>::updateViaCostUpToEdge(const E* const prev, const E* const e, const V* const v, double& time, double& effort, double& length) const {
     424              :     assert(prev && e);
     425              :     for (const std::pair<const E*, const E*>& follower : prev->getViaSuccessors()) {
     426              :         if (follower.first == e) {
     427              :             updateViaEdgeCost(follower.second, v, time, effort, length);
     428              :             break;
     429              :         }
     430              :     }
     431              : }
     432              : 
     433              : template<class E, class N, class V, class M>
     434            0 : void Node2EdgeRouter<E, N, V, M>::init(std::vector<const E*> fromEdges, const V* const vehicle, const SUMOTime msTime) {
     435              :     // all EdgeInfos touched in the previous query are either in myFrontierList or myFound: clean those up
     436            0 :     for (auto& edgeInfo : this->myFrontierList) {
     437            0 :         edgeInfo->reset();
     438              :     }
     439              :     this->myFrontierList.clear();
     440            0 :     for (auto& edgeInfo : this->myFound) {
     441            0 :         edgeInfo->reset();
     442              :     }
     443              :     this->myFound.clear();
     444            0 :     for (const E* from : fromEdges) {
     445            0 :         if (from->isInternal()) {
     446            0 :             continue;
     447              :         }
     448              :         int edgeID = from->getNumericalID();
     449            0 :         auto& fromInfo = this->myEdgeInfos[edgeID];
     450            0 :         if (fromInfo.prohibited || this->isProhibited(from, vehicle)) {
     451            0 :             continue;
     452              :         }
     453            0 :         fromInfo.effort = 0.;
     454            0 :         fromInfo.heuristicEffort = 0.;
     455            0 :         fromInfo.prev = nullptr;
     456            0 :         fromInfo.leaveTime = STEPS2TIME(msTime);
     457            0 :         this->myFrontierList.push_back(&fromInfo);
     458              :     }
     459            0 : }
        

Generated by: LCOV version 2.0-1