Skip to content

Commit 93e6199

Browse files
committed
Initial commit
0 parents  commit 93e6199

23 files changed

+2940
-0
lines changed

Diff for: .gitignore

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Files generated by invoking Julia with --code-coverage
2+
*.jl.cov
3+
*.jl.*.cov
4+
5+
# Files generated by invoking Julia with --track-allocation
6+
*.jl.mem
7+
8+
# System-specific files and directories generated by the BinaryProvider and BinDeps packages
9+
# They contain absolute paths specific to the host computer, and so should not be committed
10+
deps/deps.jl
11+
deps/build.log
12+
deps/downloads/
13+
deps/usr/
14+
deps/src/
15+
16+
# Build artifacts for creating documentation generated by the Documenter package
17+
docs/build/
18+
docs/site/
19+
20+
# File generated by Pkg, the package manager, based on a corresponding Project.toml
21+
# It records a fixed state of all packages used by the project. As such, it should not be
22+
# committed for packages, but should be committed for applications that require a static
23+
# environment.
24+
Manifest.toml
25+
26+
.vscode/
27+
.ipynb_checkpoints/

Diff for: LICENSE

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Copyright (C) 2020, Deloitte Digital. All rights reserved.
2+
3+
LightOSM.jl can be downloaded from: https://github.com/DeloitteDigitalAPAC/LightOSM.jl
4+
5+
Redistribution and use in source and binary forms, with or without modification,
6+
are permitted provided that the following conditions are met:
7+
8+
Redistributions of source code must retain the above copyright notice, this list
9+
of conditions and the following disclaimer.
10+
11+
Redistributions in binary form must reproduce the above copyright notice, this list
12+
of conditions and the following disclaimer in the documentation and/or other
13+
materials provided with the distribution.
14+
15+
Neither the name of the copyright holder nor the names of its contributors may be
16+
used to endorse or promote products derived from this software without specific
17+
prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22+
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25+
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
POSSIBILITY OF SUCH DAMAGE.

Diff for: Project.toml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name = "LightOSM"
2+
uuid = "d1922b25-af4e-4ba3-84af-fe9bea896051"
3+
authors = ["Jack Chan <[email protected]>"]
4+
version = "0.1.0"
5+
6+
[deps]
7+
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
8+
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
9+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
10+
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
11+
LightXML = "9c8b4983-aa76-5018-a973-4c85ecc9e179"
12+
MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
13+
NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
14+
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
15+
SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622"
16+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
17+
StaticGraphs = "4c8beaf5-199b-59a0-a7f2-21d17de635b6"
18+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
19+
20+
[compat]
21+
julia = "1"
22+
LightGraphs = "1.3.3"
23+
DataStructures = "0.17.20"
24+
HTTP = "0.8.17"
25+
JSON = "0.21.0"
26+
LightXML = "0.9.0"
27+
MetaGraphs = "0.6.5"
28+
NearestNeighbors = "0.4.6"
29+
Parameters = "0.12.1"
30+
SimpleWeightedGraphs = "1.1.1"
31+
StaticGraphs = "0.2.0"

