Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
AStarRouter.h
Go to the documentation of this file.
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/****************************************************************************/
20// A* Algorithm using euclidean distance heuristic.
21// Based on DijkstraRouter. For routing by effort a novel heuristic would be needed.
22/****************************************************************************/
23#pragma once
24#include <config.h>
25
26#include <cassert>
27#include <string>
28#include <functional>
29#include <vector>
30#include <set>
31#include <limits>
32#include <algorithm>
33#include <iterator>
34#include <map>
35#include <iostream>
36#include <memory>
43#include "AStarLookupTable.h"
44#include "SUMOAbstractRouter.h"
45
46#define UNREACHABLE (std::numeric_limits<double>::max() / 1000.0)
47
48//#define ASTAR_DEBUG_QUERY
49//#define ASTAR_DEBUG_QUERY_FOLLOWERS
50//#define ASTAR_DEBUG_QUERY_PERF
51//#define ASTAR_DEBUG_VISITED
52//#define ASTAR_DEBUG_COND (vehicle->getID() == "")
53//#define ASTAR_DEBUG_COND (true)
54//#define ASTAR_DEBUG_LOOKUPTABLE
55//#define ASTAR_DEBUG_LOOKUPTABLE_FROM "disabled"
56//#define ASTAR_DEBUG_UNREACHABLE
57
58// ===========================================================================
59// class definitions
60// ===========================================================================
75template<class E, class V, class M>
76class AStarRouter : public SUMOAbstractRouter<E, V> {
77public:
81
87 public:
89 bool operator()(const typename SUMOAbstractRouter<E, V>::EdgeInfo* nod1, const typename SUMOAbstractRouter<E, V>::EdgeInfo* nod2) const {
90 if (nod1->heuristicEffort == nod2->heuristicEffort) {
91 return nod1->edge->getNumericalID() > nod2->edge->getNumericalID();
92 }
93 return nod1->heuristicEffort > nod2->heuristicEffort;
94 }
95 };
96
98 AStarRouter(const std::vector<E*>& edges, bool unbuildIsWarning, typename SUMOAbstractRouter<E, V>::Operation operation, const std::shared_ptr<const LookupTable> lookup = nullptr,
99 const bool havePermissions = false, const bool haveRestrictions = false) :
100 SUMOAbstractRouter<E, V>("AStarRouter", unbuildIsWarning, operation, nullptr, havePermissions, haveRestrictions),
101 myLookupTable(lookup),
102 myMaxSpeed(NUMERICAL_EPS) {
103 for (const E* const edge : edges) {
104 this->myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edge));
105 myMaxSpeed = MAX2(myMaxSpeed, edge->getSpeedLimit() * MAX2(1.0, edge->getLengthGeometryFactor()));
106 }
107 }
108
109 AStarRouter(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,
110 const bool havePermissions = false, const bool haveRestrictions = false) :
111 SUMOAbstractRouter<E, V>("AStarRouter", unbuildIsWarning, operation, nullptr, havePermissions, haveRestrictions),
112 myLookupTable(lookup),
113 myMaxSpeed(NUMERICAL_EPS) {
114 for (const auto& edgeInfo : edgeInfos) {
115 this->myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edgeInfo.edge));
116 myMaxSpeed = MAX2(myMaxSpeed, edgeInfo.edge->getSpeedLimit() * edgeInfo.edge->getLengthGeometryFactor());
117 }
118 }
119
121 virtual ~AStarRouter() {}
122
127
129 bool compute(const E* from, const E* to, const V* const vehicle,
130 SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
131 assert(from != nullptr && to != nullptr);
132 // check whether from and to can be used
133 if (this->isProhibited(from, vehicle, STEPS2TIME(msTime))) {
134 if (!silent) {
135 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + from->getID() + "'.");
136 }
137 return false;
138 }
139 // technically, a temporary permission might be lifted by the time of arrival
140 if (this->isProhibited(to, vehicle, STEPS2TIME(msTime))) {
141 if (!silent) {
142 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on destination edge '" + to->getID() + "'.");
143 }
144 return false;
145 }
146 double length = 0.; // dummy for the via edge cost update
147 this->startQuery();
148#ifdef ASTAR_DEBUG_QUERY
149 if (ASTAR_DEBUG_COND) {
150 std::cout << "DEBUG: starting search for '" << Named::getIDSecure(vehicle) << "' speed: " << MIN2(vehicle->getMaxSpeed(), myMaxSpeed * vehicle->getChosenSpeedFactor()) << " time: " << STEPS2TIME(msTime) << "\n";
151 }
152#endif
153
154 const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
155 if (this->myBulkMode && !this->myAmClean) {
156 const auto& toInfo = this->myEdgeInfos[to->getNumericalID()];
157 if (toInfo.visited) {
158 this->buildPathFrom(&toInfo, into);
159 this->endQuery(1);
160 return true;
161 }
162 } else {
163 this->init(from->getNumericalID(), msTime);
164 this->myAmClean = false;
165 }
166 // loop
167 int num_visited = 0;
168 const bool mayRevisit = myLookupTable != nullptr && !myLookupTable->consistent();
169 const double speed = vehicle == nullptr ? myMaxSpeed : MIN2(vehicle->getMaxSpeed(), myMaxSpeed * vehicle->getChosenSpeedFactor());
170 while (!this->myFrontierList.empty()) {
171 num_visited += 1;
172 // use the node with the minimal length
173 auto* const minimumInfo = this->myFrontierList.front();
174 const E* const minEdge = minimumInfo->edge;
175#ifdef ASTAR_DEBUG_QUERY
176 if (ASTAR_DEBUG_COND) {
177 std::cout << "DEBUG: hit=" << minEdge->getID()
178 << " TT=" << minimumInfo->effort
179 << " EF=" << this->getEffort(minEdge, vehicle, minimumInfo->leaveTime)
180 << " HT=" << minimumInfo->heuristicEffort
181 << " Q(TT,HT,Edge)=";
182 for (const auto& edgeInfo : this->myFrontierList) {
183 std::cout << "\n " << edgeInfo->effort << "," << edgeInfo->heuristicEffort << "," << edgeInfo->edge->getID();
184 }
185 std::cout << "\n";
186 }
187#endif
188 // check whether the destination node was already reached
189 if (minEdge == to) {
190 this->buildPathFrom(minimumInfo, into);
191 this->endQuery(num_visited);
192#ifdef ASTAR_DEBUG_QUERY_PERF
193 if (ASTAR_DEBUG_COND) {
194 std::cout << "visited " + toString(num_visited) + " edges (final path length=" + toString(into.size())
195 + " time=" + toString(this->recomputeCosts(into, vehicle, msTime))
196 + " edges=" + toString(into) + ")\n";
197 }
198#endif
199#ifdef ASTAR_DEBUG_VISITED
200 if (ASTAR_DEBUG_COND) {
202 for (const auto& i : this->myEdgeInfos) {
203 if (i.visited) {
204 dev << "edge:" << i.edge->getID() << "\n";
205 }
206 }
207 dev.close();
208 }
209#endif
210 return true;
211 }
212 std::pop_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
213 this->myFrontierList.pop_back();
214 this->myFound.push_back(minimumInfo);
215 minimumInfo->visited = true;
216 const double effortDelta = this->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
217 const double leaveTime = minimumInfo->leaveTime + this->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
218
219 // admissible A* heuristic: straight line distance at maximum speed
220 // this is calculated from the end of minEdge so it possibly includes via efforts to the followers
221 const double heuristic_remaining = (myLookupTable == nullptr ? minEdge->getDistanceTo(to) / speed :
222 myLookupTable->lowerBound(minEdge, to, speed, vehicle->getChosenSpeedFactor(),
223 minEdge->getMinimumTravelTime(nullptr), to->getMinimumTravelTime(nullptr)));
224 if (heuristic_remaining == UNREACHABLE) {
225 continue;
226 }
227 const double heuristicEffort = minimumInfo->effort + effortDelta + heuristic_remaining;
228 // check all ways from the node with the minimal length
229 for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass)) {
230 auto& followerInfo = this->myEdgeInfos[follower.first->getNumericalID()];
231 // check whether it can be used
232 if (this->isProhibited(follower.first, vehicle, leaveTime)) {
233 continue;
234 }
235 double effort = minimumInfo->effort + effortDelta;
236 double time = leaveTime;
237 this->updateViaEdgeCost(follower.second, vehicle, time, effort, length);
238 const double oldEffort = followerInfo.effort;
239 if ((!followerInfo.visited || mayRevisit) && effort < oldEffort) {
240 followerInfo.effort = effort;
241 // if we use the effort including the via effort below we would count the via twice as shown by the ticket676 test
242 // but we should never get below the real effort, see #12463
243 followerInfo.heuristicEffort = MAX2(MIN2(heuristicEffort, followerInfo.heuristicEffort), effort);
244 followerInfo.leaveTime = time;
245 followerInfo.prev = minimumInfo;
246#ifdef ASTAR_DEBUG_QUERY_FOLLOWERS
247 if (ASTAR_DEBUG_COND) {
248 std::cout << " follower=" << followerInfo.edge->getID()
249 << " OEF=" << (oldEffort == std::numeric_limits<double>::max() ? "inf" : toString(oldEffort))
250 << " TT=" << effort << " HR=" << heuristic_remaining << " HT=" << followerInfo.heuristicEffort << "\n";
251 }
252#endif
253 if (oldEffort == std::numeric_limits<double>::max()) {
254 this->myFrontierList.push_back(&followerInfo);
255 std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
256 } else {
257 auto fi = std::find(this->myFrontierList.begin(), this->myFrontierList.end(), &followerInfo);
258 if (fi == this->myFrontierList.end()) {
259 assert(mayRevisit);
260 this->myFrontierList.push_back(&followerInfo);
261 std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
262 } else {
263 std::push_heap(this->myFrontierList.begin(), fi + 1, myComparator);
264 }
265 }
266 }
267 }
268 }
269 this->endQuery(num_visited);
270#ifdef ASTAR_DEBUG_QUERY_PERF
271 if (ASTAR_DEBUG_COND) {
272 std::cout << "visited " + toString(num_visited) + " edges (unsuccessful path length: " + toString(into.size()) + ")\n";
273 }
274#endif
275 if (!silent) {
276 this->myErrorMsgHandler->informf(TL("No connection between edge '%' and edge '%' found."), from->getID(), to->getID());
277 }
278 return false;
279 }
280
281
282protected:
284
286 const std::shared_ptr<const LookupTable> myLookupTable;
287
290};
#define UNREACHABLE
Definition AStarRouter.h:46
long long int SUMOTime
Definition GUI.h:36
#define TL(string)
Definition MsgHandler.h:304
#define STEPS2TIME(x)
Definition SUMOTime.h:55
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_IGNORING
vehicles ignoring classes
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
bool operator()(const typename SUMOAbstractRouter< E, V >::EdgeInfo *nod1, const typename SUMOAbstractRouter< E, V >::EdgeInfo *nod2) const
Comparing method.
Definition AStarRouter.h:89
Computes the shortest path through a network using the A* algorithm.
Definition AStarRouter.h:76
const std::shared_ptr< const LookupTable > myLookupTable
the lookup table for travel time heuristics
virtual ~AStarRouter()
Destructor.
virtual SUMOAbstractRouter< E, V > * clone()
EdgeInfoComparator myComparator
bool compute(const E *from, const E *to, const V *const vehicle, SUMOTime msTime, std::vector< const E * > &into, bool silent=false)
Builds the route between the given edges using the minimum travel time.
double myMaxSpeed
maximum speed in the network
FullLookupTable< E, V > FLT
Definition AStarRouter.h:79
AStarRouter(const std::vector< E * > &edges, bool unbuildIsWarning, typename SUMOAbstractRouter< E, V >::Operation operation, const std::shared_ptr< const LookupTable > lookup=nullptr, const bool havePermissions=false, const bool haveRestrictions=false)
Constructor.
Definition AStarRouter.h:98
LandmarkLookupTable< E, V, M > LMLT
Definition AStarRouter.h:80
AbstractLookupTable< E, V > LookupTable
Definition AStarRouter.h:78
AStarRouter(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, const bool havePermissions=false, const bool haveRestrictions=false)
Computes the shortest path through a network using the A* algorithm.
virtual void inform(std::string msg, bool addType=true)
adds a new error to the list
static MsgHandler * getWarningInstance()
Returns the instance to add warnings to.
void informf(const std::string &format, T value, Targs... Fargs)
adds a new formatted message
Definition MsgHandler.h:116
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition Named.h:67
Static storage of an output device and its base (abstract) implementation.
void close()
Closes the device and removes it from the dictionary.
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
const E *const edge
The current edge.
double heuristicEffort
Estimated effort to reach the edge (effort + lower bound on remaining effort)
const bool myHavePermissions
whether edge permissions need to be considered
bool myBulkMode
whether we are currently operating several route queries in a bulk
std::vector< typename SUMOAbstractRouter< E, V >::EdgeInfo > myEdgeInfos
The container of edge information.
Operation myOperation
The object's operation to perform.
double getTravelTime(const E *const e, const V *const v, const double t, const double effort) const
double getEffort(const E *const e, const V *const v, double t) const
bool isProhibited(const E *const edge, const V *const vehicle, double t) const
void updateViaEdgeCost(const E *viaEdge, const V *const v, double &time, double &effort, double &length) const
void init(const int edgeID, const SUMOTime msTime)
bool myAmClean
whether we are already initialized
const bool myHaveRestrictions
whether edge restrictions need to be considered
void buildPathFrom(const typename SUMOAbstractRouter< E, V >::EdgeInfo *rbegin, std::vector< const E * > &edges)
Builds the path from marked edges.
virtual double recomputeCosts(const std::vector< const E * > &edges, const V *const v, SUMOTime msTime, double *lengthp=nullptr) const
void endQuery(int visits)
MsgHandler * myErrorMsgHandler
the handler for routing errors
std::vector< typename SUMOAbstractRouter< E, V >::EdgeInfo * > myFrontierList
A container for reusage of the min edge heap.
std::vector< typename SUMOAbstractRouter< E, V >::EdgeInfo * > myFound
list of visited Edges (for resetting)
static std::string replace(std::string str, const std::string &what, const std::string &by)
Replaces all occurrences of the second string by the third string within the first string.