1+ module NaturalIndexing
2+
3+ import GeoInterface as GI
4+ import Extents
5+
6+ using .. SpatialTreeInterface
7+
8+ """
9+ NaturalLevel{E <: Extents.Extent}
10+
11+ A level in the natural tree. Stored in a vector in [`NaturalIndex`](@ref).
12+
13+ - `extents` is a vector of extents of the children of the node
14+ """
15+ struct NaturalLevel{E <: Extents.Extent }
16+ extents:: Vector{E} # child extents
17+ end
18+
19+ Base. show (io:: IO , level:: NaturalLevel ) = print (io, " NaturalLevel($(length (level. extents)) extents)" )
20+ Base. show (io:: IO , :: MIME"text/plain" , level:: NaturalLevel ) = Base. show (io, level)
21+
22+ """
23+ NaturalIndex{E <: Extents.Extent}
24+
25+ A natural tree index. Stored in a vector in [`NaturalIndex`](@ref).
26+
27+ - `nodecapacity` is the "spread", number of children per node
28+ - `extent` is the extent of the tree
29+ - `levels` is a vector of [`NaturalLevel`](@ref)s
30+ """
31+ struct NaturalIndex{E <: Extents.Extent }
32+ nodecapacity:: Int # "spread", number of children per node
33+ extent:: E
34+ levels:: Vector{NaturalLevel{E}}
35+ end
36+
37+ GI. extent (idx:: NaturalIndex ) = idx. extent
38+ Extents. extent (idx:: NaturalIndex ) = idx. extent
39+
40+ function Base. show (io:: IO , :: MIME"text/plain" , idx:: NaturalIndex )
41+ println (io, " NaturalIndex with $(length (idx. levels)) levels and $(idx. nodecapacity) children per node" )
42+ println (io, " extent: $(idx. extent) " )
43+ end
44+
45+ function Base. show (io:: IO , idx:: NaturalIndex )
46+ println (io, " NaturalIndex($(length (idx. levels)) levels, $(idx. extent) )" )
47+ end
48+
49+ function NaturalIndex (geoms; nodecapacity = 32 )
50+ e1 = GI. extent (first (geoms))
51+ E = typeof (e1)
52+ return NaturalIndex {E} (geoms; nodecapacity = nodecapacity)
53+ end
54+
55+ function NaturalIndex {E} (geoms; nodecapacity = 32 ) where E <: Extents.Extent
56+ last_level_extents = GI. extent .(geoms)
57+ return NaturalIndex {E} (last_level_extents; nodecapacity = nodecapacity)
58+ end
59+
60+ function NaturalIndex (last_level_extents:: Vector{E} ; nodecapacity = 32 ) where E <: Extents.Extent
61+ return NaturalIndex {E} (last_level_extents; nodecapacity = nodecapacity)
62+ end
63+
64+ function NaturalIndex {E} (last_level_extents:: Vector{E} ; nodecapacity = 32 ) where E <: Extents.Extent
65+ ngeoms = length (last_level_extents)
66+ last_level = NaturalLevel (last_level_extents)
67+
68+ nlevels = _number_of_levels (nodecapacity, ngeoms)
69+
70+ levels = Vector {NaturalLevel{E}} (undef, nlevels)
71+ levels[end ] = last_level
72+
73+ for level_index in (nlevels- 1 ): (- 1 ): 1
74+ prev_level = levels[level_index+ 1 ] # this is always instantiated
75+ nrects = _number_of_keys (nodecapacity, nlevels - (level_index), ngeoms)
76+ # @show level_index nrects
77+ extents = [
78+ begin
79+ start = (rect_index - 1 ) * nodecapacity + 1
80+ stop = min (start + nodecapacity - 1 , length (prev_level. extents))
81+ reduce (Extents. union, view (prev_level. extents, start: stop))
82+ end
83+ for rect_index in 1 : nrects
84+ ]
85+ levels[level_index] = NaturalLevel (extents)
86+ end
87+
88+ return NaturalIndex (nodecapacity, reduce (Extents. union, levels[1 ]. extents), levels)
89+
90+ end
91+
92+ function _number_of_keys (nodecapacity:: Int , level:: Int , ngeoms:: Int )
93+ return ceil (Int, ngeoms / (nodecapacity ^ (level)))
94+ end
95+
96+ """
97+ _number_of_levels(nodecapacity::Int, ngeoms::Int)
98+
99+ Calculate the number of levels in a natural tree for a given number of geometries and node capacity.
100+
101+ ## How this works
102+
103+ The number of keys in a level is given by `ngeoms / nodecapacity ^ level`.
104+
105+ The number of levels is the smallest integer such that the number of keys in the last level is 1.
106+ So it goes - if that makes sense.
107+ """
108+ function _number_of_levels (nodecapacity:: Int , ngeoms:: Int )
109+ level = 1
110+ while _number_of_keys (nodecapacity, level, ngeoms) > 1
111+ level += 1
112+ end
113+ return level
114+ end
115+
116+
117+ # This is like a pointer to a node in the tree.
118+ """
119+ NaturalTreeNode{E <: Extents.Extent}
120+
121+ A reference to a node in the natural tree. Kind of like a tree cursor.
122+
123+ - `parent_index` is a pointer to the parent index
124+ - `level` is the level of the node in the tree
125+ - `index` is the index of the node in the level
126+ - `extent` is the extent of the node
127+ """
128+ struct NaturalTreeNode{E <: Extents.Extent }
129+ parent_index:: NaturalIndex{E}
130+ level:: Int
131+ index:: Int
132+ extent:: E
133+ end
134+
135+ Extents. extent (node:: NaturalTreeNode ) = node. extent
136+
137+ # What does SpatialTreeInterface require of trees?
138+ # - Parents completely cover their children
139+ # - `GI.extent(node)` returns `Extent`
140+ # - can mean that `Extents.extent(node)` returns the extent of the node
141+ # - `nchild(node)` returns the number of children of the node
142+ # - `getchild(node)` returns an iterator over all children of the node
143+ # - `getchild(node, i)` returns the i-th child of the node
144+ # - `isleaf(node)` returns a boolean indicating whether the node is a leaf
145+ # - `child_indices_extents(node)` returns an iterator over the indices and extents of the children of the node
146+
147+ function SpatialTreeInterface. nchild (node:: NaturalTreeNode )
148+ start_idx = (node. index - 1 ) * node. parent_index. nodecapacity + 1
149+ stop_idx = min (start_idx + node. parent_index. nodecapacity - 1 , length (node. parent_index. levels[node. level+ 1 ]. extents))
150+ return stop_idx - start_idx + 1
151+ end
152+
153+ function SpatialTreeInterface. getchild (node:: NaturalTreeNode , i:: Int )
154+ child_index = (node. index - 1 ) * node. parent_index. nodecapacity + i
155+ return NaturalTreeNode (
156+ node. parent_index,
157+ node. level + 1 , # increment level by 1
158+ child_index, # index of this particular child
159+ node. parent_index. levels[node. level+ 1 ]. extents[child_index] # the extent of this child
160+ )
161+ end
162+
163+ # Get all children of a node
164+ function SpatialTreeInterface. getchild (node:: NaturalTreeNode )
165+ return (SpatialTreeInterface. getchild (node, i) for i in 1 : SpatialTreeInterface. nchild (node))
166+ end
167+
168+ SpatialTreeInterface. isleaf (node:: NaturalTreeNode ) = node. level == length (node. parent_index. levels) - 1
169+
170+ function SpatialTreeInterface. child_indices_extents (node:: NaturalTreeNode )
171+ start_idx = (node. index - 1 ) * node. parent_index. nodecapacity + 1
172+ stop_idx = min (start_idx + node. parent_index. nodecapacity - 1 , length (node. parent_index. levels[node. level+ 1 ]. extents))
173+ return ((i, node. parent_index. levels[node. level+ 1 ]. extents[i]) for i in start_idx: stop_idx)
174+ end
175+
176+ # implementation for "root node" / top level tree
177+
178+ SpatialTreeInterface. isleaf (node:: NaturalIndex ) = length (node. levels) == 1
179+
180+ SpatialTreeInterface. nchild (node:: NaturalIndex ) = length (node. levels[1 ]. extents)
181+
182+ SpatialTreeInterface. getchild (node:: NaturalIndex ) = SpatialTreeInterface. getchild (NaturalTreeNode (node, 0 , 1 , node. extent))
183+ SpatialTreeInterface. getchild (node:: NaturalIndex , i) = SpatialTreeInterface. getchild (NaturalTreeNode (node, 0 , 1 , node. extent), i)
184+
185+ SpatialTreeInterface. child_indices_extents (node:: NaturalIndex ) = (i_ext for i_ext in enumerate (node. levels[1 ]. extents))
186+
187+ struct NaturallyIndexedRing
188+ points:: Vector{Tuple{Float64, Float64}}
189+ index:: NaturalIndex {Extents. Extent{(:X , :Y ), NTuple{2 , NTuple{2 , Float64}}}}
190+ end
191+
192+ function NaturallyIndexedRing (points:: Vector{Tuple{Float64, Float64}} ; nodecapacity = 32 )
193+ index = NaturalIndex (GO. edge_extents (GI. LinearRing (points)); nodecapacity)
194+ return NaturallyIndexedRing (points, index)
195+ end
196+
197+ NaturallyIndexedRing (ring:: NaturallyIndexedRing ) = ring
198+
199+ function GI. convert (:: Type{NaturallyIndexedRing} , :: GI.LinearRingTrait , geom)
200+ points = GO. tuples (geom). geom
201+ return NaturallyIndexedRing (points)
202+ end
203+
204+ Base. show (io:: IO , :: MIME"text/plain" , ring:: NaturallyIndexedRing ) = Base. show (io, ring)
205+
206+ Base. show (io:: IO , ring:: NaturallyIndexedRing ) = print (io, " NaturallyIndexedRing($(length (ring. points)) points) with index $(sprint (show, ring. index)) " )
207+
208+ GI. ncoord (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing ) = 2
209+ GI. is3d (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing ) = false
210+ GI. ismeasured (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing ) = false
211+
212+ GI. npoint (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing ) = length (ring. points)
213+ GI. getpoint (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing ) = ring. points[i]
214+ GI. getpoint (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing , i:: Int ) = ring. points[i]
215+
216+ GI. ngeom (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing ) = length (ring. points)
217+ GI. getgeom (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing ) = ring. points
218+ GI. getgeom (:: GI.LinearRingTrait , ring:: NaturallyIndexedRing , i:: Int ) = ring. points[i]
219+
220+ Extents. extent (ring:: NaturallyIndexedRing ) = ring. index. extent
221+
222+ GI. isgeometry (:: NaturallyIndexedRing ) = true
223+ GI. geomtrait (:: NaturallyIndexedRing ) = GI. LinearRingTrait ()
224+
225+ function prepare_naturally (geom)
226+ return GO. apply (GI. PolygonTrait (), geom) do poly
227+ return GI. Polygon ([GI. convert (NaturallyIndexedRing, GI. LinearRingTrait (), ring) for ring in GI. getring (poly)])
228+ end
229+ end
230+
231+ export NaturalTree, NaturallyIndexedRing, prepare_naturally
232+
233+ end # module NaturalIndexing
234+
235+ using . NaturalIndexing
0 commit comments