61d9a1a attempts to implement rerouting (changing an existing route) using a modified LPA* implementation. LPA* is a descendant of A*, which in turn is a descendant of the Dijkstra algorithm, Navit’s tried-and-true routing algorithm.
(For more information on LPA*, see its Wikipedia article. As I am the author of the article, there is a slight chance that some errors I made in my implementation are also found in the article—refer to the source linked in the article to be completely sure.)
Deviations from standard LPA*:
- Where Navit deviates from standard Dijkstra, I applied the same deviations to the LPA* implementation. Specifically, the cost of a node represents its distance to the destination (in canonical LPA* it is the cost from the start, just as in Dijkstra) and the destination has a cost of 0.
- LPA* uses a heuristic, just like A* does—I used a fixed value of zero for that. For A*, this is a valid heuristic (but turns it into Dijkstra); in my understanding, it is also valid for LPA*.
- Since the heuristic is zero, this eliminates the need for two-dimensional keys in the priority queue (for which Navit uses a Fibonacci heap), somewhat simplifying things.
- Canonical LPA* terminates when the lowest key in the priority queue is greater than or equal to the cost of the destination (current position in our implementation) and when the destination (for us, the current position) is locally consistent. I did not implement this check but instead exit only when all nodes are locally consistent again. (Somewhat similar to our Dijkstra implementation.)
When a traffic message comes in which changes the cost of some segments, I add its endpoints to the queue, which causes them to be re-evaluated. At that point, retrieving segment costs will already return the updated costs. The rest is pretty much standard LPA*: take the node with the lowest key from the queue, expand it, update its neighbors (which may place them in the queue if they’re found to be locally inconsistent), then continue with the next node.
However, I notice that the implementation seems to loop or “ping-pong” between the same nodes over and over again, gradually increasing their cost further and further, and I’ve been trying to figure out why this is happening—so far to no avail.
If somebody would like to run this code:
- Grab a map containing Southern Germany and Austria. (The first test case, Munich-Geisenhausen, requires only a map of Bavaria.)
- Ensure your
navit.xml
has the following element inside its <navit/>
element:
- Ensure you have
xmlstarlet
installed (should be available from your distro’s package manager)
- Clone https://github.com/mvglasow/navit-goodies
- Check
~/src/navit-goodies/scripts/traffic/traffic-test
(you may need to adapt some paths to your local dir layout)
- Build and launch Navit
- cd to
~/src/navit-goodies/scripts/traffic
- run
traffic-test -i
(-i
causes the script to prompt you for each step)
- after each test, quit Navit and delete
traffic.xml
from your Navit dir (this is a cache of all active traffic messages; the test requires starting with no messages in the cache).
You can comment out all but the first test case (Munich-Geisenhausen) from scripts/traffic/traffic-testcases.txt
(the first test case is enough to reproduce the issue).
Each test case will do the following:
- calculate a route
- inject a traffic message affecting the route (thus triggering a partial recalculation, affecting only changed points)
- clear the route and fully recalculate it with the traffic condition present from the start
- clear the traffic message
It will write three XML files: route without traffic condition, route after partial recalculation, route after full recalculation. These can be diffed or examined with the GIS tool of your choice.