File: algorithm.html

package info (click to toggle)
routino 3.4.3-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,196 kB
  • sloc: ansic: 22,554; javascript: 7,897; xml: 5,980; perl: 2,597; cs: 921; makefile: 826; sh: 503; cpp: 382; python: 376
file content (413 lines) | stat: -rw-r--r-- 20,681 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Routino : Algorithm</title>

<!--
 Routino documentation - algorithm

 Part of the Routino routing software.

 This file Copyright 2008-2015 Andrew M. Bishop

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see http://www.gnu.org/licenses/.
-->

<link href="style.css" type="text/css" rel="stylesheet">
</head>

<body>

<!-- Header Start -->

<div class="header">

<h1>Routino : Algorithm</h1>

</div>

<!-- Header End -->

<!-- Content Start -->

<div class="content">

<h2 id="H_1_1">Algorithms</h2>

This page describes the development of the algorithm that is used in Routino for
finding routes.

<h3 id="H_1_1_1">Simplest Algorithm</h3>

The algorithm to find a route is fundamentally simple: Start at the beginning,
follow all possible routes and keep going until you reach the end.
<p>
While this method does work, it isn't fast.  To be able to find a route quickly
needs a different algorithm, one that can find the correct answer without
wasting time on routes that lead nowhere.

<h3 id="H_1_1_2">Improved Algorithm</h3>

The simplest way to do this is to follow all possible segments from the starting
node to the next nearest node (an intermediate node in the complete journey).
For each node that is reached store the shortest route from the starting node
and the length of that route.  The list of intermediate nodes needs to be
maintained in order of shortest overall route on the assumption that there is a
straight line route from here to the end node.
<br>
At each point the intermediate node that has the shortest potential overall
journey time is processed before any other node.  From the first node in the
list follow all possible segments and place the newly discovered nodes into the
same list ordered in the same way.  This will tend to constrain the list of
nodes examined to be the ones that are between the start and end nodes.  If at
any point you reach a node that has already been reached by a longer route then
you can discard that route since the newly discovered route is shorter.
Conversely if the previously discovered route is shorter then discard the new
route.
<br>
At some point the end node will be reached and then any routes with potential
lengths longer than this actual route can be immediately discarded.  The few
remaining potential routes must be continued until they are found to be shorter
or have no possibility of being shorter.  The shortest possible route is then
found.
<p>
At all times when looking at a node only those segments that are possible by the
chosen means of transport are followed.  This allows the type of transport to be
handled easily.  When finding the quickest route the same rules apply except
that the criterion for sorting is the shortest potential route (assuming that
from each node to the end is the fastest possible type of highway).
<p>
This method also works, but again it isn't very fast.  The problem is that the
complexity is proportional to the number of nodes or segments in all routes
examined between the start and end nodes.  Maintaining the list of intermediate
nodes in order is the most complex part.

<h3 id="H_1_1_3">Final Algorithm</h3>

