Skip to content

Commit 105c6c7

Browse files
author
Haykuhi
committed
Merge branch 'master' of https://github.com/livelycode/routing
Conflicts: src/store.erl
2 parents 172b8f8 + a3d1c8a commit 105c6c7

15 files changed

+243
-132
lines changed

docs/Performance Log.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#Performance Log
2+
3+
Test of in-memory database alternatives. The time for the same request was measured for each database.
4+
5+
Request: http://127.0.0.1:2904/route?from=26999575&to=275283807
6+
##Using ETS
7+
"time":211519
8+
##Using Membase
9+
###mcd library
10+
"time":19172683
11+
###merle library
12+
"time":17069207
13+
##Using Redis
14+
###eredis library
15+
"time":8140534

docs/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Starts the routing system.
88
Stops the routing system.
99

1010
### load_osm_data(File)
11-
Loads OpenStreetMap data from a .osm file. The system needs to be stopped first.
11+
Loads OpenStreetMap data from a .osm file. The system needs to be started first.
1212

1313
# HTTP APIs
1414
##Route

docs/report.md

Lines changed: 118 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,161 @@
11
# OSM Routing
22

33
## Projektziel
4+
Ziel des Projektes ist die Schaffung der technischen Infrastruktur, welche die Entwicklung eines Navigationssystems für körperlich behinderte Menschen ermöglicht.
5+
Es soll möglich sein, den kürzesten Weg zwischen zwei Nodes eines OpenStreetMap Datensets zu ermitteln.
6+
Der ermittelte Weg soll von einem Menschen mit körperlicher Behinderung komplett begehbar sein.
7+
Das System soll ausserdem eine Wegbeschreibung des ermittelten Weges generieren.
48

59
## Architektur
610
Im folgenden wird eine Übersicht über die Architektur des Systems gegeben. Dazu gehören ein Diagramm über die einzelnen Softwaremodule, eine Beschreibung des Ablaufs einer Anfrage sowie eine Übersicht über die verwendeten Erlang Module.
711

8-
### Diagramm
12+
913
![Overview](routing.pdf)
1014

1115
### Bearbeitung einer Anfrage an das System
16+
1. HTTP Anfrage wird gesendet.
17+
2. Der Server erhält die Anfrage. Die Bearbeitung der Anfrage übernimmt das Erlang System.
18+
3. server:request() wird aufgerufen. Diese Funktion dient der Erkennung der Art der Anfrage und zur Weiterleitung an die entsprechenden Submodule.
19+
4. requests:route() wird aufgerufen. Diese Funktion berechnet die Route der Anfrage. Dies geschieht mithilfe der Funktionen des astar Erlang moduls.
20+
5. Die Funtionen aus dem astar Modul greifen zur Berechnung der Route auf das geodata Erlang Modul zu. Das geodata Modul dient als High-Level API für die Datenbank. Alle Anfrage des geodata Moduls an die Datenbank laufen über das store Modul, welches als Low-Level API für diese dient.
21+
6. Nachdem die Route berechnet ist wird die HTTP Antwort in JSON enkodiert. Anschliesend sendet die Funktion server:request() die HTTP Antwort.
1222

1323
### Erlang Module
1424
#### astar
25+
Dieses Modul implementiert einen A* Algorithmus, um den kürzesten Weg zwischen zwei Knoten zu finden.
26+
1527
#### geodata
28+
Diese Modul dient als High-Level API für die Datenbank. Alle Zugriffe auf die Datenbank laufen über dieses Modul.
29+
1630
#### osm_parser
31+
Mithilfe dieses Moduls ist es mögich eine .osm Datei auszulesen und deren Inhalt in der Datenbank zu sichern.
32+
Die Daten werden beim Einlesen über generische Regeln gefiltert. So lassen sich leicht für Behinderte ungeignete Wege aus dem Datenset entfernen.
33+
1734
#### priority_queue
35+
Dieses Modul implementiert eine Prioritätswarteschlange, welche vom Routing-Algorithmus benötigt wird.
36+
1837
#### requests
38+
Dieses Modul implementiert die HTTP Antworten des HTTP Servers.
39+
1940
#### routing
41+
Dieses Modul dient als Schnittstelle für das komplette System. Andere Erlang Systeme sollten über diese Schnittstelle mit dem Routingsystem kommunizieren.
42+
2043
#### server
44+
Dieses Modul implementiert die HTTP API des Servers. Es ist nur die Serverlogik und die JSON Serialisierung enthalten.
45+
2146
#### store
47+
Dieses Modul dient als API der ets Tabellen. Die ets Tabellen sollten nur über diese API angespochen werden.
2248

2349
## Softwarekomponenten
2450
### Programmiersprache
25-
Die Wahl der Programmiersprache viel auf Erlang.
51+
Die Wahl der Programmiersprache des Backends viel auf Erlang.
2652

