Skip to content

Commit 77dd2c3

Browse files
committed
Merge branch 'DeloitteDigitalAPAC-master'
2 parents 5d45dfb + 4eae631 commit 77dd2c3

File tree

11 files changed

+92
-55
lines changed

11 files changed

+92
-55
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "LightOSM"
22
uuid = "d1922b25-af4e-4ba3-84af-fe9bea896051"
33
authors = ["Jack Chan <[email protected]>"]
4-
version = "0.1.19"
4+
version = "0.1.20"
55

66
[deps]
77
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"

docs/src/testing_use.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Creating the graph relies on some LightOSM internals to populate all other field
6666
U = LightOSM.DEFAULT_OSM_INDEX_TYPE
6767
T = LightOSM.DEFAULT_OSM_ID_TYPE
6868
W = LightOSM.DEFAULT_OSM_EDGE_WEIGHT_TYPE
69-
g = OSMGraph{U,T,W}(nodes=nodes, highways=ways)
69+
g = OSMGraph{U,T,W}(nodes=nodes, ways=ways)
7070
LightOSM.add_node_and_edge_mappings!(g)
7171
LightOSM.add_weights!(g, :distance) # or :time
7272
LightOSM.add_graph!(g, :static) # or any desired graph type
@@ -99,7 +99,7 @@ See [`Restriction`](@ref) for more information on `Restriction` objects.
9999
And then when instantiating the `OSMGraph`
100100

101101
```julia
102-
g = OSMGraph{U,T,W}(nodes=nodes, highways=ways, restrictions=restrictions)
102+
g = OSMGraph{U,T,W}(nodes=nodes, ways=ways, restrictions=restrictions)
103103
LightOSM.add_indexed_restrictions!(g)
104104
```
105105

src/graph.jl

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,15 @@ end
173173

174174

175175
"""
176-
Adds mappings between nodes, edges and highways to `OSMGraph`.
176+
Adds mappings between nodes, edges and ways to `OSMGraph`.
177177
"""
178178
function add_node_and_edge_mappings!(g::OSMGraph{U,T,W}) where {U <: Integer,T <: Integer,W <: Real}
179-
for (way_id, way) in g.highways
179+
for (way_id, way) in g.ways
180180
@inbounds for (i, node_id) in enumerate(way.nodes)
181-
if haskey(g.node_to_highway, node_id)
182-
push!(g.node_to_highway[node_id], way_id)
181+
if haskey(g.node_to_way, node_id)
182+
push!(g.node_to_way[node_id], way_id)
183183
else
184-
g.node_to_highway[node_id] = [way_id]
184+
g.node_to_way[node_id] = [way_id]
185185
end
186186

