Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/router/AStarRouter.h
194024 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2012-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 AStarRouter.h
15
/// @author Jakob Erdmann
16
/// @author Daniel Krajzewicz
17
/// @author Michael Behrisch
18
/// @date January 2012
19
///
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>
37
#include <utils/common/MsgHandler.h>
38
#include <utils/common/StringTokenizer.h>
39
#include <utils/common/StringUtils.h>
40
#include <utils/common/StdDefs.h>
41
#include <utils/common/ToString.h>
42
#include <utils/iodevices/OutputDevice.h>
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
// ===========================================================================
61
/**
62
* @class AStarRouter
63
* @brief Computes the shortest path through a network using the A* algorithm.
64
*
65
* The template parameters are:
66
* @param E The edge class to use (MSEdge/ROEdge)
67
* @param V The vehicle class to use (MSVehicle/ROVehicle)
68
* @param BASE The base class to use (SUMOAbstractRouterPermissions/SUMOAbstractRouter)
69
*
70
* The router is edge-based. It must know the number of edges for internal reasons
71
* and whether a missing connection between two given edges (unbuild route) shall
72
* be reported as an error or as a warning.
73
*
74
*/
75
template<class E, class V, class M>
76
class AStarRouter : public SUMOAbstractRouter<E, V> {
77
public:
78
typedef AbstractLookupTable<E, V> LookupTable;
79
typedef FullLookupTable<E, V> FLT;
80
typedef LandmarkLookupTable<E, V, M> LMLT;
81
82
/**
83
* @class EdgeInfoComparator
84
* Class to compare (and so sort) nodes by their effort
85
*/
86
class EdgeInfoComparator {
87
public:
88
/// Comparing method
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
97
/// Constructor
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
120
/// Destructor
121
virtual ~AStarRouter() {}
122
123
virtual SUMOAbstractRouter<E, V>* clone() {
124
return new AStarRouter<E, V, M>(this->myEdgeInfos, this->myErrorMsgHandler == MsgHandler::getWarningInstance(), this->myOperation, myLookupTable,
125
this->myHavePermissions, this->myHaveRestrictions);
126
}
127
128
/** @brief Builds the route between the given edges using the minimum travel time */
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) {
201
OutputDevice& dev = OutputDevice::getDevice(StringUtils::replace(Named::getIDSecure(vehicle), ":", "_") + "_" + toString(STEPS2TIME(msTime)));
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
282
protected:
283
EdgeInfoComparator myComparator;
284
285
/// @brief the lookup table for travel time heuristics
286
const std::shared_ptr<const LookupTable> myLookupTable;
287
288
/// @brief maximum speed in the network
289
double myMaxSpeed;
290
};
291
292