27-
Pro:
53+
PRO:
2854

2955
* Virtuelle Maschine:
30-
Erlang läuft auf einer VM. Damit ist es sehr leicht möglich ein Programm zu ändern, während es läuft, ohne es komplett neu zu kompilieren und neu zu starten. Dies erhöht die Produktivität.
31-
56+
Erlang läuft auf einer VM. Damit ist es sehr leicht möglich ein Programm im laufenden Betrieb zu ändern, ohne es komplett neu zu kompilieren und neu zu starten. Dies erhöht die Entwickler-Produktivität.
57+
3258
* Referenzielle Transparenz:
33-
Da der einzige Effekt einer nebeneffektfreien Funktion ihr Rückgabewert ist, sind Sie leichter zu verstehen als Prozeduren.
59+
Da der einzige Effekt einer nebeneffektfreien Funktion ihr Rückgabewert ist, ist Sie leichter zu verstehen als eine Prozedur.
3460

3561
* Deklarative Sprache:
3662
Erlang ist eine Deklarative Sprache. Im vergleich zu imperativen Sprachen führt das zu kürzeren, leichter verständlichen Programmen.
3763

3864
* Leichtgewichtige Prozesse:
3965
Prozesse sind Teil der Erlang VM und nicht Teil des Betriebssystems. Dadurch sind Sie wesentlich leichtgewichtiger als OS Prozesse oder sogar Threads. Auf einem handelsüblichen Rechner ist es möglich mehrere hundertausend Erlang Prozesse laufen zu lassen. Damit ist Erlang optimal für ein asynchrones und ausfallsicheres System geeignet.
4066

41-
42-
Kontra:
67+
KONTRA:
4368

4469
* Performancekritischer, sequentieller Code:
45-
Durch die Indirektion der VM geht ein Teil der Performance der Hardware verloren. Obwohl der grösste Teil des Systems durch IO Operationen dominiert wird, gibt es kleine performancekritische, sequentielle Code Stücke (z.B. der routing Algorithmus). Hier könnte man durch eine Hardwarenhe Sprache wie C eine verbesserte Abtwortzeit erreichen.
70+
Durch die Indirektion der VM geht ein Teil der Performance der Hardware verloren. Obwohl der grösste Teil des Systems durch IO Operationen dominiert wird, gibt es kleine, performancekritische, sequentielle Codestücke (z.B. der routing Algorithmus). Hier könnte man durch eine Hardwarenahe Sprache wie C eine verbesserte Antwortzeit erreichen.
4671

4772
### Datenbank
73+
74+
Als Datenbank werden mehrere ets Tabellen verwendet.
75+
76+
* http://www.erlang.org/doc/man/ets.html
77+
78+
PRO:
79+
80+
* Einfache API:
81+
Da ets Tabellen einfache Key-Value Stores sind, sind Sie sehr einfach zu benutzen.
82+
83+
* Erlang Modul:
84+
ets Tabellen sind Teil des Erlang Systems. Damit sind Sie direkt über Erlang Syntax verwendbar. Darüber hinaus werden die Abhängigkeiten des Systems nicht unnötig erweitert.
85+
86+
* In-Memory Datenbank:
87+
ets Tabellen werden komplett im Hauptspeicher gehalten. Zugriffe auf die Tabellen sind damit sehr schnell.
88+
89+
KONTRA:
90+
91+
* Speicherverbrauch:
92+
Da alle Tabellen komplett im Hautspeicher gehalten werden, verbrauchen diese viel Platz. Es ist somit nicht möglich extrem grosse .osm Dateien einzulesen.
93+
4894
### Algorithmus
49-
### HTTP Server
95+
Zur Berechnung der kürzesten Route zwischen zwei Knoten wird der A-Star Algorithmus verwendet.
96+
97+
PRO:
98+
99+
* Einfach
100+
A-Star ist leicht zu implementieren. Sämtliche in vergleichbaren Systemen implementierten Algorithmen basieren auf A-Star mit zusätzlichen Optimierungen.
101+
102+
KONTRA:
103+
104+
* Speicherbedarf
105+
Da alle besuchten Knoten in einer Liste gahelten werden, benötigt A-Star im worst-case sehr viel Speicher.
106+
107+
### HTTP API
108+
Das gesamte System wird über eine HTTP Schnittstelle angesprochen.
109+
110+
PRO:
111+
112+
* Universelle API:
113+
Sowohl lokale Anfragen als auch Netzwerkanfragen können über eine API getätigt werden.
114+
115+
* Einfache API:
116+
HTTP Anfragen sind sehr einfach zu stellen. Es existiert zu jeder populären Sprache ein entsprechendes Modul.
117+
118+
KONTRA:
119+
120+
* Nichts (bei der Art des Systems).
121+
122+
##Performanz-Tests
123+
Ausgewertet wurden verschiedene Metriken von drei Routen unterschiedlicher Länge:
124+
125+
| Metrik | Rohrbach - Uni | OMZ Uni - Mensa Uni | OMZ Uni - Mannheim |
126+
| - | - | - | - |
127+
| Zeit | 82 ms | 190 ms | 1520 ms |
128+
| Nodes in Ergebnis | 20 | 25 | 71 |
129+
| Pfadlänge | 460 m | 4151 m | 17 866 m |
130+
| Besuchte Nodes in Algorithmus | 451 | 2550 | 15816 |
131+
| Speicherbedarf | 44 k words | 220 k words | 1338 k words |
132+
50133