The final algorithm that is implemented in the router is basically the one above
but with an important difference.  Instead of finding a long route among a data
set of 8,000,000 nodes (number of highway nodes in UK at beginning of 2010) it
finds one long route in a data set of 1,000,000 nodes and a few hundred very
short routes in the full data set.  Since the time taken to find a route is
proportional to the number of nodes that need to be considered the main route
takes 1/10th of the time and the very short routes take almost no time at all.
<p>
The solution to making the algorithm fast is therefore to discard most of the
nodes and only keep the interesting ones.  In this case a node is deemed to be
interesting if it is the junction of three or more segments or the junction of
two segments with different properties or has a routing restriction different
from the connecting segments.  In the algorithm and following description these
are classed as <em>super-nodes</em>.  Starting at each super-node a
<em>super-segment</em> is generated that finishes on another super-node and
contains the <em>shortest</em> path along segments with identical properties
(and these properties are inherited by the super-segment).  The point of
choosing the shortest route is that since all segments considered have identical
properties they will be treated identically when properties are taken into
account.  This decision making process can be repeated until the only the most
important and interesting nodes remain.
<p class="center">
<img alt="Original data" src="example0.png">
<br>
Original Highways
<p class="center">
<img alt="Iteration 1" src="example1.png">
<br>
First Iteration
<p class="center">
<img alt="Iteration 2" src="example2.png">
<br>
Second Iteration
<p class="center">
<img alt="Iteration 3" src="example3.png">
<br>
Third Iteration
<p class="center">
<img alt="Iteration 4" src="example4.png">
<br>
Fourth Iteration
<p>
To find a route between a start and finish point now comprises the following
steps (assuming a shortest route is required):
<ol>
  <li>Find all shortest routes from the start point along normal segments and
  stopping when super-nodes are reached.
  <li>Find all shortest routes from the end point backwards along normal
  segments and stopping when super-nodes are reached.
  <li>Find the shortest route along super-segments from the set of super-nodes
  in step 1 to the set of super-nodes in step 2 (taking into account the lengths
  found in steps 1 and 2 between the start/finish super-nodes and the ultimate
  start/finish point).
  <li>For each super-segment in step 3 find the shortest route between the two
  end-point super-nodes.
</ol>
This multi-step process is considerably quicker than using all nodes but gives a
result that still contains the full list of nodes that are visited.  There are
some special cases though, for example very short routes that do not pass
through any super-nodes, or routes that start or finish on a super-node.  In
these cases one or more of the steps listed can be removed or simplified.
<p>
When the first route reaches the final node the length of that route is retained
as a benchmark.  Any shorter complete route that is calculated later would
replace this benchmark.  As routes are tested any partial routes that are longer
than the benchmark can be immediately discarded.  Other partial routes have the
length of a perfect straight highway to the final node added to them and if the
total exceeds the benchmark they can also be discarded.  Very quickly the number
of possible routes is reduced until the absolute shortest is found.
<p>
For routes that do not start or finish on a node in the original data set a fake
node is added to an existing segment.  This requires special handling in the
algorithm but it gives mode flexibility for the start, finish and intermediate
points in a route.

<h4 id="H_1_1_3_1">Algorithm Evolution</h4>

In Routino versions 1.0 to 1.4 the algorithm used to select a super-node was the
same as above except that node properties were not included.  Routino versions
1.4.1 to 1.5.1 used a slightly different algorithm which only chose nodes that
were junctions between segments with different properties (or has a routing
restriction that is different from connecting segments in versions 1.5 and
1.5.1).  The addition of turn restrictions (described in more detail below)
requires the original algorithm since the super-segments more accurately reflect
the underlying topology.

<h4 id="H_1_1_3_2">Algorithm Implementation</h4>

The algorithm that is used for finding the route between the super-nodes using
super-segments is the A* algorithm (or a slight variation of it).  This was not
a deliberate design decision, but evolved into it during development.  This
algorithm relies on calculating the lowest score (shortest distance or quickest
time) to each node from the starting node.  The remaining score for the path to
the destination node is estimated (based on a straight line using the fastest
type of highway) and added to the current score and the result recorded.  At
each step the unvisited node that has the lowest current score is examined and
all nodes connected to it have their scores calculated.  When the destination
node has been reached all remaining unvisited nodes with scores higher than the
destination node's score can be discarded and the few remaining nodes examined.
<p>
The algorithm used to find the route between super-nodes using normal segments
is Dijkstra's algorithm (although it is implemented as the same algorithm as
above but with no estimated cost).  Since these routes tend to be short and the
CPU time for calculating the heuristic cost function is relatively large this
tends to give a quicker solution.


<h3 id="H_1_1_4">Routing Preferences</h3>

