@@ -10,118 +10,19 @@ import AbstractTrees
10
10
export query, do_query
11
11
export FlatNoTree
12
12
13
- # ## Interface
14
- # Interface definition for spatial tree types.
15
- # There is no abstract supertype here since it's impossible to enforce,
16
- # but we do have a few methods that are common to all spatial tree types.
13
+ # The spatial tree interface and its implementations are defined here.
14
+ include (" interface.jl" )
15
+ include (" implementations.jl" )
17
16
18
- """
19
- isspatialtree(tree)::Bool
20
-
21
- Return true if the object is a spatial tree, false otherwise.
22
-
23
- ## Implementation notes
24
-
25
- For type stability, if your spatial tree type is `MyTree`, you should define
26
- `isspatialtree(::Type{MyTree}) = true`, and `isspatialtree(::MyTree)` will forward
27
- to that method automatically.
28
- """
29
- isspatialtree (:: T ) where T = isspatialtree (T)
30
- isspatialtree (:: Type{<: Any} ) = false
31
-
32
-
33
- """
34
- getchild(node)
35
-
36
- Return an iterator over all the children of a node.
37
- This may be materialized if necessary or available,
38
- but can also be lazy (like a generator).
39
- """
40
- getchild (node) = AbstractTrees. children (node)
41
-
42
- """
43
- getchild(node, i)
44
-
45
- Return the `i`-th child of a node.
46
- """
47
- getchild (node, i) = getchild (node)[i]
48
-
49
- """
50
- nchild(node)
17
+ # Here we have some algorithms that use the spatial tree interface.
18
+ # The first file holds a single depth-first search, i.e., a single-tree query.
19
+ include (" depth_first_search.jl" )
51
20
52
- Return the number of children of a node.
53
- """
54
- nchild (node) = length (getchild (node))
55
-
56
- """
57
- isleaf(node)
58
-
59
- Return true if the node is a leaf node, i.e., there are no "children" below it.
60
- [`getchild`](@ref) should still work on leaf nodes, though, returning an iterator over the extents stored in the node - and similarly for `getnodes.`
61
- """
62
- isleaf (node) = error (" isleaf is not implemented for node type $(typeof (node)) " )
63
-
64
- """
65
- child_indices_extents(node)
66
-
67
- Return an iterator over the indices and extents of the children of a node.
68
-
69
- Each value of the iterator should take the form `(i, extent)`.
70
-
71
- This can only be invoked on leaf nodes!
72
- """
73
- function child_indices_extents (node)
74
- return zip (1 : nchild (node), getchild (node))
75
- end
76
-
77
- """
78
- node_extent(node)
79
-
80
- Return the extent like object of the node.
81
- Falls back to `GI.extent` by default, which falls back
82
- to `Extents.extent`.
83
-
84
- Generally, defining `Extents.extent(node)` is sufficient here, and you
85
- won't need to define this
86
-
87
- The reason we don't use that directly is to give users of this interface
88
- a way to define bounding boxes that are not extents, like spherical caps
89
- and other such things.
90
- """
91
- node_extent (node) = GI. extent (node)
92
-
93
- # ## Query functions
94
- # These are generic functions that work with any spatial tree type that implements the interface.
95
-
96
-
97
- """
98
- do_query(f, predicate, tree)
99
-
100
- Call `f(i)` for each index `i` in the tree that satisfies `predicate(extent(i))`.
101
-
102
- This is generic to anything that implements the SpatialTreeInterface, particularly the methods
103
- [`isleaf`](@ref), [`getchild`](@ref), and [`child_extents`](@ref).
104
- """
105
- function do_query (f:: F , predicate:: P , node:: N ) where {F, P, N}
106
- if isleaf (node)
107
- for (i, leaf_geometry_extent) in child_indices_extents (node)
108
- if predicate (leaf_geometry_extent)
109
- @controlflow f (i)
110
- end
111
- end
112
- else
113
- for child in getchild (node)
114
- if predicate (node_extent (child))
115
- @controlflow do_query (f, predicate, child)
116
- end
117
- end
118
- end
119
- end
120
- function do_query (predicate, node)
121
- a = Int[]
122
- do_query (Base. Fix1 (push!, a), predicate, node)
123
- return a
124
- end
21
+ # The second file holds a dual depth-first search, i.e., a dual-tree query.
22
+ # This iterates over two trees simultaneously, and is substantially more efficient
23
+ # than two separate single-tree queries since it can prune branches in tandem as it
24
+ # descends into the trees.
25
+ include (" dual_depth_first_search.jl" )
125
26
126
27
127
28
"""
@@ -155,94 +56,4 @@ sanitize_predicate(::GI.AbstractTrait, pred) = sanitize_predicate(GI.extent(pred
155
56
sanitize_predicate (pred:: Extents.Extent ) = Base. Fix1 (Extents. intersects, pred)
156
57
157
58
158
- """
159
- do_dual_query(f, predicate, node1, node2)
160
-
161
- Call `f(i1, i2)` for each index `i1` in `node1` and `i2` in `node2` that satisfies `predicate(extent(i1), extent(i2))`.
162
-
163
- This is generic to anything that implements the SpatialTreeInterface, particularly the methods
164
- [`isleaf`](@ref), [`getchild`](@ref), and [`child_extents`](@ref).
165
- """
166
- function do_dual_query (f:: F , predicate:: P , node1:: N1 , node2:: N2 ) where {F, P, N1, N2}
167
- if isleaf (node1) && isleaf (node2)
168
- # both nodes are leaves, so we can just iterate over the indices and extents
169
- for (i1, extent1) in child_indices_extents (node1)
170
- for (i2, extent2) in child_indices_extents (node2)
171
- if predicate (extent1, extent2)
172
- @controlflow f (i1, i2)
173
- end
174
- end
175
- end
176
- elseif isleaf (node1) # node2 is not a leaf, node1 is - recurse further into node2
177
- for child in getchild (node2)
178
- if predicate (node_extent (node1), node_extent (child))
179
- @controlflow do_dual_query (f, predicate, node1, child)
180
- end
181
- end
182
- elseif isleaf (node2) # node1 is not a leaf, node2 is - recurse further into node1
183
- for child in getchild (node1)
184
- if predicate (node_extent (child), node_extent (node2))
185
- @controlflow do_dual_query (f, predicate, child, node2)
186
- end
187
- end
188
- else # neither node is a leaf, recurse into both children
189
- for child1 in getchild (node1)
190
- for child2 in getchild (node2)
191
- if predicate (node_extent (child1), node_extent (child2))
192
- @controlflow do_dual_query (f, predicate, child1, child2)
193
- end
194
- end
195
- end
196
- end
197
- end
198
-
199
- # Finally, here's a sample implementation of the interface for STRtrees
200
-
201
- using SortTileRecursiveTree: STRtree, STRNode, STRLeafNode
202
-
203
- isspatialtree (:: Type{<: STRtree} ) = true
204
- nchild (tree:: STRtree ) = nchild (tree. rootnode)
205
- getchild (tree:: STRtree ) = getchild (tree. rootnode)
206
- getchild (tree:: STRtree , i) = getchild (tree. rootnode, i)
207
- isleaf (tree:: STRtree ) = isleaf (tree. rootnode)
208
- child_indices_extents (tree:: STRtree ) = child_indices_extents (tree. rootnode)
209
-
210
- isspatialtree (:: Type{<: STRNode} ) = true
211
- nchild (node:: STRNode ) = length (node. children)
212
- getchild (node:: STRNode ) = node. children
213
- getchild (node:: STRNode , i) = node. children[i]
214
- isleaf (node:: STRNode ) = false # STRNodes are not leaves by definition
215
-
216
- isspatialtree (:: Type{<: STRLeafNode} ) = true
217
- isleaf (node:: STRLeafNode ) = true
218
- child_indices_extents (node:: STRLeafNode ) = zip (node. indices, node. extents)
219
-
220
-
221
- """
222
- FlatNoTree(iterable_of_geoms_or_extents)
223
-
224
- Represents a flat collection with no tree structure, i.e., a brute force search.
225
- This is cost free, so particularly useful when you don't want to build a tree!
226
- """
227
- struct FlatNoTree{T}
228
- geometries:: T
229
- end
230
-
231
- isspatialtree (:: Type{<: FlatNoTree} ) = true
232
- isleaf (tree:: FlatNoTree ) = true
233
-
234
- # NOTE: use pairs instead of enumerate here, so that we can support
235
- # iterators or collections that define custom `pairs` methods.
236
- # This includes things like filtered extent lists, for example,
237
- # so we can perform extent thinning with no allocations.
238
- function child_indices_extents (tree:: FlatNoTree{T} ) where T
239
- # This test only applies at compile time and should be optimized away in any case.
240
- # And we can use multiple dispatch to override anyway, but it should be cost free I think.
241
- if applicable (Base. keys, T)
242
- return ((i, GI. extent (obj)) for (i, obj) in pairs (tree. geometries))
243
- else
244
- return ((i, GI. extent (obj)) for (i, obj) in enumerate (tree. geometries))
245
- end
246
- end
247
-
248
59
end # module SpatialTreeInterface
0 commit comments