51134
## Weiterentwicklung
52135
### Geochouch
136+
Es bietet sich an, das Routing-System an GeoCouch anzuknüpfen. GeoCouch ermöglicht die Berechnung von Bounding-Box Anfragen, welche für eine weitere Verbesserung der Wegbeschreibung und einen fortgeschrittenen Name-Server nötig sind.
137+
Da GeoCouch/CouchDB ebenfalls in Erlang implementiert wurde, ließe sich das Routing System sogar zu einem CouchDB-Plugin entwickeln. Das hätte den Vorteil einer schnelleren Daten-Zugriffszeit und würde das Deployment erleichtern.
138+
Ein mit GeoCouch gebundeltes Routing System bietet beste Vorraussetzungen zur Entwicklung von Routing-Webapps. Der Entwickler dieser Apps müsste lediglich JavaScript und HTML beherrschen, da die komplette Backend-Funktionalität von GeoCouch abgedeckt werden kann.
139+
140+
* https://github.com/couchbase/geocouch
141+
* http://www.couchbase.org/
142+
143+
###GeoJson
144+
Sämtliche APIs des Routing-Systems sollten auf GeoJson standardisiert werden. GeoJson ist ein leichtgewichtiger Standard für Geo-Daten, der sich wachsender Beliebtheit in der Webentwicklung erfreut.
145+
Die Standardisierung würde die Verwendung vor allem von UI-libraries deutlich erleichtern.
146+
147+
* http://geojson.org/
148+
53149
### Polymaps
54-
### Algorithmus
150+
Polymaps ist eine JavaScript library, die das Erstellen stark angepasster und dynamischer Karten ermöglicht. Als karthografische Ressourcen können OpenStreetMap, Google Maps, Bing und weitere Anbieter eingebunden werden.
151+
Die Anpassung und das Anzeigen zusätzlicher Informationen (Pfade, Lables, Statistiken, ...) auf der Karte erfolgt über SVG und GeoJson.
152+
Es eignet sich besonders Polymaps zusammen mit d3.js zu verwenden - eine weitere Javascript library, die das Erstellen datengetriebener Dokumente ermöglicht.
153+
Hinter Polymaps und d3.js stehen herraussragende Entwickler der Stanford Visualization Group und eine weitere Wartung der libraries ist sicher.
154+
155+
* http://polymaps.org/
156+
* http://mbostock.github.com/d3/
157+
158+
## Mitwirkende
159+
* Johannes Auer
160+
* Haykuhi Jaghinyan
161+
* Mirko Kiefer

docs/report.pdf

68.1 KB
Binary file not shown.

src/astar.erl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
%
66
% This module implements A* to find the shortest path between two nodes
77
% It makes use of the geodata module for accessing data.
8-
% The priority queue used by the algorithm is implemented in the priority_queue module.
8+
% The priority queue used by the algorithm is implemented in the astar_priority_queue module.
99

1010
-module(astar).
1111
-export([shortest_path/2, shortest_path_with_distances/2]).
@@ -27,7 +27,7 @@ shortest_path_internal(SourceID, TargetID) ->
2727
StartT = now(),
2828
Tab = ets:new(shortest_path, [set, public]),
2929
VisitedNodes = ets:new(visited_nodes, [set, public]),
30-
Q1 = priority_queue:new(),
30+
Q1 = astar_priority_queue:new(),
3131