One of the important features of Routino is the ability to select a route that
is optimum for a set of criteria such as preferences for each type of highway,
speed limits and other restrictions and highway properties.
<p>
All of these features are handled by assigning a score to each segment while
calculating the route and trying to minimise the score rather than simply
minimising the length.
<dl>
  <dt>Segment length
  <dd>When calculating the shortest route the length of the segment is the
  starting point for the score.
  <dt>Speed preference
  <dd>When calculating the quickest route the time taken calculated from the
  length of the segment and the lower of the highway's own speed limit and the
  user's speed preference for the type of highway is the starting point for the
  score.
  <dt>One-way restriction
  <dd>If a highway has the one-way property in the opposite direction to the
  desired travel and the user's preference is to obey one-way restrictions then
  the segment is ignored.
  <dt>Weight, height, width &amp; length limits
  <dd>If a highway has one of these limits and its value is less than the user's
  specified requirement then the segment is ignored.
  <dt>Highway preference
  <dd>The highway preference specified by the user is a percentage, these are
  scaled so that the most preferred highway type has a weighted preference of
  1.0 (0% always has a weighted preference of 0.0).  The calculated score for a
  segment is divided by this weighted preference.
  <dt>Highway properties
  <dd>The other highway properties are specified by the user as a percentage and
  each highway either has that property or not.  The user's property preference
  is scaled into the range 0.0 (for 0%) to 1.0 (for 100%) to give a weighted
  preference, a second "non-property" weighted preference is calculated in the
  same way after subtracting the user's preference from 100%.  If a segment has
  a particular property then the calculated score is divided by the weighted
  preference for that property, if not then it is divided by the non-property
  weighted preference.  A non-linear transformation is applied so that changing
  property preferences close to 50% do not cause large variations in routes.
</dl>

<h3 id="H_1_1_5">Data Pruning</h3>

From version 2.2 there are options to "prune" nodes and segments from the input
data which means to remove nodes and/or segments without significantly changing
the routing results.
<p>
The pruning options must meet a number of conditions to be useful:
<ul>
  <li>The topology relevant to routing must remain unchanged.  The instructions
    that are produced from the reduced set of nodes and segments must be
    sufficiently accurate for anybody trying to follow them on the ground.
  <li>Any restrictions belonging to nodes or segments that stop certain types of
    traffic from following a particular highway must be preserved.
  <li>The total length must be calculated using the original data and not the
    simplified data which by its nature will typically be shorter.
  <li>The location of the remaining nodes and segments must be a good
    representation of the original nodes and segments.  Since the calculated
    route may be displayed on a map the remaining nodes and segments must
    clearly indicate the route to take.
</ul>
<p>
The prune options all have user-controllable parameters which allow the
geographical accuracy to be controlled.  This means that although the topology
is the same the geographical accuracy can be sacrificed slightly to minimise the
number of nodes and segments.
<p>
The pruning options that are available are:
<ul>
  <li>Removing the access permissions for a transport type from segments if it
  is not possible to route that transport type from those segments to a
  significant number of other places.  The limit on the pruning is set by the
  total length of the isolated group of segments.  This significantly increases
  the chance that a route will be found by not putting waypoints in inaccessible
  places.
  <li>Removing short segments, the limit is set by the length of the segment.
  This removes a number of redundant segments (and associated nodes) but rules
  are applied to ensure that removing the segments does not alter junction
  topology or remove node access permissions or changes in way properties.
  <li>Removing nodes from almost straight highways, the limit is set by the
  distance between the remaining segments and the original nodes.  This removes
  a large number of redundant nodes (and therefore segments) but again care is
  taken not to remove node access permissions or changes in way properties.
</ul>

<h3 id="H_1_1_6">Turn Restrictions</h3>