187187
if i < length(way.nodes)
@@ -193,16 +193,16 @@ function add_node_and_edge_mappings!(g::OSMGraph{U,T,W}) where {U <: Integer,T <
193193
d = way.nodes[i]
194194
end
195195

196-
g.edge_to_highway[[o, d]] = way_id
196+
g.edge_to_way[[o, d]] = way_id
197197

198198
if !way.tags["oneway"]::Bool
199-
g.edge_to_highway[[d, o]] = way_id
199+
g.edge_to_way[[d, o]] = way_id
200200
end
201201
end
202202
end
203203
end
204204

205-
@assert(length(g.nodes) == length(g.node_to_highway), "Data quality issue: number of graph nodes ($(length(g.nodes::Dict{T,Node{T}}))) not equal to set of nodes extracted from highways ($(length(g.node_to_highway)))")
205+
@assert(length(g.nodes) == length(g.node_to_way), "Data quality issue: number of graph nodes ($(length(g.nodes::Dict{T,Node{T}}))) not equal to set of nodes extracted from ways ($(length(g.node_to_way)))")
206206
g.node_to_index = OrderedDict{T,U}(n => i for (i, n) in enumerate(collect(keys(g.nodes))))
207207
g.index_to_node = OrderedDict{U,T}(i => n for (n, i) in g.node_to_index)
208208
end
@@ -215,7 +215,7 @@ function add_node_tags!(g::OSMGraph)
215215
@inline function _roundedmean(hwys, subkey, T)
216216
total = zero(T)
217217
for id in hwys
218-
total += g.highways[id].tags[subkey]::T
218+
total += g.ways[id].tags[subkey]::T
219219
end
220220
return round(T, total/length(hwys))
221221
end
@@ -224,10 +224,10 @@ function add_node_tags!(g::OSMGraph)
224224
L = DEFAULT_OSM_LANES_TYPE
225225

226226
for (id, data) in g.nodes
227-
highways = g.node_to_highway[id]
227+
ways = g.node_to_way[id]
228228
tags_dict = g.nodes[id].tags::Dict{String, Any}
229-
tags_dict["maxspeed"] = _roundedmean(highways, "maxspeed", M)
230-
tags_dict["lanes"] = _roundedmean(highways, "lanes", L)
229+
tags_dict["maxspeed"] = _roundedmean(ways, "maxspeed", M)
230+
tags_dict["lanes"] = _roundedmean(ways, "lanes", L)
231231
push!(g.node_coordinates, [data.location.lat, data.location.lon])
232232
end
233233
end
@@ -236,15 +236,15 @@ end
236236
Finds the adjacent node id on a given way.
237237
"""
238238
function adjacent_node(g::OSMGraph, node::T, way::T)::Union{T,Vector{<:T}} where T <: Integer
239-
way_nodes = g.highways[way].nodes
239+
way_nodes = g.ways[way].nodes
240240
if node == way_nodes[1]
241241
return way_nodes[2]
242242
elseif node == way_nodes[end]
243243
return way_nodes[length(way_nodes) - 1]
244244
else
245245
idx = findfirst(isequal(node), way_nodes)
246-
is_oneway = g.highways[way].tags["oneway"]
247-
is_reverseway = g.highways[way].tags["reverseway"]
246+
is_oneway = g.ways[way].tags["oneway"]
247+
is_reverseway = g.ways[way].tags["reverseway"]
248248
if is_oneway && !is_reverseway
249249
return way_nodes[idx + 1]
250250
elseif is_oneway && is_reverseway
@@ -274,7 +274,7 @@ function add_indexed_restrictions!(g::OSMGraph{U,T,W}) where {U <: Integer,T <:
274274
elseif r.is_exclusive
275275
# only_right_turn, only_left_turn, only_straight_on
276276
# Multiple to_ways, e.g. if only_right_turn, then left turn and straight on are restricted
277-
all_ways = g.node_to_highway[r.via_node]
277+
all_ways = g.node_to_way[r.via_node]
278278
permitted_to_way = r.to_way
279279
restricted_to_ways = [w for w in all_ways if w != r.from_way && w != permitted_to_way]
280280
else
@@ -294,14 +294,14 @@ function add_indexed_restrictions!(g::OSMGraph{U,T,W}) where {U <: Integer,T <:
294294
end
295295

296296
elseif r.type == "via_way"
297-
via_way_nodes_list = [g.highways[w].nodes for w in r.via_way::Vector{T}]
297+
via_way_nodes_list = [g.ways[w].nodes for w in r.via_way::Vector{T}]
298298
via_way_nodes = join_arrays_on_common_trailing_elements(via_way_nodes_list...)::Vector{T}
299299

300-
from_way_nodes = g.highways[r.from_way].nodes
300+
from_way_nodes = g.ways[r.from_way].nodes
301301
from_via_intersection_node = first_common_trailing_element(from_way_nodes, via_way_nodes)
302302
from_node = adjacent_node(g, from_via_intersection_node, r.from_way)::T
303303

304-
to_way_nodes = g.highways[r.to_way].nodes
304+
to_way_nodes = g.ways[r.to_way].nodes
305305
to_via_intersection_node = first_common_trailing_element(to_way_nodes, via_way_nodes)
306306
to_node = adjacent_node(g, to_via_intersection_node, r.to_way)::T
307307

@@ -321,14 +321,14 @@ end
321321
Adds edge weights to `OSMGraph`.
322322
"""
323323
function add_weights!(g::OSMGraph, weight_type::Symbol=:distance)
324-
n_edges = length(g.edge_to_highway)
324+
n_edges = length(g.edge_to_way)
325325
o_indices = Vector{Int}(undef, n_edges) # edge origin node indices
326326
d_indices = Vector{Int}(undef, n_edges) # edge destination node indices
327327
weights = Vector{Float64}(undef, n_edges)
328328

329329
W = DEFAULT_OSM_EDGE_WEIGHT_TYPE
330330

331-
@inbounds for (i, edge) in enumerate(keys(g.edge_to_highway))
331+
@inbounds for (i, edge) in enumerate(keys(g.edge_to_way))
332332
o_loc = g.nodes[edge[1]].location
333333
d_loc = g.nodes[edge[2]].location
334334

@@ -337,12 +337,12 @@ function add_weights!(g::OSMGraph, weight_type::Symbol=:distance)
337337

338338
dist = distance(o_loc, d_loc, :haversine)
339339
if weight_type == :time || weight_type == :lane_efficiency
340-
highway = g.edge_to_highway[edge]
341-
maxspeed = g.highways[highway].tags["maxspeed"]::DEFAULT_OSM_MAXSPEED_TYPE
340+
highway = g.edge_to_way[edge]
341+
maxspeed = g.ways[highway].tags["maxspeed"]::DEFAULT_OSM_MAXSPEED_TYPE
342342
if weight_type == :time
343343
weight = dist / maxspeed
344344
else
345-
lanes = g.highways[highway].tags["lanes"]::DEFAULT_OSM_LANES_TYPE
345+
lanes = g.ways[highway].tags["lanes"]::DEFAULT_OSM_LANES_TYPE
346346
lane_efficiency = get(LANE_EFFICIENCY, lanes, 1.0)
347347
weight = dist / (maxspeed * lane_efficiency)
348348
end
@@ -387,21 +387,21 @@ function trim_to_largest_connected_component!(g::OSMGraph{U, T, W}, graph, weigh
387387
sort!(cc, by=x -> length(x), rev=true)
388388
indices_to_delete = flatten(cc[2:end])
389389
nodes_to_delete = [g.index_to_node[i] for i in indices_to_delete]
390-
highways_to_delete = Set(flatten([g.node_to_highway[n] for n in nodes_to_delete]))
390+
ways_to_delete = Set(flatten([g.node_to_way[n] for n in nodes_to_delete]))
391391

392392
delete_from_dict!(g.nodes, nodes_to_delete, :on_key)
393393
delete_from_dict!(g.node_to_index, nodes_to_delete, :on_key)
394-
delete_from_dict!(g.node_to_highway, nodes_to_delete, :on_key)
394+
delete_from_dict!(g.node_to_way, nodes_to_delete, :on_key)
395395
delete_from_dict!(g.index_to_node, indices_to_delete, :on_key)
396-
delete_from_dict!(g.highways, highways_to_delete, :on_key)
397-
delete_from_dict!(g.edge_to_highway, highways_to_delete, :on_value)
396+
delete_from_dict!(g.ways, ways_to_delete, :on_key)
397+
delete_from_dict!(g.edge_to_way, ways_to_delete, :on_value)
398398

399399
for (id, r) in g.restrictions
400400
if r.via_node !== nothing
401401
!isempty(intersect([r.via_node::T], nodes_to_delete)) && delete!(g.restrictions, id)
402402
elseif r.via_way !== nothing
403403
ways = Set([r.from_way, r.to_way, r.via_way::Vector{T}...])
404-
!isempty(intersect(ways, highways_to_delete)) && delete!(g.restrictions, id)
404+
!isempty(intersect(ways, ways_to_delete)) && delete!(g.restrictions, id)
405405
end
406406
end
407407

src/nearest_node.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function nearest_node(g::OSMGraph{U, T, W},
2121
cartesian_locations = LightOSM.to_cartesian(points)
2222
idxs, dists = knn(g.kdtree, cartesian_locations, n_neighbours, true)
2323
neighbours = [[g.index_to_node[j] for j in i] for i in idxs]
24-
dists = [d for d in dists]
24+
dists = collect(dists)
2525
return neighbours, dists
2626
end
2727

src/parse.jl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ is_restriction(tags::AbstractDict)::Bool = get(tags, "type", "") == "restriction
122122
"""
123123
Determine if a restriction is valid and has usable data.
124124
"""
125-
function is_valid_restriction(members::AbstractArray, highways::AbstractDict{T,Way{T}})::Bool where T <: Integer
125+
function is_valid_restriction(members::AbstractArray, ways::AbstractDict{T,Way{T}})::Bool where T <: Integer
126126
role_counts = DefaultDict(0)
127127
role_type_counts = DefaultDict(0)
128128
ways_set = Set{Int}()
@@ -135,7 +135,7 @@ function is_valid_restriction(members::AbstractArray, highways::AbstractDict{T,W
135135
role = member["role"]
136136

137137
if type == "way"
138-
if !haskey(highways, id) || id in ways_set
138+
if !haskey(ways, id) || id in ways_set
139139
# Cannot process missing and duplicate from/via/to ways
140140
return false
141141
else
@@ -164,9 +164,9 @@ function is_valid_restriction(members::AbstractArray, highways::AbstractDict{T,W
164164
end
165165

166166
to_way = ways_mapping["to"][1]
167-
trailing_to_way_nodes = trailing_elements(highways[to_way].nodes)
167+
trailing_to_way_nodes = trailing_elements(ways[to_way].nodes)
168168
from_way = ways_mapping["from"][1]
169-
trailing_from_way_nodes = trailing_elements(highways[from_way].nodes)
169+
trailing_from_way_nodes = trailing_elements(ways[from_way].nodes)
170170

171171
if via_node isa Integer && (!(via_node in trailing_to_way_nodes) || !(via_node in trailing_from_way_nodes))
172172
# Via node must be a trailing node on to_way and from_way
@@ -177,7 +177,7 @@ function is_valid_restriction(members::AbstractArray, highways::AbstractDict{T,W
177177
try
178178
# Via way trailing nodes must intersect with all to_ways and from_ways
179179
via_ways = ways_mapping["via"]
180-
via_way_nodes_list = [highways[w].nodes for w in via_ways]
180+
via_way_nodes_list = [ways[w].nodes for w in via_ways]
181181
via_way_nodes = join_arrays_on_common_trailing_elements(via_way_nodes_list...)
182182
trailing_via_way_nodes = trailing_elements(via_way_nodes)
183183

@@ -202,7 +202,7 @@ function parse_osm_network_dict(osm_network_dict::AbstractDict, network_type::Sy
202202
T = DEFAULT_OSM_ID_TYPE
203203
W = DEFAULT_OSM_EDGE_WEIGHT_TYPE
204204

205-
highways = Dict{T,Way{T}}()
205+
ways = Dict{T,Way{T}}()
206206
highway_nodes = Set{Int}([])
207207
for way in osm_network_dict["way"]
208208
if haskey(way, "tags") && haskey(way, "nodes")
@@ -215,7 +215,7 @@ function parse_osm_network_dict(osm_network_dict::AbstractDict, network_type::Sy
215215
nds = way["nodes"]
216216
union!(highway_nodes, nds)
217217
id = way["id"]
218-
highways[id] = Way(id, nds, tags)
218+
ways[id] = Way(id, nds, tags)
219219
elseif is_railway(tags) && matches_network_type(tags, network_type)
220220
tags["rail_type"] = haskey(tags,"railway") ? tags["railway"] : "unknown"
221221
tags["electrified"] = haskey(tags,"electrified") ? tags["electrified"] : "unknown"
@@ -229,7 +229,7 @@ function parse_osm_network_dict(osm_network_dict::AbstractDict, network_type::Sy
229229
nds = way["nodes"]
230230
union!(highway_nodes, nds)
231231
id = way["id"]
232-
highways[id] = Way(id, nds, tags)
232+
ways[id] = Way(id, nds, tags)
233233
end
234234
end
235235
end
@@ -253,7 +253,7 @@ function parse_osm_network_dict(osm_network_dict::AbstractDict, network_type::Sy
253253
tags = relation["tags"]
254254
members = relation["members"]
255255

256-
if is_restriction(tags) && is_valid_restriction(members, highways)
256+
if is_restriction(tags) && is_valid_restriction(members, ways)
257257
restriction_kwargs = DefaultDict(Vector)
258258
for member in members
259259
key = "$(member["role"])_$(member["type"])"
@@ -277,7 +277,7 @@ function parse_osm_network_dict(osm_network_dict::AbstractDict, network_type::Sy
277277
end
278278
end
279279
end
280-
return OSMGraph{U,T,W}(nodes=nodes, highways=highways, restrictions=restrictions)
280+
return OSMGraph{U,T,W}(nodes=nodes, ways=ways, restrictions=restrictions)
281281
end
282282

283283
"""

src/simplification.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,22 @@ function path_to_endpoint(g::AbstractGraph, (ep, ep_succ)::Tuple{T,T}) where {T<
4545
return path
4646
end
4747

48+
"""
49+
Return the total weight of a path given as a Vector of Ids.
50+
"""
4851
function total_weight(g::OSMGraph, path::Vector{<:Integer})
4952
sum((g.weights[path[[i, i+1]]...] for i in 1:length(path)-1))
5053
end
5154

55+
function ways_in_path(g::OSMGraph, path::Vector{<:Integer})
56+
ways = Set{Int}()
57+
for i in 1:(length(path)-1)
58+
edge = [g.index_to_node[path[i]], g.index_to_node[path[i+1]]]
59+
push!(ways, g.edge_to_way[edge])
60+
end
61+
return collect(ways)
62+
end
63+
5264
"""
5365
Build a new graph which simplifies the topology of osmg.graph.
5466
The resulting graph only contains intersections and dead ends from the original graph.
@@ -79,7 +91,6 @@ function simplify_graph(osmg::OSMGraph{U, T, W}) where {U, T, W}
7991
u = index_mapping[first(path)]
8092
v = index_mapping[last(path)]
8193
path_weight = total_weight(osmg, path)
82-
8394
if add_edge!(graph, (u, v))
8495
key = 0
8596
weights[u, v] = path_weight
@@ -92,12 +103,17 @@ function simplify_graph(osmg::OSMGraph{U, T, W}) where {U, T, W}
92103
edges[u,v,key] = path
93104
end
94105

106+
edge_to_way = Dict{NTuple{3,U}, Vector{T}}()
107+
for (edge, path) in edges
108+
edge_to_way[edge] = ways_in_path(osmg, path)
109+
end
110+
95111
return SimplifiedOSMGraph(
96112
osmg,
97113
node_coordinates,
98114
node_to_index,
99115
index_to_node,
100-
Dict{Vector{T}, Vector{T}}(),
116+
edge_to_way,
101117
graph,
102118
edges,
103119
weights,

src/types.jl

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ Container for storing OpenStreetMap node, way, relation and graph related obejct
8686
`U <: Integer,T <: Integer,W <: Real`
8787
- `nodes::Dict{T,Node{T}}`: Mapping of node ids to node objects.
8888
- `node_coordinates::Vector{Vector{W}}`: Vector of node coordinates [[lat, lon]...], indexed by graph vertices.
89-
- `highways::Dict{T,Way{T}}`: Mapping of way ids to way objects.
89+
- `ways::Dict{T,Way{T}}`: Mapping of way ids to way objects. Previously called `highways`.
9090
- `node_to_index::OrderedDict{T,U}`: Mapping of node ids to graph vertices.
9191
- `index_to_node::OrderedDict{U,T}`: Mapping of graph vertices to node ids.
92-
- `node_to_highway::Dict{T,Vector{T}}`: Mapping of node ids to vector of way ids.
93-
- `edge_to_highway::Dict{Vector{T},T}`: Mapping of edges (adjacent node pairs) to way ids.
92+
- `node_to_way::Dict{T,Vector{T}}`: Mapping of node ids to vector of way ids. Previously called `node_to_highway`.
93+
- `edge_to_way::Dict{Vector{T},T}`: Mapping of edges (adjacent node pairs) to way ids. Previously called `edge_to_highway`.
9494
- `restrictions::Dict{T,Restriction{T}}`: Mapping of relation ids to restriction objects.
9595
- `indexed_restrictions::Union{DefaultDict{U,Vector{MutableLinkedList{U}}},Nothing}`: Mapping of via node ids to ordered sequences of restricted node ids.
9696
- `graph::Union{AbstractGraph,Nothing}`: Either DiGraph, StaticDiGraph, SimpleWeightedDiGraph or MetaDiGraph.
@@ -104,11 +104,11 @@ abstract type AbstractOSMGraph end
104104
@with_kw mutable struct OSMGraph{U <: Integer,T <: Integer,W <: Real} <: AbstractOSMGraph
105105
nodes::Dict{T,Node{T}} = Dict{T,Node{T}}()
106106
node_coordinates::Vector{Vector{W}} = Vector{Vector{W}}() # needed for astar heuristic
107-
highways::Dict{T,Way{T}} = Dict{T,Way{T}}()
107+
ways::Dict{T,Way{T}} = Dict{T,Way{T}}()
108108
node_to_index::OrderedDict{T,U} = OrderedDict{T,U}()
109109
index_to_node::OrderedDict{U,T} = OrderedDict{U,T}()
110-
node_to_highway::Dict{T,Vector{T}} = Dict{T,Vector{T}}()
111-
edge_to_highway::Dict{Vector{T},T} = Dict{Vector{T},T}()
110+
node_to_way::Dict{T,Vector{T}} = Dict{T,Vector{T}}()
111+
edge_to_way::Dict{Vector{T},T} = Dict{Vector{T},T}()
112112
restrictions::Dict{T,Restriction{T}} = Dict{T,Restriction{T}}()
113113
indexed_restrictions::Union{DefaultDict{U,Vector{MutableLinkedList{U}}},Nothing} = nothing
114114
graph::Union{AbstractGraph,Nothing} = nothing
@@ -123,13 +123,26 @@ struct SimplifiedOSMGraph{U <: Integer,T <: Integer,W <: Real} <: AbstractOSMGra
123123
node_coordinates::Vector{Vector{W}} # needed for astar heuristic
124124
node_to_index::OrderedDict{T,U}
125125
index_to_node::OrderedDict{U,T}
126-
edge_to_highway::Dict{Vector{T},Vector{T}}
126+
edge_to_way::Dict{NTuple{3,U},Vector{T}}
127127
graph::Union{AbstractGraph,Nothing}
128128
edges::Dict{NTuple{3, U}, Vector{U}}
129129
weights::Union{SparseMatrixCSC{W,U},Nothing}
130130
dijkstra_states::Union{Vector{Vector{U}},Nothing}
131131
end
132132

133+
function Base.getproperty(g::OSMGraph, field::Symbol)
134+
# Ensure renaming of "highways" to "ways" is backwards compatible
135+
if field === :highways
136+
return getfield(g, :ways)
137+
elseif field === :node_to_highway
138+
return getfield(g, :node_to_way)
139+
elseif field === :edge_to_highway
140+
return getfield(g, :edge_to_way)
141+
else
142+
return getfield(g, field)
143+
end
144+
end
145+
133146
"""
134147
OpenStreetMap building polygon.
135148

0 commit comments

Comments
 (0)