3232
ets:insert(Tab, {SourceID, {previous, undefined}, {distance, 0}}),
3333
Path = recurse_nodes(SourceID, TargetID, #state{tab=Tab, queue=Q1, visited_nodes=VisitedNodes}),
@@ -51,12 +51,12 @@ recurse_nodes(Target, Target, State) ->
5151
recurse_nodes(Node, Target, State=#state{tab=Tab, queue=Queue, visited_nodes=Visited}) ->
5252
visited(Node, true, Visited),
5353
OldQueueEntry = add_heuristic(get_distance(Node, Tab), Node, Target),
54-
Q1 = priority_queue:remove({Node, OldQueueEntry}, Queue),
54+
Q1 = astar_priority_queue:remove({Node, OldQueueEntry}, Queue),
5555
Neighbours = neighbours(Node),
5656
Q2 = update_distances(Node, Target, Neighbours, State#state{queue=Q1}),
5757
case gb_trees:size(Q2) of
5858
0 -> Tab;
59-
_Any -> {ClosestNode, _Distance} = priority_queue:smallest(Q2),
59+
_Any -> {ClosestNode, _Distance} = astar_priority_queue:smallest(Q2),
6060
recurse_nodes(ClosestNode, Target, State#state{queue=Q2})
6161
end.
6262

@@ -81,8 +81,8 @@ recurse_neighbours([Neighbour|Rest], CurrentNode, CurrentDistance, Target, State
8181

8282
% returns an updated version of the priority queue
8383
update_distance(Node, PreviousNode, OldDistance, NewDistance, Target, State) ->
84-
Q1 = priority_queue:remove({Node, add_heuristic(OldDistance, Node, Target)}, State#state.queue),
85-
Q2 = priority_queue:add({Node, add_heuristic(NewDistance, Node, Target)}, Q1),
84+
Q1 = astar_priority_queue:remove({Node, add_heuristic(OldDistance, Node, Target)}, State#state.queue),
85+
Q2 = astar_priority_queue:add({Node, add_heuristic(NewDistance, Node, Target)}, Q1),
8686
ets:insert(State#state.tab, {Node, {previous, PreviousNode}, {distance, NewDistance}}),
8787
Q2.
8888

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
%
66
% This module provides a simple wrapper around erlang's gb_tree module to simplify usage as an efficient priority queue.
77

8-
-module(priority_queue).
8+
-module(astar_priority_queue).
99

1010
-export([new/0, add/2, list/1, remove/2, remove_all/2, smallest/1]).
1111

src/astar_priority_queue_test.erl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
-module(astar_priority_queue_test).
3+
4+
-include_lib("eunit/include/eunit.hrl").
5+
6+
7+
add_remove_get_smallest_test() ->
8+
List = [{abc, 1}, {ab, 2}, {de, 4}],
9+
Q1 = astar_priority_queue:add(List, gb_trees:empty()),
10+
11+
?assertEqual(List, astar_priority_queue:list(Q1)),
12+
13+
?assertEqual({abc, 1}, astar_priority_queue:smallest(Q1)),
14+
15+
Q2 = astar_priority_queue:remove({abc, 1}, Q1),
16+
?assertEqual([{ab, 2}, {de, 4}], astar_priority_queue:list(Q2)),
17+
18+
Q3 = astar_priority_queue:remove_all([{ab, 2}, {de, 4}], Q2),
19+
?assertEqual([], astar_priority_queue:list(Q3)).

src/geodata.erl

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313

1414
neighbours(NodeID) ->
1515
WayIds = store:node2wayids(NodeID),
16-
Ways = lists:map(fun(WayID) -> #way{refs=Refs} = store:lookup_way(WayID),
17-
Refs end, WayIds),
18-
Neighbours = lists:flatten(lists:map(fun(Refs) -> neighbours(NodeID, Refs) end, Ways)),
16+
Refs = lists:map(fun(WayID) ->
17+
#way{refs=Refs} = store:lookup_way(WayID),
18+
Refs
19+
end, WayIds),
20+
Neighbours = utils:flatten_once(Refs),
1921
NeighboursDetails = [{{node, Neighbour}, {distance, distance(NodeID, Neighbour)}} ||
2022
Neighbour <- Neighbours, store:lookup_node(Neighbour) =/= undefined],
2123
NeighboursDetails.
@@ -95,23 +97,4 @@ angle(A, B, C) ->
9597
coords(Node) ->
9698
{LatFloat, _} = string:to_float(Node#node.lat),
9799
{LonFloat, _} = string:to_float(Node#node.lon),
98-
{LatFloat, LonFloat}.
99-
100-
neighbours(Element, List) ->
101-
case List of
102-
[] -> Result = [];
103-
[Element] -> Result = [];
104-
[Element | Rest] -> [Next | _] = Rest, Result = [Next];
105-
[First | Rest] -> Result = neighbours(Element, First, Rest)
106-
end,
107-
Result.
108-
109-
neighbours(Element, Last, List) ->
110-
case List of
111-
[] -> Result = [Last];
112-
[Element | []] -> Result = [Last];
113-
[Element | Rest] -> [Next | _] = Rest, Result = [Last, Next];
114-
[_First | []] -> Result = [];
115-
[First | Rest] -> Result = neighbours(Element, First, Rest)
116-
end,
117-
Result.
100+
{LatFloat, LonFloat}.

0 commit comments

Comments
 (0)