Skip to content

Commit 42b0acf

Browse files
jarodlamcaptchanjack
authored andcommitted
Add nearest way and R-tree
1 parent bdbf786 commit 42b0acf

17 files changed

Lines changed: 371 additions & 18 deletions

Project.toml

Lines changed: 3 additions & 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 <jchan2@deloitte.com.au>"]
4-
version = "0.2.5"
4+
version = "0.2.6"
55

66
[deps]
77
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
@@ -15,6 +15,7 @@ Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
1515
QuickHeaps = "30b38841-0f52-47f8-a5f8-18d5d4064379"
1616
SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622"
1717
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
18+
SpatialIndexing = "d4ead438-fe20-5cc5-a293-4fd39a41b74c"
1819
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1920
StaticGraphs = "4c8beaf5-199b-59a0-a7f2-21d17de635b6"
2021
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
@@ -30,6 +31,7 @@ NearestNeighbors = "0.4.6"
3031
Parameters = "0.12.1"
3132
QuickHeaps = "0.1.1"
3233
SimpleWeightedGraphs = "1.2.0"
34+
SpatialIndexing = "0.1.3"
3335
StaticArrays = "1.4.6"
3436
StaticGraphs = "0.3.0"
3537
julia = "1"

docs/src/graph_utilities.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# [`Graph Utilities`](@ref) Methods
1+
# Graph Utilities
22

33
```@docs
44
index_to_node_id

docs/src/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ Pages = [
1111
"create_graph.md",
1212
"shortest_path.md",
1313
"nearest_node.md",
14+
"nearest_way.md",
1415
"download_buildings.md",
1516
"create_buildings.md",
16-
"geolocation.md"
17-
"graph_utilities.md"
17+
"geolocation.md",
18+
"graph_utilities.md",
1819
"defaults.md"
1920
]
2021
```

docs/src/nearest_node.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
```@docs
44
nearest_node
5+
nearest_nodes
56
```

docs/src/nearest_way.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Nearest Node
2+
3+
```@docs
4+
nearest_way
5+
nearest_ways
6+
nearest_point_on_way
7+
```

src/LightOSM.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ using HTTP
1414
using JSON
1515
using LightXML
1616
using StaticArrays
17+
using SpatialIndexing
1718

1819
export GeoLocation,
1920
OSMGraph,
2021
Node,
2122
Way,
23+
EdgePoint,
2224
Restriction,
2325
Building,
2426
PathAlgorithm,
@@ -46,6 +48,9 @@ export GeoLocation,
4648
path_from_parents,
4749
nearest_node,
4850
nearest_nodes,
51+
nearest_way,
52+
nearest_ways,
53+
nearest_point_on_way,
4954
download_osm_buildings,
5055
buildings_from_object,
5156
buildings_from_download,
@@ -75,6 +80,7 @@ include("graph_utilities.jl")
7580
include("traversal.jl")
7681
include("shortest_path.jl")
7782
include("nearest_node.jl")
83+
include("nearest_way.jl")
7884
include("buildings.jl")
7985
include("subgraph.jl")
8086