The addition of turn restrictions in version 2.0 adds a set of further
complications because it introduces a set of constraints that are far more
complex than one-way streets.
<p>
A turn restriction in the simplest case is a combination of a segment, node and
segment such that routes are not allowed to go from the first segment to the
second one through the specified node.  Exceptions for certain types of traffic
can also be specified.  Currently only this simplest type of turn restriction is
handled by the algorithm.
<p>
The first complication of turn restrictions is that the algorithm above requires
that super-segments are composed of segments with identical properties.  A turn
restriction is not the same in both directions so a super-segment cannot include
any route through that turn restriction.  The node at the centre of the turn
restriction must therefore be a super-node to avoid this.  In addition to this
all nodes connected to the turn restriction node by a single segment must also
be super-nodes to avoid any long-distance super-segments starting at the
restricted node.
<p>
The second complication of a turn restriction is that the optimum route may
require passing through the same node more than once.  This can happen where the
route needs to work around a turn restriction by driving past it, turning round
(on a roundabout perhaps) and coming back along the same highway.  Without turn
restrictions a route could be defined purely by the set of nodes that were
passed; no node would exist more than once along a route between two points.
With turn restrictions the route is defined by a node and the segment used to
get there; no route between two points will ever need to follow the same segment
in the same direction more than once.  This means that the optimisation
algorithm calculates scores for directed segments (indexed by segment and end
node) rather than for nodes.
<p>
A side-effect of this is that a route that works around a turn restriction must
be calculable using the super-segments that are stored in the database.  This
puts a limit on the amount of database optimisation that can be performed
because if too many super-segments are removed the optimum work-around may also
be removed.  The solution to this is to ensure that the database preserves all
loops that can be used to turn around and reverse direction, previously
super-segments that started and finished on the same super-node were disallowed.
<p>
Another side-effect of having the route composed of a set of locations (nodes)
as well as the direction of travel (segments used to reach them) is that via
points in the route can be forced to continue in the original direction.  If the
chosen method of transport obeys turn restrictions then it will not reverse
direction at a via point but will find an optimum route continuing in the same
direction.  The only exception to this is when the route ahead at a waypoint is
into a dead-end and an immediate U-turn is allowed.
<p>
A side-effect of having the starting direction at a via point defined by the
previous part of the route is that overall non-optimal routes may be found even
though each section between via points is optimal.  For a route with a start,
middle and end point defined it can be the case that the shortest route from the
start to the middle arrives in the opposite direction to that required for the
optimal route from the middle to the end.  The calculation of the route in
separate sections therefore may give a non-optimum result even though each
section is itself optimum based on the start conditions.
<p>
Overall the presence of turn restrictions in the database makes the routing
slower even for regions of the map that have no turn restrictions.

<h3 id="H_1_1_7">Data Implementation</h3>

The hardest part of implementing this router is the data organisation.  The
arrangement of the data to minimise the number of operations required to follow
a route from one node to another is much harder than designing the algorithm
itself.
<p>
The final implementation uses a separate table for nodes, segments and ways.
Each table individually is implemented as a C-language data structure that is
written to disk by a program which parses the OpenStreetMap XML data file.  In
the router these data structures are memory mapped so that the operating system
handles the problems of loading the needed data blocks from disk.
<p>
Each node contains a latitude and longitude and they are sorted geographically
so that converting a latitude and longitude coordinate to a node is fast as well
as looking up the coordinate of a node.  The node also contains the location in
the array of segments for the first segment that uses that node.
<br>
Each segment contains the location of the two nodes as well as the way that the
segment came from.  The location of the next segment that uses one of the two
nodes is also stored; the next segment for the other node is the following one
in the array.  The length of the segment is also pre-computed and stored.
<br>
Each way has a name, a highway type, a list of allowed types of traffic, a speed
limit, any weight, height, width or length restrictions and the highway
properties.
<p>
The super-nodes are mixed in with the nodes and the super-segments are mixed in
with the segments.  For the nodes they are the same as the normal nodes, so just
a flag is needed to indicate that they are super.  The super-segments are in
addition to the normal segments so they increase the database size (by about
10%) and are also marked with a flag.  Some segments are therefore flagged as
both normal segments and super-segments if they both have the same end nodes.
<p>
The relations are stored separately from the nodes, segments and ways.  For the
turn restriction relations the initial and final segments are stored along with
the restricted node itself.  Each node that has a turn restriction is marked in
the main node storage with a flag to indicate this information.

</div>

<!-- Content End -->

<!-- Footer Start -->

<div class="footer">

<address>
&copy; Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
</address>

</div>

<!-- Footer End -->

</body>

</html>