/src/geos/src/index/VertexSequencePackedRtree.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca> |
7 | | * |
8 | | * This is free software; you can redistribute and/or modify it under |
9 | | * the terms of the GNU Lesser General Public Licence as published |
10 | | * by the Free Software Foundation. |
11 | | * See the COPYING file for more information. |
12 | | * |
13 | | **********************************************************************/ |
14 | | |
15 | | |
16 | | |
17 | | #include <geos/geom/Coordinate.h> |
18 | | #include <geos/geom/CoordinateSequence.h> |
19 | | #include <geos/geom/Envelope.h> |
20 | | #include <geos/index/VertexSequencePackedRtree.h> |
21 | | |
22 | | #include <algorithm> |
23 | | |
24 | | using geos::geom::Envelope; |
25 | | |
26 | | namespace geos { |
27 | | namespace index { |
28 | | |
29 | | |
30 | | /** |
31 | | * Creates a new tree over the given sequence of coordinates. |
32 | | * The sequence should be spatially coherent to provide query performance. |
33 | | * |
34 | | * @param pts a sequence of points |
35 | | */ |
36 | | /* public */ |
37 | | VertexSequencePackedRtree::VertexSequencePackedRtree(const CoordinateSequence& pts) |
38 | 0 | : items(pts) |
39 | 0 | , removedItems(pts.size(), false) |
40 | 0 | { |
41 | 0 | build(); |
42 | 0 | } |
43 | | |
44 | | /* public */ |
45 | | std::vector<Envelope> |
46 | | VertexSequencePackedRtree::getBounds() |
47 | 0 | { |
48 | 0 | std::vector<Envelope> boundsCopy = bounds; |
49 | | // std::vector<Envelope> boundsCopy; |
50 | | // std::copy(bounds.begin(), bounds.end(), boundsCopy.begin()); |
51 | 0 | return boundsCopy; |
52 | 0 | } |
53 | | |
54 | | //-- Index Build -------------------------------------------------------------- |
55 | | |
56 | | /* private */ |
57 | | void |
58 | | VertexSequencePackedRtree::build() |
59 | 0 | { |
60 | 0 | levelOffset = computeLevelOffsets(); |
61 | 0 | bounds = createBounds(); |
62 | 0 | } |
63 | | |
64 | | /** |
65 | | * Computes the level offsets. |
66 | | * This is the position in the <tt>bounds</tt> array of each level. |
67 | | * |
68 | | * The levelOffsets array includes a sentinel value of offset[0] = 0. |
69 | | * The top level is always of size 1, |
70 | | * and so also indicates the total number of bounds. |
71 | | * |
72 | | * @return the level offsets |
73 | | */ |
74 | | /* private */ |
75 | | std::vector<std::size_t> |
76 | | VertexSequencePackedRtree::computeLevelOffsets() |
77 | 0 | { |
78 | 0 | std::vector<std::size_t> offsets; |
79 | 0 | offsets.push_back(0u); |
80 | 0 | std::size_t szLevel = items.size(); |
81 | 0 | std::size_t currOffset = 0; |
82 | 0 | do { |
83 | 0 | szLevel = levelNodeCount(szLevel); |
84 | 0 | currOffset += szLevel; |
85 | 0 | offsets.push_back(currOffset); |
86 | 0 | } while (szLevel > 1); |
87 | 0 | return offsets; |
88 | 0 | } |
89 | | |
90 | | /* private static */ |
91 | | std::size_t |
92 | | VertexSequencePackedRtree::ceilDivisor(std::size_t num, std::size_t denom) |
93 | 0 | { |
94 | 0 | std::size_t div = num / denom; |
95 | 0 | return div * denom >= num ? div : div + 1; |
96 | 0 | } |
97 | | |
98 | | /* private */ |
99 | | std::size_t |
100 | | VertexSequencePackedRtree::levelNodeCount(std::size_t numNodes) |
101 | 0 | { |
102 | 0 | return ceilDivisor(numNodes, nodeCapacity); |
103 | 0 | } |
104 | | |
105 | | /* private */ |
106 | | std::vector<Envelope> |
107 | | VertexSequencePackedRtree::createBounds() |
108 | 0 | { |
109 | 0 | std::size_t bndsSize = levelOffset[levelOffset.size() - 1] + 1; |
110 | 0 | std::vector<Envelope> bnds(bndsSize); |
111 | 0 | fillItemBounds(bnds); |
112 | |
|
113 | 0 | for (std::size_t lvl = 1; lvl < levelOffset.size(); lvl++) { |
114 | 0 | fillLevelBounds(lvl, bnds); |
115 | 0 | } |
116 | 0 | return bnds; |
117 | 0 | } |
118 | | |
119 | | |
120 | | /* private */ |
121 | | void |
122 | | VertexSequencePackedRtree::fillItemBounds(std::vector<Envelope>& bnds) |
123 | 0 | { |
124 | 0 | std::size_t nodeStart = 0; |
125 | 0 | std::size_t bndIndex = 0; |
126 | 0 | do { |
127 | 0 | std::size_t nodeEnd = clampMax(nodeStart + nodeCapacity, items.size()); |
128 | 0 | bnds[bndIndex++] = computeItemEnvelope(items, nodeStart, nodeEnd); |
129 | 0 | nodeStart = nodeEnd; |
130 | 0 | } |
131 | 0 | while (nodeStart < items.size()); |
132 | 0 | } |
133 | | |
134 | | |
135 | | /* private */ |
136 | | void |
137 | | VertexSequencePackedRtree::fillLevelBounds(std::size_t lvl, std::vector<Envelope>& bnds) |
138 | 0 | { |
139 | 0 | std::size_t levelStart = levelOffset[lvl - 1]; |
140 | 0 | std::size_t levelEnd = levelOffset[lvl]; |
141 | 0 | std::size_t nodeStart = levelStart; |
142 | 0 | std::size_t levelBndIndex = levelOffset[lvl]; |
143 | 0 | do { |
144 | 0 | std::size_t nodeEnd = clampMax(nodeStart + nodeCapacity, levelEnd); |
145 | 0 | bnds[levelBndIndex++] = computeNodeEnvelope(bnds, nodeStart, nodeEnd); |
146 | 0 | nodeStart = nodeEnd; |
147 | 0 | } |
148 | 0 | while (nodeStart < levelEnd); |
149 | 0 | } |
150 | | |
151 | | |
152 | | /* private static */ |
153 | | Envelope |
154 | | VertexSequencePackedRtree::computeNodeEnvelope(const std::vector<Envelope>& bnds, |
155 | | std::size_t start, std::size_t end) |
156 | 0 | { |
157 | 0 | Envelope env; |
158 | 0 | for (std::size_t i = start; i < end; i++) { |
159 | 0 | env.expandToInclude(bnds[i]); |
160 | 0 | } |
161 | 0 | return env; |
162 | 0 | } |
163 | | |
164 | | /* private static */ |
165 | | Envelope |
166 | | VertexSequencePackedRtree::computeItemEnvelope(const geom::CoordinateSequence& items, |
167 | | std::size_t start, std::size_t end) |
168 | 0 | { |
169 | 0 | Envelope env; |
170 | 0 | for (std::size_t i = start; i < end; i++) { |
171 | 0 | env.expandToInclude(items[i]); |
172 | 0 | } |
173 | 0 | return env; |
174 | 0 | } |
175 | | |
176 | | //-- Index Query -------------------------------------------------------------- |
177 | | |
178 | | /** |
179 | | * Queries the index to find all items which intersect an extent. |
180 | | * The query result is a list of the indices of input coordinates |
181 | | * which intersect the extent. |
182 | | * |
183 | | * @param queryEnv the query extent |
184 | | * @return an array of the indices of the input coordinates |
185 | | */ |
186 | | /* public */ |
187 | | void |
188 | | VertexSequencePackedRtree::query(const Envelope& queryEnv, std::vector<std::size_t>& result) const |
189 | 0 | { |
190 | 0 | std::size_t level = levelOffset.size() - 1; |
191 | 0 | queryNode(queryEnv, level, 0, result); |
192 | 0 | } |
193 | | |
194 | | |
195 | | /* private */ |
196 | | void |
197 | | VertexSequencePackedRtree::queryNode(const Envelope& queryEnv, |
198 | | std::size_t level, std::size_t nodeIndex, |
199 | | std::vector<std::size_t>& result) const |
200 | 0 | { |
201 | 0 | std::size_t boundsIndex = levelOffset[level] + nodeIndex; |
202 | 0 | Envelope nodeEnv = bounds[boundsIndex]; |
203 | | |
204 | | //--- node is empty |
205 | 0 | if (nodeEnv.isNull()) |
206 | 0 | return; |
207 | 0 | if (! queryEnv.intersects(nodeEnv)) |
208 | 0 | return; |
209 | | |
210 | 0 | std::size_t childNodeIndex = nodeIndex * nodeCapacity; |
211 | 0 | if (level == 0) { |
212 | 0 | queryItemRange(queryEnv, childNodeIndex, result); |
213 | 0 | } |
214 | 0 | else { |
215 | 0 | queryNodeRange(queryEnv, level - 1, childNodeIndex, result); |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | | /* private */ |
220 | | void |
221 | | VertexSequencePackedRtree::queryNodeRange(const Envelope& queryEnv, |
222 | | std::size_t level, std::size_t nodeStartIndex, |
223 | | std::vector<std::size_t>& result) const |
224 | 0 | { |
225 | 0 | std::size_t levelMax = levelSize(level); |
226 | 0 | for (std::size_t i = 0; i < nodeCapacity; i++) { |
227 | 0 | std::size_t index = nodeStartIndex + i; |
228 | 0 | if (index >= levelMax) |
229 | 0 | return; |
230 | 0 | queryNode(queryEnv, level, index, result); |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | | |
235 | | /* private */ |
236 | | std::size_t |
237 | | VertexSequencePackedRtree::levelSize(std::size_t level) const |
238 | 0 | { |
239 | 0 | return levelOffset[level + 1] - levelOffset[level]; |
240 | 0 | } |
241 | | |
242 | | |
243 | | /* private */ |
244 | | void |
245 | | VertexSequencePackedRtree::queryItemRange(const Envelope& queryEnv, |
246 | | std::size_t itemIndex, std::vector<std::size_t>& result) const |
247 | 0 | { |
248 | 0 | for (std::size_t i = 0; i < nodeCapacity; i++) { |
249 | 0 | std::size_t index = itemIndex + i; |
250 | 0 | if (index >= items.size()) |
251 | 0 | return; |
252 | 0 | const Coordinate& p = items[index]; |
253 | 0 | bool removed = removedItems[index]; |
254 | 0 | if ( (!removed) && queryEnv.contains(p)) |
255 | 0 | result.push_back(index); |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | //-- Index Modify -------------------------------------------------------------- |
260 | | |
261 | | /** |
262 | | * Removes the input item at the given index from the spatial index. |
263 | | * |
264 | | * @param index the index of the item in the input |
265 | | */ |
266 | | /* public */ |
267 | | void |
268 | | VertexSequencePackedRtree::remove(std::size_t index) |
269 | 0 | { |
270 | 0 | removedItems[index] = true; |
271 | | |
272 | | //--- prune the item parent node if all its items are removed |
273 | 0 | std::size_t nodeIndex = index / nodeCapacity; |
274 | 0 | if (! isItemsNodeEmpty(nodeIndex)) |
275 | 0 | return; |
276 | | |
277 | 0 | bounds[nodeIndex].setToNull(); |
278 | |
|
279 | 0 | if (levelOffset.size() <= 2) |
280 | 0 | return; |
281 | | |
282 | | //-- prune the node parent if all children removed |
283 | 0 | std::size_t nodeLevelIndex = nodeIndex / nodeCapacity; |
284 | 0 | if (! isNodeEmpty(1, nodeLevelIndex)) |
285 | 0 | return; |
286 | | |
287 | 0 | std::size_t nodeIndex1 = levelOffset[1] + nodeLevelIndex; |
288 | 0 | bounds[nodeIndex1].setToNull(); |
289 | | |
290 | | // TODO: propagate removal up the tree nodes? |
291 | 0 | } |
292 | | |
293 | | /* private */ |
294 | | bool |
295 | | VertexSequencePackedRtree::isNodeEmpty(std::size_t level, std::size_t index) |
296 | 0 | { |
297 | 0 | std::size_t start = index * nodeCapacity; |
298 | 0 | std::size_t end = clampMax(start + nodeCapacity, levelOffset[level]); |
299 | 0 | for (std::size_t i = start; i < end; i++) { |
300 | 0 | if (!bounds[i].isNull()) |
301 | 0 | return false; |
302 | 0 | } |
303 | 0 | return true; |
304 | 0 | } |
305 | | |
306 | | /* private */ |
307 | | bool |
308 | | VertexSequencePackedRtree::isItemsNodeEmpty(std::size_t nodeIndex) |
309 | 0 | { |
310 | 0 | std::size_t start = nodeIndex * nodeCapacity; |
311 | 0 | std::size_t end = clampMax(start + nodeCapacity, items.size()); |
312 | 0 | for (std::size_t i = start; i < end; i++) { |
313 | 0 | if (!removedItems[i]) return false; |
314 | 0 | } |
315 | 0 | return true; |
316 | 0 | } |
317 | | |
318 | | /* private static */ |
319 | | std::size_t |
320 | | VertexSequencePackedRtree::clampMax(std::size_t x, std::size_t max) |
321 | 0 | { |
322 | 0 | return x > max ? max: x; |
323 | 0 | } |
324 | | |
325 | | |
326 | | } // namespace geos.index |
327 | | } // namespace geos |
328 | | |