src/constants.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ optional.
228228
values are used instead based on the value of the `highway` way tag. If no
229229
`highway` way tag is available, the value for `"other"` is used. Unit is km/h.
230230
Default value:
231-
```julia
231+
```
232232
Dict(
233233
"motorway" => 100,
234234
"trunk" => 100,
@@ -244,7 +244,7 @@ optional.
244244
values are used instead based on the value of the `highway` way tag. If no
245245
`highway` way tag is available, the value for `"other"` is used.
246246
Default value:
247-
```julia
247+
```
248248
Dict(
249249
"motorway" => 3,
250250
"trunk" => 3,
@@ -259,7 +259,7 @@ optional.
259259
- `lane_efficiency::AbstractDict{<:Integer,<:Real}`: Gives the lane efficiency based on
260260
number of lanes. `1.0` is used for any number of lanes not specified here.
261261
Default value:
262-
```julia
262+
```
263263
LANE_EFFICIENCY = Dict(
264264
1 => 0.7,
265265
2 => 0.8,
@@ -270,12 +270,12 @@ optional.
270270
- `building_height_per_level::Integer`: If the `height` building tag is not available,
271271
it is calculated by multiplying this value by the number of levels from the
272272
`building:levels` tag. Unit is metres. Default value:
273-
```julia
273+
```
274274
4
275275
```
276276
- `max_building_levels::Integer`: If the `building:levels` tag is not available, a number
277277
is randomly chosen between 1 and this value. Default value:
278-
```julia
278+
```
279279
3
280280
```
281281
"""

src/geometry.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,51 @@ function bounding_box_from_point(point::GeoLocation, radius::Number)::NamedTuple
213213
bottom_left, top_right = calculate_location([point, point], [225, 45], [radius, radius])
214214
return (minlat = bottom_left.lat, minlon = bottom_left.lon, maxlat = top_right.lat, maxlon = top_right.lon)
215215
end
216+
217+
"""
218+
nearest_point_on_line(x1::T,
219+
y1::T,
220+
x2::T,
221+
y2::T,
222+
x::T,
223+
y::T
224+
)::Tuple{T,T,T} where {T <: AbstractFloat}
225+
226+
Finds the nearest position along a straight line to a given point.
227+
228+
# Arguments
229+
- `x1::T`, `y1::T`: Starting point of the line.
230+
- `x2::T`, `y2::T`: Ending point of the line.
231+
- `x::T`, `y::T`: Point to nearest position to.
232+
233+
# Returns
234+
- `::Tuple`:
235+
- `::T`: x-coordinate of nearest position.
236+
- `::T`: y-coordinate of nearest position.
237+
- `::T`: Position along the line, from 0 to 1.
238+
"""
239+
function nearest_point_on_line(x1::T,
240+
y1::T,
241+
x2::T,
242+
y2::T,
243+
x::T,
244+
y::T
245+
)::Tuple{T,T,T} where {T <: AbstractFloat}
246+
A = x - x1
247+
B = y - y1
248+
C = x2 - x1
249+
D = y2 - y1
250+
dot = A * C + B * D
251+
len_sq = C * C + D * D
252+
param = -one(T)
253+
if len_sq != 0 # in case of 0 length line
254+
param = dot / len_sq
255+
end
256+
if param < 0.0
257+
return (x1, y1, zero(T))
258+
elseif param > 1.0
259+
return (x2, y2, one(T))
260+
else
261+
return (x1 + param * C, y1 + param * D, param)
262+
end
263+
end

src/graph.jl

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function graph_from_object(osm_data_object::Union{XMLDocument,Dict};
5555
g.dijkstra_states = Vector{Vector{U}}(undef, length(g.nodes))
5656
end
5757

58-
add_kdtree!(g)
58+
add_kdtree_and_rtree!(g)
5959
@info "Created OSMGraph object with kwargs: network_type=$network_type, weight_type=$weight_type, graph_type=$graph_type, precompute_dijkstra_states=$precompute_dijkstra_states, largest_connected_component=$largest_connected_component"
6060
return g
6161
end
@@ -454,16 +454,76 @@ function add_dijkstra_states!(g::OSMGraph{U,T,W}) where {U <: Integer,T <: Integ
454454
set_dijkstra_state!(g, collect(vertices(g.graph)))
455455
end
456456

457+
"""
458+
get_cartesian_locations(g::OSMGraph)
459+
460+
Calculates the Cartesian location of all nodes in the graph.
461+
462+
Returns a 3-by-n matrix where each column is the `xyz` coordinates of a node. Column indices
463+
correspond to the `g.graph` vertex indices.
464+
"""
465+
function get_cartesian_locations(g::OSMGraph)
466+
node_locations = [index_to_node(g, index).location for index in 1:nv(g.graph)]
467+
return to_cartesian(node_locations)
468+
end
469+
470+
"""
471+
add_kdtree_and_rtree!(g::OSMGraph)
472+
473+
Adds k-d tree and R-tree to `OSMGraph` for finding nearest nodes and ways.
474+
"""
475+
function add_kdtree_and_rtree!(g::OSMGraph)
476+
cartesian_locations = get_cartesian_locations(g)
477+
add_kdtree!(g, cartesian_locations)
478+
add_rtree!(g, cartesian_locations)
479+
end
480+
457481
"""
458482
add_kdtree!(g::OSMGraph)
483+
add_kdtree!(g::OSMGraph, cartesian_locations::Matrix{Float64})
459484
460485
Adds KDTree to `OSMGraph` for finding nearest neighbours.
461486
"""
462-
function add_kdtree!(g::OSMGraph)
463-
node_locations = [node.location for (id, node) in g.nodes] # node locations must have the same order as node indices
464-
cartesian_locations = to_cartesian(node_locations)
487+
function add_kdtree!(g::OSMGraph, cartesian_locations::Matrix{Float64})
465488
g.kdtree = KDTree(cartesian_locations)
466489
end
490+
function add_kdtree!(g::OSMGraph)
491+
cartesian_locations = get_cartesian_locations(g)
492+
add_kdtree!(g, cartesian_locations)
493+
end
494+
495+
"""
496+
add_rtree!(g::OSMGraph)
497+
add_rtree!(g::OSMGraph, cartesian_locations::Matrix{Float64})
498+
499+
Adds an R-tree to `OSMGraph` for finding nearest ways.
500+
501+
# Warning
502+
Make sure to suppress outputs!
503+
Behaviour as of SpatialIndexing.jl 0.1.3 will print a line for every single OSM way, which
504+
will flood the terminal if not suppressed. Use with caution for now.
505+
"""
506+
function add_rtree!(g::OSMGraph{U,T,W}, cartesian_locations::Matrix{Float64}) where {U,T,W}
507+
# Get bounding box for every way ID
508+
way_ids = collect(keys(g.ways))
509+
data = map(way_ids) do way_id
510+
node_indices = node_id_to_index(g, g.ways[way_id].nodes)
511+
x = [cartesian_locations[1,i] for i in node_indices]
512+
y = [cartesian_locations[2,i] for i in node_indices]
513+
z = [cartesian_locations[3,i] for i in node_indices]
514+
min_pt = (minimum(x), minimum(y), minimum(z))
515+
max_pt = (maximum(x), maximum(y), maximum(z))
516+
return SpatialElem(SpatialIndexing.Rect(min_pt, max_pt), way_id, nothing)
517+
end
518+
519+
tree = RTree{Float64,3}(T, Nothing)
520+
SpatialIndexing.load!(tree, data)
521+
g.rtree = tree
522+
end
523+
function add_rtree!(g::OSMGraph)
524+
cartesian_locations = get_cartesian_locations(g)
525+
add_rtree!(g, cartesian_locations)
526+
end
467527

468528
"""
469529
get_graph_type(g::OSMGraph)

0 commit comments

Comments
 (0)