Skip to content

Commit 22b4d0c

Browse files
committed
RTree: support iteration (all elements)
1 parent e3921ec commit 22b4d0c

3 files changed

Lines changed: 76 additions & 1 deletion

File tree

src/abstract.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ regiontype(si::SpatialIndex) = regiontype(typeof(si))
4141
Base.length(si::SpatialIndex) = si.nelems
4242
Base.isempty(si::SpatialIndex) = length(si) == 0
4343

44+
# SpatialIndex data element iteration support
45+
Base.IteratorEltype(::Type{<:SpatialIndex}) = Base.HasEltype()
46+
Base.IteratorSize(::Type{<:SpatialIndex}) = Base.HasLength()
47+
# concrete SpatialIndex types should implement iterate()
48+
4449
# arbitrary spatial elements support
4550

4651
# Type trait for supporting id() method

src/rtree/query.jl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,53 @@ function _isempty(node::Node, region::Region{T,N}) where {T,N}
5454
return true
5555
end
5656

57+
# the RTreeIterator/RTreeRegionQueryIterator state
58+
# FIXME can mark whether the MBR of the node satisfies the query, so all
59+
# its subnodes and data elements need not to be checked
60+
struct RTreeIteratorState{T,N,V}
61+
leaf::Leaf{T,N,V} # current leaf node
62+
indices::Vector{Int} # indices of the nodes (in their parents) in the current subtree
63+
end
64+
65+
# get the current data element pointed by `RTreeIteratorState`
66+
Base.get(state::RTreeIteratorState) = @inbounds(state.leaf[state.indices[1]])
67+
68+
# iterate all R-tree data elements
69+
function Base.iterate(tree::RTree)
70+
isempty(tree) && return nothing
71+
node = tree.root
72+
indices = fill(1, height(tree))
73+
# get the first leaf
74+
while level(node) > 0
75+
node = node[1]
76+
end
77+
state = RTreeIteratorState(node, indices)
78+
return get(state), state # first element of the first leaf
79+
end
80+
81+
function Base.iterate(tree::RTree, state::RTreeIteratorState)
82+
@inbounds if state.indices[1] < length(state.leaf) # fast branch: next data element in the same leaf
83+
state.indices[1] += 1
84+
return get(state), state
85+
end
86+
# leaf iterations is done, go up until the first non-visited branch
87+
node = state.leaf
88+
while state.indices[level(node) + 1] >= length(node)
89+
hasparent(node) || return nothing # returned to root, iteration finished
90+
node = parent(node)
91+
end
92+
# go down into the first leaf of the new subtree
93+
ix = state.indices[level(node) + 1] += 1
94+
node = node[ix]
95+
@inbounds while true
96+
state.indices[level(node) + 1] = 1
97+
level(node) == 0 && break
98+
node = node[1]
99+
end
100+
new_state = RTreeIteratorState(node, state.indices)
101+
return get(new_state), new_state
102+
end
103+
57104
#=
58105
TODO
59106

test/rtree.jl

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
@testset "RTree" begin
21
@testset "SpatialElem" begin
32
@test SI.check_hasmbr(SI.Rect{Float64, 2}, SpatialElem{Float64, 2, Int, Int})
43
@test_throws ArgumentError SI.check_hasmbr(SI.Rect{Float64, 2}, Int)
@@ -10,11 +9,13 @@
109
@test_throws ArgumentError SI.check_hasid(Int, SpatialElem{Float64, 2, Nothing, Int})
1110
end
1211

12+
@testset "RTree" begin
1313
@testset "Basic Operations" begin
1414
tree_vars = [SI.RTreeStar, SI.RTreeLinear, SI.RTreeQuadratic]
1515
@testset "RTree{Float,2,Int32,Int}(variant=$tree_var)" for tree_var in tree_vars
1616
ambr = SI.Rect((0.0, 0.0), (0.0, 0.0))
1717
bmbr = SI.Rect((0.0, 1.0), (0.0, 1.0))
18+
cmbr = SI.Rect((0.5, 0.5), (0.5, 0.6))
1819

1920
tree = RTree{Float64, 2}(Int32, Int, variant=tree_var)
2021
@test tree isa RTree{Float64, 2, SpatialElem{Float64, 2, Int32, Int}}
@@ -32,6 +33,9 @@ end
3233
@test SI.check(tree)
3334
@test_throws KeyError delete!(tree, SI.empty(SI.regiontype(tree)), 1)
3435
@test SI.check(tree)
36+
@test iterate(tree) === nothing
37+
@test collect(tree) == eltype(tree)[]
38+
@test typeof(collect(tree)) === Vector{eltype(tree)}
3539

3640
@test insert!(tree, ambr, 1, 2) === tree
3741
@test length(tree) == 1
@@ -43,18 +47,31 @@ end
4347
@test_throws KeyError delete!(tree, bmbr, 1)
4448
@test_throws KeyError delete!(tree, ambr, 2)
4549
@test SI.check(tree)
50+
@test collect(tree) == [SpatialElem(ambr, Int32(1), 2)]
4651

4752
@test insert!(tree, bmbr, 2, 2) === tree
4853
@test length(tree) == 2
4954
@test SI.height(tree) == 1
5055
@test isequal(SI.mbr(tree.root), SI.combine(ambr, bmbr))
5156
@test SI.check(tree)
57+
@test length(collect(tree)) == 2
5258
@test_throws KeyError delete!(tree, bmbr, 1)
5359
@test_throws KeyError delete!(tree, ambr, 2)
5460
@test_throws KeyError delete!(tree, SI.empty(SI.regiontype(tree)), 1)
5561
@test delete!(tree, ambr, 1) === tree
5662
@test length(tree) == 1
5763
@test SI.check(tree)
64+
65+
insert!(tree, ambr, 1, 2)
66+
insert!(tree, cmbr, 2, 3)
67+
@test length(tree) == 3
68+
@test_throws SpatialIndexException SI.check(tree) # duplicate ID=2 (b and c)
69+
@test delete!(tree, cmbr, 2) === tree
70+
@test length(tree) == 2
71+
# prepare tree with a(id=1), b(id=2), c(id=3)
72+
@test insert!(tree, cmbr, 3, 3) === tree
73+
@test length(tree) == 3
74+
@test SI.check(tree)
5875
end
5976

6077
@testset "RTree{Int,3,String,Nothing}(variant=$tree_var) (no id)" for tree_var in tree_vars
@@ -141,6 +158,12 @@ end
141158
end
142159
end
143160
end
161+
162+
@testset "iterate" begin
163+
all_elems = collect(tree)
164+
@test length(all_elems) == length(tree)
165+
@test eltype(all_elems) === eltype(tree)
166+
end
144167
end
145168

146169
@testset "bulk load" begin

0 commit comments

Comments
 (0)