Diff for: README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# LightOSM.jl
2+
3+
`LightOSM.jl` is **[Julia](https://julialang.org/)** package for downloading and analysing geospatial data from **[OpenStreetMap](https://wiki.openstreetmap.org/wiki/Main_Page)** APIs (**[Nominatim](https://nominatim.openstreetmap.org/ui/search.html)** and **[Overpass](https://overpass-api.de)**), such as nodes, ways, relations and building polygons.
4+
5+
## Acknowledgements
6+
7+
`LightOSM.jl` is inspired by the Python package **[OSMnx](https://github.com/gboeing/osmnx)** for its interface and Overpass query logic. Graph analysis algorithms (connected components and shortest path) are based on **[LightGraphs.jl](https://github.com/JuliaGraphs/LightGraphs.jl)** implementation, but adapted to account for turn restrictions and improve runtime performance.
8+
9+
Another honourable mention goes to an existing Julia package **[OpenStreetMapX.jl](https://github.com/pszufe/OpenStreetMapX.jl)** as many learnings were taken to improve parsing of raw OpenStreetMap data.
10+
11+
## Key Features
12+
13+
- `Search`, `download` and `save` OpenSteetMap data in .osm, .xml or .json, using a place name, centroid point or bounding box
14+
- Parse OpenStreetMap `transport network` data such as motorway, cycleway or walkway
15+
- Parse OpenStreetMap `buildings` data into a format consistent with the **[GeoJSON](https://tools.ietf.org/html/rfc7946)** standard, allowing for visualisation with libraries such as **[deck.gl](https://github.com/visgl/deck.gl)**
16+
- Calculate `shortest path` between two nodes using the Dijkstra or A\* algorithm (based on LightGraphs.jl, but adapted for better performance and use cases such as `turn resrictions`)
17+
- Find `nearest nodes` from a query point using a K-D Tree data structure (implemented using **[NearestNeighbors.jl](https://github.com/KristofferC/NearestNeighbors.jl)**)
18+
19+
## Documentation
20+
21+
Documentation for the API can be found here **[INSERT DOCUMENTATION LINK]()**.
22+
23+
## Usage
24+
25+
A comprehensive tutorial can be found found **[here](https://deloittedigitalapac.github.io/LightOSM.jl/notebooks/tutorial.html)**.
26+
27+
## Benchmarks
28+
29+
Benchmark comparison for shortest path algorithms can be found **[here](https://deloittedigitalapac.github.io/LightOSM.jl/notebooks/benchmarks.html)**.

Diff for: benchmark/Project.toml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[deps]
2+
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
3+
OpenStreetMapX = "86cd37e6-c0ff-550b-95fe-21d72c8d4fc9"
4+
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
5+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
6+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
7+
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"

Diff for: benchmark/benchmarks.jl

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
using Random
2+
using BenchmarkTools
3+
using LightOSM
4+
using OpenStreetMapX
5+
using LightGraphs
6+
using DataStructures
7+
using JSON
8+
9+
"""
10+
Setup
11+
"""
12+
13+
Random.seed!(1234) # Set seed so experiment is reproducible
14+
15+
point = GeoLocation(-37.8142176, 144.9631608) # Melbourne, Australia
16+
radius = 5 # km
17+
data_file = joinpath(@__DIR__, "benchmark_map.osm")
18+
19+
g_losm = LightOSM.graph_from_download(:point,
20+
point=point,
21+
radius=radius,
22+
weight_type=:distance,
23+
save_to_file_location=data_file)
24+
25+
# g_losm = LightOSM.graph_from_file(data_file, weight_type=:distance)
26+
@time g_losm_precompute = LightOSM.graph_from_file(data_file, weight_type=:distance, precompute_dijkstra_states=true)
27+
28+
g_osmx = OpenStreetMapX.get_map_data(data_file,
29+
use_cache=false,
30+
only_intersections=false,
31+
trim_to_connected_graph=true)
32+
33+
34+
"""
35+
Define benchmark functions
36+
"""
37+
38+
function losm_shortest_path(g::LightOSM.OSMGraph, o_d_nodes, algorithm)
39+
for (o, d) in o_d_nodes
40+
try
41+
LightOSM.shortest_path(g, o, d, algorithm=algorithm)
42+
catch
43+
# Error exception will be thrown if path does not exist from origin to destination node
44+
end
45+
end
46+
end
47+
48+
function osmx_shortest_path(g::OpenStreetMapX.MapData, o_d_nodes, algorithm)
49+
for (o, d) in o_d_nodes
50+
try
51+
OpenStreetMapX.find_route(g, o, d, g.w, routing=algorithm, heuristic=(u, v) -> OpenStreetMapX.get_distance(u, v, g_osmx.nodes, g_osmx.n), get_distance=false, get_time=false)
52+
catch
53+
# Error exception will be thrown if path does not exist from origin to destination node
54+
end
55+
end
56+
end
57+
58+
function lg_shortest_path(g::LightOSM.OSMGraph, o_d_indices, algorithm)
59+
if algorithm == :astar
60+
for (o, d) in o_d_indices
61+
try
62+
LightGraphs.a_star(g.graph, o, d, g.weights)
63+
catch
64+
# Error exception will be thrown if path does not exist from origin to destination node
65+
end
66+
end
67+
elseif algorithm == :dijkstra
68+
for (o, d) in o_d_indices
69+
try
70+
state = LightGraphs.dijkstra_shortest_paths(g.graph, o, g.weights)
71+
LightGraphs.enumerate_paths(state, d)
72+
catch
73+
# Error exception will be thrown if path does not exist from origin to destination node
74+
end
75+
end
76+
end
77+
end
78+
79+
function extract(bmark_trial)
80+
io = IOBuffer()
81+
show(io, "text/plain", bmark_trial)
82+
83+
s = String(take!(io))
84+
s = split.((split(s, "\n")), ":")
85+
86+
result = Dict{String,String}()
87+
88+
for item in s
89+
if length(item) >= 2
90+
result[strip(item[1])] = strip(item[2])
91+
end
92+
end
93+
@info "Extracted benchmark result: $result"
94+
return result
95+
end
96+
97+
"""
98+
Define experiment
99+
"""
100+
101+
n_paths = [1, 10, 100, 1000, 10000]
102+
o_d_indices = OrderedDict()
103+
o_d_nodes = OrderedDict()
104+
105+
losm_nodes = collect(keys(g_losm.nodes))
106+
osmx_nodes = collect(keys(g_osmx.nodes))
107+
common_nodes = intersect(losm_nodes, osmx_nodes)
108+
109+
for n in n_paths
110+
rand_o_d_indices = rand(1:length(common_nodes), n, 2)
111+
o_d_indices[n] = [[o, d] for (o, d) in eachrow(rand_o_d_indices) if o != d]
112+
o_d_nodes[n] = [[common_nodes[o], common_nodes[d]] for (o, d) in eachrow(rand_o_d_indices) if o != d]
113+
end
114+
115+
"""
116+
Run experiment
117+
"""
118+
results = Dict(:dijkstra => DefaultDict(Dict), :astar => DefaultDict(Dict))
119+
120+
results[:dijkstra][1][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[1], :dijkstra))
121+
results[:dijkstra][1][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[1], :dijkstra))
122+
results[:dijkstra][1][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[1], :dijkstra))
123+
results[:dijkstra][1][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[1], :dijkstra))
124+
125+
results[:dijkstra][10][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[10], :dijkstra))
126+
results[:dijkstra][10][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[10], :dijkstra))
127+
results[:dijkstra][10][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[10], :dijkstra))
128+
results[:dijkstra][10][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[10], :dijkstra))
129+
130+
results[:dijkstra][100][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[100], :dijkstra))
131+
results[:dijkstra][100][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[100], :dijkstra))
132+
results[:dijkstra][100][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[100], :dijkstra))
133+
results[:dijkstra][100][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[100], :dijkstra))
134+
135+
results[:dijkstra][1000][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[1000], :dijkstra))
136+
results[:dijkstra][1000][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[1000], :dijkstra))
137+
results[:dijkstra][1000][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[1000], :dijkstra))
138+
results[:dijkstra][1000][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[1000], :dijkstra))
139+
140+
results[:dijkstra][10000][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[10000], :dijkstra))
141+
results[:dijkstra][10000][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[10000], :dijkstra))
142+
results[:dijkstra][10000][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[10000], :dijkstra))
143+
results[:dijkstra][10000][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[10000], :dijkstra))
144+
145+
results[:astar][1][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[1], :astar))
146+
results[:astar][1][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[1], :astar))
147+
results[:astar][1][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[1], :astar))
148+
results[:astar][1][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[1], :astar))
149+
150+
results[:astar][10][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[10], :astar))
151+
results[:astar][10][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[10], :astar))
152+
results[:astar][10][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[10], :astar))
153+
results[:astar][10][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[10], :astar))
154+
155+
results[:astar][100][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[100], :astar))
156+
results[:astar][100][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[100], :astar))
157+
results[:astar][100][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[100], :astar))
158+
results[:astar][100][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[100], :astar))
159+
160+
results[:astar][1000][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[1000], :astar))
161+
results[:astar][1000][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[1000], :astar))
162+
results[:astar][1000][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[1000], :astar))
163+
results[:astar][1000][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[1000], :astar))
164+
165+
results[:astar][10000][:losm] = extract(@benchmark losm_shortest_path(g_losm, o_d_nodes[10000], :astar))
166+
results[:astar][10000][:losm_precompute] = extract(@benchmark losm_shortest_path(g_losm_precompute, o_d_nodes[10000], :astar))
167+
results[:astar][10000][:osmx] = extract(@benchmark osmx_shortest_path(g_osmx, o_d_nodes[10000], :astar))
168+
results[:astar][10000][:lg] = extract(@benchmark lg_shortest_path(g_losm, o_d_indices[10000], :astar))
169+
170+
"""
171+
Export Results
172+
"""
173+
174+
open(joinpath(@__DIR__, "benchmark_results.json"), "w") do io
175+
write(io, json(results))
176+
end

0 commit comments

Comments
 (0)