/src/gdal/port/cpl_quad_tree.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: CPL - Common Portability Library |
4 | | * Purpose: Implementation of quadtree building and searching functions. |
5 | | * Derived from shapelib and mapserver implementations |
6 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
7 | | * Even Rouault, <even dot rouault at spatialys.com> |
8 | | * |
9 | | ****************************************************************************** |
10 | | * Copyright (c) 1999-2008, Frank Warmerdam |
11 | | * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com> |
12 | | * |
13 | | * SPDX-License-Identifier: MIT |
14 | | ****************************************************************************** |
15 | | */ |
16 | | |
17 | | #include "cpl_port.h" |
18 | | #include "cpl_quad_tree.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cstdio> |
22 | | #include <cstring> |
23 | | |
24 | | #include "cpl_conv.h" |
25 | | #include "cpl_error.h" |
26 | | |
27 | | constexpr int MAX_DEFAULT_TREE_DEPTH = 12; |
28 | | constexpr int MAX_SUBNODES = 4; |
29 | | |
30 | | typedef struct _QuadTreeNode QuadTreeNode; |
31 | | |
32 | | struct _QuadTreeNode |
33 | | { |
34 | | /* area covered by this psNode */ |
35 | | CPLRectObj rect; |
36 | | |
37 | | int nFeatures; /* number of shapes stored at this psNode. */ |
38 | | |
39 | | int nNumSubNodes; /* number of active subnodes */ |
40 | | |
41 | | void **pahFeatures; /* list of shapes stored at this psNode. */ |
42 | | CPLRectObj *pasBounds; |
43 | | |
44 | | QuadTreeNode *apSubNode[MAX_SUBNODES]; |
45 | | }; |
46 | | |
47 | | struct _CPLQuadTree |
48 | | { |
49 | | QuadTreeNode *psRoot; |
50 | | CPLQuadTreeGetBoundsFunc pfnGetBounds; |
51 | | CPLQuadTreeGetBoundsExFunc pfnGetBoundsEx; |
52 | | void *pUserData; |
53 | | int nFeatures; |
54 | | int nMaxDepth; |
55 | | int nBucketCapacity; |
56 | | double dfSplitRatio; |
57 | | bool bForceUseOfSubNodes; |
58 | | }; |
59 | | |
60 | | static void CPLQuadTreeAddFeatureInternal(CPLQuadTree *hQuadTree, |
61 | | void *hFeature, |
62 | | const CPLRectObj *pRect); |
63 | | static void CPLQuadTreeNodeDestroy(QuadTreeNode *psNode); |
64 | | |
65 | | /* -------------------------------------------------------------------- */ |
66 | | /* If the following is 0.5, psNodes will be split in half. If it */ |
67 | | /* is 0.6 then each apSubNode will contain 60% of the parent */ |
68 | | /* psNode, with 20% representing overlap. This can be help to */ |
69 | | /* prevent small objects on a boundary from shifting too high */ |
70 | | /* up the hQuadTree. */ |
71 | | /* -------------------------------------------------------------------- */ |
72 | | constexpr double DEFAULT_SPLIT_RATIO = 0.55; |
73 | | |
74 | | /* |
75 | | ** Returns TRUE if rectangle a is contained in rectangle b |
76 | | */ |
77 | | static CPL_INLINE bool CPL_RectContained(const CPLRectObj *a, |
78 | | const CPLRectObj *b) |
79 | 0 | { |
80 | 0 | return a->minx >= b->minx && a->maxx <= b->maxx && a->miny >= b->miny && |
81 | 0 | a->maxy <= b->maxy; |
82 | 0 | } |
83 | | |
84 | | /* |
85 | | ** Returns TRUE if rectangles a and b overlap |
86 | | */ |
87 | | static CPL_INLINE bool CPL_RectOverlap(const CPLRectObj *a, const CPLRectObj *b) |
88 | 0 | { |
89 | 0 | if (a->minx > b->maxx) |
90 | 0 | return false; |
91 | 0 | if (a->maxx < b->minx) |
92 | 0 | return false; |
93 | 0 | if (a->miny > b->maxy) |
94 | 0 | return false; |
95 | 0 | if (a->maxy < b->miny) |
96 | 0 | return false; |
97 | 0 | return true; |
98 | 0 | } |
99 | | |
100 | | /************************************************************************/ |
101 | | /* CPLQuadTreeNodeCreate() */ |
102 | | /************************************************************************/ |
103 | | |
104 | | static QuadTreeNode *CPLQuadTreeNodeCreate(const CPLRectObj *pRect) |
105 | 0 | { |
106 | 0 | QuadTreeNode *psNode = |
107 | 0 | static_cast<QuadTreeNode *>(CPLMalloc(sizeof(QuadTreeNode))); |
108 | |
|
109 | 0 | psNode->nFeatures = 0; |
110 | 0 | psNode->pahFeatures = nullptr; |
111 | 0 | psNode->pasBounds = nullptr; |
112 | |
|
113 | 0 | psNode->nNumSubNodes = 0; |
114 | |
|
115 | 0 | memcpy(&(psNode->rect), pRect, sizeof(CPLRectObj)); |
116 | |
|
117 | 0 | return psNode; |
118 | 0 | } |
119 | | |
120 | | /************************************************************************/ |
121 | | /* CPLQuadTreeCreate() */ |
122 | | /************************************************************************/ |
123 | | |
124 | | /** |
125 | | * Create a new quadtree |
126 | | * |
127 | | * @param pGlobalBounds a pointer to the global extent of all |
128 | | * the elements that will be inserted |
129 | | * @param pfnGetBounds a user provided function to get the bounding box of |
130 | | * the inserted elements. If it is set to NULL, then |
131 | | * CPLQuadTreeInsertWithBounds() must be used, and |
132 | | * extra memory will be used to keep features bounds in the |
133 | | * quad tree. |
134 | | * |
135 | | * @return a newly allocated quadtree |
136 | | */ |
137 | | |
138 | | CPLQuadTree *CPLQuadTreeCreate(const CPLRectObj *pGlobalBounds, |
139 | | CPLQuadTreeGetBoundsFunc pfnGetBounds) |
140 | 0 | { |
141 | 0 | CPLAssert(pGlobalBounds); |
142 | | |
143 | | /* -------------------------------------------------------------------- */ |
144 | | /* Allocate the hQuadTree object */ |
145 | | /* -------------------------------------------------------------------- */ |
146 | 0 | CPLQuadTree *hQuadTree = |
147 | 0 | static_cast<CPLQuadTree *>(CPLMalloc(sizeof(CPLQuadTree))); |
148 | |
|
149 | 0 | hQuadTree->nFeatures = 0; |
150 | 0 | hQuadTree->pfnGetBounds = pfnGetBounds; |
151 | 0 | hQuadTree->pfnGetBoundsEx = nullptr; |
152 | 0 | hQuadTree->nMaxDepth = 0; |
153 | 0 | hQuadTree->nBucketCapacity = 8; |
154 | |
|
155 | 0 | hQuadTree->dfSplitRatio = DEFAULT_SPLIT_RATIO; |
156 | 0 | hQuadTree->bForceUseOfSubNodes = false; |
157 | | |
158 | | /* -------------------------------------------------------------------- */ |
159 | | /* Allocate the psRoot psNode. */ |
160 | | /* -------------------------------------------------------------------- */ |
161 | 0 | hQuadTree->psRoot = CPLQuadTreeNodeCreate(pGlobalBounds); |
162 | |
|
163 | 0 | hQuadTree->pUserData = nullptr; |
164 | |
|
165 | 0 | return hQuadTree; |
166 | 0 | } |
167 | | |
168 | | /************************************************************************/ |
169 | | /* CPLQuadTreeCreateEx() */ |
170 | | /************************************************************************/ |
171 | | |
172 | | /** |
173 | | * Create a new quadtree |
174 | | * |
175 | | * @param pGlobalBounds a pointer to the global extent of all |
176 | | * the elements that will be inserted |
177 | | * @param pfnGetBoundsEx a user provided function to get the bounding box of |
178 | | * the inserted elements. If it is set to NULL, then |
179 | | * CPLQuadTreeInsertWithBounds() must be used, and |
180 | | * extra memory will be used to keep features bounds in the |
181 | | * quad tree. |
182 | | * @param pUserData user data passed to pfnGetBoundsEx |
183 | | * |
184 | | * @return a newly allocated quadtree |
185 | | */ |
186 | | |
187 | | CPLQuadTree *CPLQuadTreeCreateEx(const CPLRectObj *pGlobalBounds, |
188 | | CPLQuadTreeGetBoundsExFunc pfnGetBoundsEx, |
189 | | void *pUserData) |
190 | 0 | { |
191 | 0 | CPLAssert(pGlobalBounds); |
192 | | |
193 | | /* -------------------------------------------------------------------- */ |
194 | | /* Allocate the hQuadTree object */ |
195 | | /* -------------------------------------------------------------------- */ |
196 | 0 | CPLQuadTree *hQuadTree = |
197 | 0 | static_cast<CPLQuadTree *>(CPLMalloc(sizeof(CPLQuadTree))); |
198 | |
|
199 | 0 | hQuadTree->nFeatures = 0; |
200 | 0 | hQuadTree->pfnGetBounds = nullptr; |
201 | 0 | hQuadTree->pfnGetBoundsEx = pfnGetBoundsEx; |
202 | 0 | hQuadTree->nMaxDepth = 0; |
203 | 0 | hQuadTree->nBucketCapacity = 8; |
204 | |
|
205 | 0 | hQuadTree->dfSplitRatio = DEFAULT_SPLIT_RATIO; |
206 | 0 | hQuadTree->bForceUseOfSubNodes = false; |
207 | | |
208 | | /* -------------------------------------------------------------------- */ |
209 | | /* Allocate the psRoot psNode. */ |
210 | | /* -------------------------------------------------------------------- */ |
211 | 0 | hQuadTree->psRoot = CPLQuadTreeNodeCreate(pGlobalBounds); |
212 | |
|
213 | 0 | hQuadTree->pUserData = pUserData; |
214 | |
|
215 | 0 | return hQuadTree; |
216 | 0 | } |
217 | | |
218 | | /************************************************************************/ |
219 | | /* CPLQuadTreeGetAdvisedMaxDepth() */ |
220 | | /************************************************************************/ |
221 | | |
222 | | /** |
223 | | * Returns the optimal depth of a quadtree to hold nExpectedFeatures |
224 | | * |
225 | | * @param nExpectedFeatures the expected maximum number of elements to be |
226 | | * inserted. |
227 | | * |
228 | | * @return the optimal depth of a quadtree to hold nExpectedFeatures |
229 | | */ |
230 | | |
231 | | int CPLQuadTreeGetAdvisedMaxDepth(int nExpectedFeatures) |
232 | 0 | { |
233 | | /* -------------------------------------------------------------------- */ |
234 | | /* Try to select a reasonable one */ |
235 | | /* that implies approximately 8 shapes per node. */ |
236 | | /* -------------------------------------------------------------------- */ |
237 | 0 | int nMaxDepth = 0; |
238 | 0 | int nMaxNodeCount = 1; |
239 | |
|
240 | 0 | while (nMaxNodeCount < nExpectedFeatures / 4) |
241 | 0 | { |
242 | 0 | nMaxDepth += 1; |
243 | 0 | nMaxNodeCount = nMaxNodeCount * 2; |
244 | 0 | } |
245 | |
|
246 | 0 | CPLDebug("CPLQuadTree", "Estimated spatial index tree depth: %d", |
247 | 0 | nMaxDepth); |
248 | | |
249 | | /* NOTE: Due to problems with memory allocation for deep trees, |
250 | | * automatically estimated depth is limited up to 12 levels. |
251 | | * See Ticket #1594 for detailed discussion. |
252 | | */ |
253 | 0 | if (nMaxDepth > MAX_DEFAULT_TREE_DEPTH) |
254 | 0 | { |
255 | 0 | nMaxDepth = MAX_DEFAULT_TREE_DEPTH; |
256 | |
|
257 | 0 | CPLDebug("CPLQuadTree", |
258 | 0 | "Falling back to max number of allowed index tree " |
259 | 0 | "levels (%d).", |
260 | 0 | MAX_DEFAULT_TREE_DEPTH); |
261 | 0 | } |
262 | |
|
263 | 0 | return nMaxDepth; |
264 | 0 | } |
265 | | |
266 | | /************************************************************************/ |
267 | | /* CPLQuadTreeSetMaxDepth() */ |
268 | | /************************************************************************/ |
269 | | |
270 | | /** |
271 | | * Set the maximum depth of a quadtree. By default, quad trees have |
272 | | * no maximum depth, but a maximum bucket capacity. |
273 | | * |
274 | | * @param hQuadTree the quad tree |
275 | | * @param nMaxDepth the maximum depth allowed |
276 | | */ |
277 | | |
278 | | void CPLQuadTreeSetMaxDepth(CPLQuadTree *hQuadTree, int nMaxDepth) |
279 | 0 | { |
280 | 0 | hQuadTree->nMaxDepth = nMaxDepth; |
281 | 0 | } |
282 | | |
283 | | /************************************************************************/ |
284 | | /* CPLQuadTreeSetBucketCapacity() */ |
285 | | /************************************************************************/ |
286 | | |
287 | | /** |
288 | | * Set the maximum capacity of a node of a quadtree. The default value is 8. |
289 | | * Note that the maximum capacity will only be honoured if the features |
290 | | * inserted have a point geometry. Otherwise it may be exceeded. |
291 | | * |
292 | | * @param hQuadTree the quad tree |
293 | | * @param nBucketCapacity the maximum capacity of a node of a quadtree |
294 | | */ |
295 | | |
296 | | void CPLQuadTreeSetBucketCapacity(CPLQuadTree *hQuadTree, int nBucketCapacity) |
297 | 0 | { |
298 | 0 | if (nBucketCapacity > 0) |
299 | 0 | hQuadTree->nBucketCapacity = nBucketCapacity; |
300 | 0 | } |
301 | | |
302 | | /************************************************************************/ |
303 | | /* CPLQuadTreeForceUseOfSubNodes() */ |
304 | | /************************************************************************/ |
305 | | |
306 | | /** |
307 | | * Force the quadtree to insert as much as possible a feature whose bbox |
308 | | * spread over multiple subnodes into those subnodes, rather than in the |
309 | | * list of features attached to the node. |
310 | | * |
311 | | * @param hQuadTree the quad tree |
312 | | */ |
313 | | |
314 | | void CPLQuadTreeForceUseOfSubNodes(CPLQuadTree *hQuadTree) |
315 | 0 | { |
316 | 0 | hQuadTree->bForceUseOfSubNodes = true; |
317 | 0 | } |
318 | | |
319 | | /************************************************************************/ |
320 | | /* CPLQuadTreeInsert() */ |
321 | | /************************************************************************/ |
322 | | |
323 | | /** |
324 | | * Insert a feature into a quadtree |
325 | | * |
326 | | * @param hQuadTree the quad tree |
327 | | * @param hFeature the feature to insert |
328 | | */ |
329 | | |
330 | | void CPLQuadTreeInsert(CPLQuadTree *hQuadTree, void *hFeature) |
331 | 0 | { |
332 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
333 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
334 | 0 | { |
335 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
336 | 0 | "hQuadTree->pfnGetBounds == NULL"); |
337 | 0 | return; |
338 | 0 | } |
339 | 0 | hQuadTree->nFeatures++; |
340 | 0 | CPLRectObj bounds; |
341 | 0 | if (hQuadTree->pfnGetBoundsEx) |
342 | 0 | hQuadTree->pfnGetBoundsEx(hFeature, hQuadTree->pUserData, &bounds); |
343 | 0 | else |
344 | 0 | hQuadTree->pfnGetBounds(hFeature, &bounds); |
345 | 0 | CPLQuadTreeAddFeatureInternal(hQuadTree, hFeature, &bounds); |
346 | 0 | } |
347 | | |
348 | | /************************************************************************/ |
349 | | /* CPLQuadTreeInsertWithBounds() */ |
350 | | /************************************************************************/ |
351 | | |
352 | | /** |
353 | | * Insert a feature into a quadtree |
354 | | * |
355 | | * @param hQuadTree the quad tree |
356 | | * @param hFeature the feature to insert |
357 | | * @param psBounds bounds of the feature |
358 | | */ |
359 | | void CPLQuadTreeInsertWithBounds(CPLQuadTree *hQuadTree, void *hFeature, |
360 | | const CPLRectObj *psBounds) |
361 | 0 | { |
362 | 0 | hQuadTree->nFeatures++; |
363 | 0 | CPLQuadTreeAddFeatureInternal(hQuadTree, hFeature, psBounds); |
364 | 0 | } |
365 | | |
366 | | /************************************************************************/ |
367 | | /* CPLQuadTreeRemove() */ |
368 | | /************************************************************************/ |
369 | | |
370 | | static bool CPLQuadTreeRemoveInternal(QuadTreeNode *psNode, void *hFeature, |
371 | | const CPLRectObj *psBounds) |
372 | 0 | { |
373 | 0 | bool bRemoved = false; |
374 | |
|
375 | 0 | for (int i = 0; i < psNode->nFeatures; i++) |
376 | 0 | { |
377 | 0 | if (psNode->pahFeatures[i] == hFeature) |
378 | 0 | { |
379 | 0 | if (i < psNode->nFeatures - 1) |
380 | 0 | { |
381 | 0 | memmove(psNode->pahFeatures + i, psNode->pahFeatures + i + 1, |
382 | 0 | (psNode->nFeatures - 1 - i) * sizeof(void *)); |
383 | 0 | if (psNode->pasBounds) |
384 | 0 | { |
385 | 0 | memmove(psNode->pasBounds + i, psNode->pasBounds + i + 1, |
386 | 0 | (psNode->nFeatures - 1 - i) * sizeof(CPLRectObj)); |
387 | 0 | } |
388 | 0 | } |
389 | 0 | bRemoved = true; |
390 | 0 | psNode->nFeatures--; |
391 | 0 | break; |
392 | 0 | } |
393 | 0 | } |
394 | 0 | if (psNode->nFeatures == 0 && psNode->pahFeatures != nullptr) |
395 | 0 | { |
396 | 0 | CPLFree(psNode->pahFeatures); |
397 | 0 | CPLFree(psNode->pasBounds); |
398 | 0 | psNode->pahFeatures = nullptr; |
399 | 0 | psNode->pasBounds = nullptr; |
400 | 0 | } |
401 | | |
402 | | /* -------------------------------------------------------------------- */ |
403 | | /* Recurse to subnodes if they exist. */ |
404 | | /* -------------------------------------------------------------------- */ |
405 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
406 | 0 | { |
407 | 0 | if (psNode->apSubNode[i] && |
408 | 0 | CPL_RectOverlap(&(psNode->apSubNode[i]->rect), psBounds)) |
409 | 0 | { |
410 | 0 | bRemoved |= CPLQuadTreeRemoveInternal(psNode->apSubNode[i], |
411 | 0 | hFeature, psBounds); |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | | /* -------------------------------------------------------------------- */ |
416 | | /* Only collapse the subnodes when all of them are empty leaves: */ |
417 | | /* the node then becomes a leaf again and may re-split on a later */ |
418 | | /* insertion. Destroying an individual empty subnode would leave a */ |
419 | | /* quadrant hole: features falling in it can neither descend nor */ |
420 | | /* trigger a split (splitting requires a node without subnodes), */ |
421 | | /* so they would pile up in this node's bucket forever, degrading */ |
422 | | /* every search overlapping it to a linear scan. */ |
423 | | /* -------------------------------------------------------------------- */ |
424 | 0 | if (psNode->nNumSubNodes != 0) |
425 | 0 | { |
426 | 0 | bool bAllSubNodesEmpty = true; |
427 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
428 | 0 | { |
429 | 0 | if (psNode->apSubNode[i] && |
430 | 0 | (psNode->apSubNode[i]->nFeatures != 0 || |
431 | 0 | psNode->apSubNode[i]->nNumSubNodes != 0)) |
432 | 0 | { |
433 | 0 | bAllSubNodesEmpty = false; |
434 | 0 | break; |
435 | 0 | } |
436 | 0 | } |
437 | 0 | if (bAllSubNodesEmpty) |
438 | 0 | { |
439 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
440 | 0 | { |
441 | 0 | if (psNode->apSubNode[i]) |
442 | 0 | CPLQuadTreeNodeDestroy(psNode->apSubNode[i]); |
443 | 0 | psNode->apSubNode[i] = nullptr; |
444 | 0 | } |
445 | 0 | psNode->nNumSubNodes = 0; |
446 | 0 | } |
447 | 0 | } |
448 | |
|
449 | 0 | return bRemoved; |
450 | 0 | } |
451 | | |
452 | | /** |
453 | | * Remove a feature from a quadtree. |
454 | | * |
455 | | * Currently the quadtree is not re-balanced. |
456 | | * |
457 | | * @param hQuadTree the quad tree |
458 | | * @param hFeature the feature to remove |
459 | | * @param psBounds bounds of the feature (or NULL if pfnGetBounds has been |
460 | | * filled) |
461 | | */ |
462 | | void CPLQuadTreeRemove(CPLQuadTree *hQuadTree, void *hFeature, |
463 | | const CPLRectObj *psBounds) |
464 | 0 | { |
465 | 0 | if (psBounds == nullptr && hQuadTree->pfnGetBounds == nullptr && |
466 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
467 | 0 | { |
468 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
469 | 0 | "hQuadTree->pfnGetBounds == NULL"); |
470 | 0 | return; |
471 | 0 | } |
472 | 0 | CPLRectObj bounds; // keep variable in this outer scope |
473 | 0 | if (psBounds == nullptr) |
474 | 0 | { |
475 | 0 | if (hQuadTree->pfnGetBoundsEx) |
476 | 0 | hQuadTree->pfnGetBoundsEx(hFeature, hQuadTree->pUserData, &bounds); |
477 | 0 | else |
478 | 0 | hQuadTree->pfnGetBounds(hFeature, &bounds); |
479 | 0 | psBounds = &bounds; |
480 | 0 | } |
481 | 0 | if (CPLQuadTreeRemoveInternal(hQuadTree->psRoot, hFeature, psBounds)) |
482 | 0 | { |
483 | 0 | hQuadTree->nFeatures--; |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | | /************************************************************************/ |
488 | | /* CPLQuadTreeNodeDestroy() */ |
489 | | /************************************************************************/ |
490 | | |
491 | | static void CPLQuadTreeNodeDestroy(QuadTreeNode *psNode) |
492 | 0 | { |
493 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
494 | 0 | { |
495 | 0 | if (psNode->apSubNode[i]) |
496 | 0 | CPLQuadTreeNodeDestroy(psNode->apSubNode[i]); |
497 | 0 | } |
498 | |
|
499 | 0 | if (psNode->pahFeatures) |
500 | 0 | { |
501 | 0 | CPLFree(psNode->pahFeatures); |
502 | 0 | CPLFree(psNode->pasBounds); |
503 | 0 | } |
504 | |
|
505 | 0 | CPLFree(psNode); |
506 | 0 | } |
507 | | |
508 | | /************************************************************************/ |
509 | | /* CPLQuadTreeDestroy() */ |
510 | | /************************************************************************/ |
511 | | |
512 | | /** |
513 | | * Destroy a quadtree |
514 | | * |
515 | | * @param hQuadTree the quad tree to destroy |
516 | | */ |
517 | | |
518 | | void CPLQuadTreeDestroy(CPLQuadTree *hQuadTree) |
519 | 0 | { |
520 | 0 | CPLAssert(hQuadTree); |
521 | 0 | CPLQuadTreeNodeDestroy(hQuadTree->psRoot); |
522 | 0 | CPLFree(hQuadTree); |
523 | 0 | } |
524 | | |
525 | | /************************************************************************/ |
526 | | /* CPLQuadTreeSplitBounds() */ |
527 | | /************************************************************************/ |
528 | | |
529 | | static void CPLQuadTreeSplitBounds(double dfSplitRatio, const CPLRectObj *in, |
530 | | CPLRectObj *out1, CPLRectObj *out2) |
531 | 0 | { |
532 | | /* -------------------------------------------------------------------- */ |
533 | | /* The output bounds will be very similar to the input bounds, */ |
534 | | /* so just copy over to start. */ |
535 | | /* -------------------------------------------------------------------- */ |
536 | 0 | memcpy(out1, in, sizeof(CPLRectObj)); |
537 | 0 | memcpy(out2, in, sizeof(CPLRectObj)); |
538 | | |
539 | | /* -------------------------------------------------------------------- */ |
540 | | /* Split in X direction. */ |
541 | | /* -------------------------------------------------------------------- */ |
542 | 0 | if ((in->maxx - in->minx) > (in->maxy - in->miny)) |
543 | 0 | { |
544 | 0 | const double range = in->maxx - in->minx; |
545 | |
|
546 | 0 | out1->maxx = in->minx + range * dfSplitRatio; |
547 | 0 | out2->minx = in->maxx - range * dfSplitRatio; |
548 | 0 | } |
549 | | |
550 | | /* -------------------------------------------------------------------- */ |
551 | | /* Otherwise split in Y direction. */ |
552 | | /* -------------------------------------------------------------------- */ |
553 | 0 | else |
554 | 0 | { |
555 | 0 | const double range = in->maxy - in->miny; |
556 | |
|
557 | 0 | out1->maxy = in->miny + range * dfSplitRatio; |
558 | 0 | out2->miny = in->maxy - range * dfSplitRatio; |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | /************************************************************************/ |
563 | | /* CPLQuadTreeNodeAddFeatureAlg1() */ |
564 | | /************************************************************************/ |
565 | | |
566 | | static void CPLQuadTreeNodeAddFeatureAlg1(CPLQuadTree *hQuadTree, |
567 | | QuadTreeNode *psNode, void *hFeature, |
568 | | const CPLRectObj *pRect) |
569 | 0 | { |
570 | 0 | if (psNode->nNumSubNodes == 0) |
571 | 0 | { |
572 | | // If we have reached the max bucket capacity, try to insert |
573 | | // in a subnode if possible. |
574 | 0 | if (psNode->nFeatures >= hQuadTree->nBucketCapacity) |
575 | 0 | { |
576 | 0 | CPLRectObj half1 = {0.0, 0.0, 0.0, 0.0}; |
577 | 0 | CPLRectObj half2 = {0.0, 0.0, 0.0, 0.0}; |
578 | 0 | CPLRectObj quad1 = {0.0, 0.0, 0.0, 0.0}; |
579 | 0 | CPLRectObj quad2 = {0.0, 0.0, 0.0, 0.0}; |
580 | 0 | CPLRectObj quad3 = {0.0, 0.0, 0.0, 0.0}; |
581 | 0 | CPLRectObj quad4 = {0.0, 0.0, 0.0, 0.0}; |
582 | |
|
583 | 0 | CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect, |
584 | 0 | &half1, &half2); |
585 | 0 | CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1, |
586 | 0 | &quad2); |
587 | 0 | CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3, |
588 | 0 | &quad4); |
589 | |
|
590 | 0 | if (memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 && |
591 | 0 | memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 && |
592 | 0 | memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 && |
593 | 0 | memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 && |
594 | 0 | (hQuadTree->bForceUseOfSubNodes || |
595 | 0 | CPL_RectContained(pRect, &quad1) || |
596 | 0 | CPL_RectContained(pRect, &quad2) || |
597 | 0 | CPL_RectContained(pRect, &quad3) || |
598 | 0 | CPL_RectContained(pRect, &quad4))) |
599 | 0 | { |
600 | 0 | psNode->nNumSubNodes = 4; |
601 | 0 | psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1); |
602 | 0 | psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2); |
603 | 0 | psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3); |
604 | 0 | psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4); |
605 | |
|
606 | 0 | const int oldNumFeatures = psNode->nFeatures; |
607 | 0 | void **oldFeatures = psNode->pahFeatures; |
608 | 0 | CPLRectObj *pasOldBounds = psNode->pasBounds; |
609 | 0 | psNode->nFeatures = 0; |
610 | 0 | psNode->pahFeatures = nullptr; |
611 | 0 | psNode->pasBounds = nullptr; |
612 | | |
613 | | // Redispatch existing pahFeatures in apSubNodes. |
614 | 0 | for (int i = 0; i < oldNumFeatures; i++) |
615 | 0 | { |
616 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
617 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
618 | 0 | CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, |
619 | 0 | oldFeatures[i], |
620 | 0 | &pasOldBounds[i]); |
621 | 0 | else |
622 | 0 | { |
623 | 0 | CPLRectObj bounds; |
624 | 0 | if (hQuadTree->pfnGetBoundsEx) |
625 | 0 | hQuadTree->pfnGetBoundsEx( |
626 | 0 | oldFeatures[i], hQuadTree->pUserData, &bounds); |
627 | 0 | else |
628 | 0 | hQuadTree->pfnGetBounds(oldFeatures[i], &bounds); |
629 | 0 | CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, |
630 | 0 | oldFeatures[i], &bounds); |
631 | 0 | } |
632 | 0 | } |
633 | |
|
634 | 0 | CPLFree(oldFeatures); |
635 | 0 | CPLFree(pasOldBounds); |
636 | | |
637 | | /* recurse back on this psNode now that it has apSubNodes */ |
638 | 0 | CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, hFeature, |
639 | 0 | pRect); |
640 | 0 | return; |
641 | 0 | } |
642 | 0 | } |
643 | 0 | } |
644 | 0 | else |
645 | 0 | { |
646 | | /* -------------------------------------------------------------------- |
647 | | */ |
648 | | /* If there are apSubNodes, then consider whether this object */ |
649 | | /* will fit in them. */ |
650 | | /* -------------------------------------------------------------------- |
651 | | */ |
652 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
653 | 0 | { |
654 | 0 | if (CPL_RectContained(pRect, &psNode->apSubNode[i]->rect)) |
655 | 0 | { |
656 | 0 | CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode->apSubNode[i], |
657 | 0 | hFeature, pRect); |
658 | 0 | return; |
659 | 0 | } |
660 | 0 | } |
661 | 0 | if (hQuadTree->bForceUseOfSubNodes) |
662 | 0 | { |
663 | 0 | bool overlaps[4]; |
664 | 0 | bool overlapAll = true; |
665 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
666 | 0 | { |
667 | 0 | overlaps[i] = |
668 | 0 | CPL_RectOverlap(pRect, &psNode->apSubNode[i]->rect); |
669 | 0 | if (!overlaps[i]) |
670 | 0 | overlapAll = false; |
671 | 0 | } |
672 | 0 | if (!overlapAll) |
673 | 0 | { |
674 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
675 | 0 | { |
676 | 0 | if (overlaps[i]) |
677 | 0 | { |
678 | 0 | CPLRectObj intersection; |
679 | 0 | intersection.minx = std::max( |
680 | 0 | pRect->minx, psNode->apSubNode[i]->rect.minx); |
681 | 0 | intersection.miny = std::max( |
682 | 0 | pRect->miny, psNode->apSubNode[i]->rect.miny); |
683 | 0 | intersection.maxx = std::min( |
684 | 0 | pRect->maxx, psNode->apSubNode[i]->rect.maxx); |
685 | 0 | intersection.maxy = std::min( |
686 | 0 | pRect->maxy, psNode->apSubNode[i]->rect.maxy); |
687 | 0 | CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, |
688 | 0 | psNode->apSubNode[i], |
689 | 0 | hFeature, &intersection); |
690 | 0 | } |
691 | 0 | } |
692 | 0 | return; |
693 | 0 | } |
694 | 0 | } |
695 | 0 | } |
696 | | |
697 | | /* -------------------------------------------------------------------- */ |
698 | | /* If none of that worked, just add it to this psNodes list. */ |
699 | | /* -------------------------------------------------------------------- */ |
700 | 0 | psNode->nFeatures++; |
701 | |
|
702 | 0 | if (psNode->nFeatures == 1) |
703 | 0 | { |
704 | 0 | CPLAssert(psNode->pahFeatures == nullptr); |
705 | 0 | psNode->pahFeatures = static_cast<void **>( |
706 | 0 | CPLMalloc(hQuadTree->nBucketCapacity * sizeof(void *))); |
707 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
708 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
709 | 0 | psNode->pasBounds = static_cast<CPLRectObj *>( |
710 | 0 | CPLMalloc(hQuadTree->nBucketCapacity * sizeof(CPLRectObj))); |
711 | 0 | } |
712 | 0 | else if (psNode->nFeatures > hQuadTree->nBucketCapacity) |
713 | 0 | { |
714 | 0 | psNode->pahFeatures = static_cast<void **>(CPLRealloc( |
715 | 0 | psNode->pahFeatures, sizeof(void *) * psNode->nFeatures)); |
716 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
717 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
718 | 0 | psNode->pasBounds = static_cast<CPLRectObj *>(CPLRealloc( |
719 | 0 | psNode->pasBounds, sizeof(CPLRectObj) * psNode->nFeatures)); |
720 | 0 | } |
721 | 0 | psNode->pahFeatures[psNode->nFeatures - 1] = hFeature; |
722 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
723 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
724 | 0 | psNode->pasBounds[psNode->nFeatures - 1] = *pRect; |
725 | |
|
726 | 0 | return; |
727 | 0 | } |
728 | | |
729 | | /************************************************************************/ |
730 | | /* CPLQuadTreeNodeAddFeatureAlg2() */ |
731 | | /************************************************************************/ |
732 | | |
733 | | static void CPLQuadTreeNodeAddFeatureAlg2(CPLQuadTree *hQuadTree, |
734 | | QuadTreeNode *psNode, void *hFeature, |
735 | | const CPLRectObj *pRect, |
736 | | int nMaxDepth) |
737 | 0 | { |
738 | | /* -------------------------------------------------------------------- */ |
739 | | /* If there are apSubNodes, then consider whether this object */ |
740 | | /* will fit in them. */ |
741 | | /* -------------------------------------------------------------------- */ |
742 | 0 | if (nMaxDepth > 1 && psNode->nNumSubNodes > 0) |
743 | 0 | { |
744 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
745 | 0 | { |
746 | 0 | if (CPL_RectContained(pRect, &psNode->apSubNode[i]->rect)) |
747 | 0 | { |
748 | 0 | CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, psNode->apSubNode[i], |
749 | 0 | hFeature, pRect, nMaxDepth - 1); |
750 | 0 | return; |
751 | 0 | } |
752 | 0 | } |
753 | 0 | } |
754 | | |
755 | | /* -------------------------------------------------------------------- */ |
756 | | /* Otherwise, consider creating four apSubNodes if could fit into */ |
757 | | /* them, and adding to the appropriate apSubNode. */ |
758 | | /* -------------------------------------------------------------------- */ |
759 | 0 | else if (nMaxDepth > 1 && psNode->nNumSubNodes == 0) |
760 | 0 | { |
761 | 0 | CPLRectObj half1, half2, quad1, quad2, quad3, quad4; |
762 | |
|
763 | 0 | CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect, &half1, |
764 | 0 | &half2); |
765 | 0 | CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1, &quad2); |
766 | 0 | CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3, &quad4); |
767 | |
|
768 | 0 | if (memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 && |
769 | 0 | memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 && |
770 | 0 | memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 && |
771 | 0 | memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 && |
772 | 0 | (CPL_RectContained(pRect, &quad1) || |
773 | 0 | CPL_RectContained(pRect, &quad2) || |
774 | 0 | CPL_RectContained(pRect, &quad3) || |
775 | 0 | CPL_RectContained(pRect, &quad4))) |
776 | 0 | { |
777 | 0 | psNode->nNumSubNodes = 4; |
778 | 0 | psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1); |
779 | 0 | psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2); |
780 | 0 | psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3); |
781 | 0 | psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4); |
782 | | |
783 | | /* recurse back on this psNode now that it has apSubNodes */ |
784 | 0 | CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, psNode, hFeature, pRect, |
785 | 0 | nMaxDepth); |
786 | 0 | return; |
787 | 0 | } |
788 | 0 | } |
789 | | |
790 | | /* -------------------------------------------------------------------- */ |
791 | | /* If none of that worked, just add it to this psNodes list. */ |
792 | | /* -------------------------------------------------------------------- */ |
793 | 0 | psNode->nFeatures++; |
794 | |
|
795 | 0 | psNode->pahFeatures = static_cast<void **>( |
796 | 0 | CPLRealloc(psNode->pahFeatures, sizeof(void *) * psNode->nFeatures)); |
797 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
798 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
799 | 0 | { |
800 | 0 | psNode->pasBounds = static_cast<CPLRectObj *>(CPLRealloc( |
801 | 0 | psNode->pasBounds, sizeof(CPLRectObj) * psNode->nFeatures)); |
802 | 0 | } |
803 | 0 | psNode->pahFeatures[psNode->nFeatures - 1] = hFeature; |
804 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
805 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
806 | 0 | { |
807 | 0 | psNode->pasBounds[psNode->nFeatures - 1] = *pRect; |
808 | 0 | } |
809 | 0 | } |
810 | | |
811 | | /************************************************************************/ |
812 | | /* CPLQuadTreeAddFeatureInternal() */ |
813 | | /************************************************************************/ |
814 | | |
815 | | static void CPLQuadTreeAddFeatureInternal(CPLQuadTree *hQuadTree, |
816 | | void *hFeature, |
817 | | const CPLRectObj *pRect) |
818 | 0 | { |
819 | 0 | if (hQuadTree->nMaxDepth == 0) |
820 | 0 | { |
821 | 0 | CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, hQuadTree->psRoot, hFeature, |
822 | 0 | pRect); |
823 | 0 | } |
824 | 0 | else |
825 | 0 | { |
826 | 0 | CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, hQuadTree->psRoot, hFeature, |
827 | 0 | pRect, hQuadTree->nMaxDepth); |
828 | 0 | } |
829 | 0 | } |
830 | | |
831 | | /************************************************************************/ |
832 | | /* CPLQuadTreeCollectFeatures() */ |
833 | | /************************************************************************/ |
834 | | |
835 | | static void CPLQuadTreeCollectFeatures(const CPLQuadTree *hQuadTree, |
836 | | const QuadTreeNode *psNode, |
837 | | const CPLRectObj *pAoi, |
838 | | int *pnFeatureCount, int *pnMaxFeatures, |
839 | | void ***pppFeatureList) |
840 | 0 | { |
841 | | /* -------------------------------------------------------------------- */ |
842 | | /* Does this psNode overlap the area of interest at all? If not, */ |
843 | | /* return without adding to the list at all. */ |
844 | | /* -------------------------------------------------------------------- */ |
845 | 0 | if (!CPL_RectOverlap(&psNode->rect, pAoi)) |
846 | 0 | return; |
847 | | |
848 | | /* -------------------------------------------------------------------- */ |
849 | | /* Grow the list to hold the features on this psNode. */ |
850 | | /* -------------------------------------------------------------------- */ |
851 | 0 | if (*pnFeatureCount + psNode->nFeatures > *pnMaxFeatures) |
852 | 0 | { |
853 | | // TODO(schwehr): Symbolic constant. |
854 | 0 | *pnMaxFeatures = (*pnFeatureCount + psNode->nFeatures) * 2 + 20; |
855 | 0 | *pppFeatureList = static_cast<void **>( |
856 | 0 | CPLRealloc(*pppFeatureList, sizeof(void *) * *pnMaxFeatures)); |
857 | 0 | } |
858 | | |
859 | | /* -------------------------------------------------------------------- */ |
860 | | /* Add the local features to the list. */ |
861 | | /* -------------------------------------------------------------------- */ |
862 | 0 | for (int i = 0; i < psNode->nFeatures; i++) |
863 | 0 | { |
864 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
865 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
866 | 0 | { |
867 | 0 | if (CPL_RectOverlap(&psNode->pasBounds[i], pAoi)) |
868 | 0 | (*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i]; |
869 | 0 | } |
870 | 0 | else |
871 | 0 | { |
872 | 0 | CPLRectObj bounds; |
873 | 0 | if (hQuadTree->pfnGetBoundsEx) |
874 | 0 | hQuadTree->pfnGetBoundsEx(psNode->pahFeatures[i], |
875 | 0 | hQuadTree->pUserData, &bounds); |
876 | 0 | else |
877 | 0 | hQuadTree->pfnGetBounds(psNode->pahFeatures[i], &bounds); |
878 | |
|
879 | 0 | if (CPL_RectOverlap(&bounds, pAoi)) |
880 | 0 | (*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i]; |
881 | 0 | } |
882 | 0 | } |
883 | | |
884 | | /* -------------------------------------------------------------------- */ |
885 | | /* Recurse to subnodes if they exist. */ |
886 | | /* -------------------------------------------------------------------- */ |
887 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
888 | 0 | { |
889 | 0 | if (psNode->apSubNode[i]) |
890 | 0 | CPLQuadTreeCollectFeatures(hQuadTree, psNode->apSubNode[i], pAoi, |
891 | 0 | pnFeatureCount, pnMaxFeatures, |
892 | 0 | pppFeatureList); |
893 | 0 | } |
894 | 0 | } |
895 | | |
896 | | /************************************************************************/ |
897 | | /* CPLQuadTreeSearch() */ |
898 | | /************************************************************************/ |
899 | | |
900 | | /** |
901 | | * Returns all the elements inserted whose bounding box intersects the |
902 | | * provided area of interest |
903 | | * |
904 | | * @param hQuadTree the quad tree |
905 | | * @param pAoi the pointer to the area of interest |
906 | | * @param pnFeatureCount the user data provided to the function. |
907 | | * |
908 | | * @return an array of features that must be freed with CPLFree |
909 | | */ |
910 | | |
911 | | void **CPLQuadTreeSearch(const CPLQuadTree *hQuadTree, const CPLRectObj *pAoi, |
912 | | int *pnFeatureCount) |
913 | 0 | { |
914 | 0 | CPLAssert(hQuadTree); |
915 | 0 | CPLAssert(pAoi); |
916 | | |
917 | 0 | int nFeatureCount = 0; |
918 | 0 | if (pnFeatureCount == nullptr) |
919 | 0 | pnFeatureCount = &nFeatureCount; |
920 | |
|
921 | 0 | *pnFeatureCount = 0; |
922 | |
|
923 | 0 | int nMaxFeatures = 0; |
924 | 0 | void **ppFeatureList = nullptr; |
925 | 0 | CPLQuadTreeCollectFeatures(hQuadTree, hQuadTree->psRoot, pAoi, |
926 | 0 | pnFeatureCount, &nMaxFeatures, &ppFeatureList); |
927 | |
|
928 | 0 | return ppFeatureList; |
929 | 0 | } |
930 | | |
931 | | /************************************************************************/ |
932 | | /* CPLQuadTreeHasMatch() */ |
933 | | /************************************************************************/ |
934 | | |
935 | | static bool CPLQuadTreeHasMatch(const CPLQuadTree *hQuadTree, |
936 | | const QuadTreeNode *psNode, |
937 | | const CPLRectObj *pAoi) |
938 | 0 | { |
939 | | /* -------------------------------------------------------------------- */ |
940 | | /* Does this psNode overlap the area of interest at all? */ |
941 | | /* -------------------------------------------------------------------- */ |
942 | 0 | if (!CPL_RectOverlap(&psNode->rect, pAoi)) |
943 | 0 | return false; |
944 | | |
945 | | /* -------------------------------------------------------------------- */ |
946 | | /* Check the local features. */ |
947 | | /* -------------------------------------------------------------------- */ |
948 | 0 | for (int i = 0; i < psNode->nFeatures; i++) |
949 | 0 | { |
950 | 0 | if (hQuadTree->pfnGetBounds == nullptr && |
951 | 0 | hQuadTree->pfnGetBoundsEx == nullptr) |
952 | 0 | { |
953 | 0 | if (CPL_RectOverlap(&psNode->pasBounds[i], pAoi)) |
954 | 0 | return true; |
955 | 0 | } |
956 | 0 | else |
957 | 0 | { |
958 | 0 | CPLRectObj bounds; |
959 | 0 | if (hQuadTree->pfnGetBoundsEx) |
960 | 0 | hQuadTree->pfnGetBoundsEx(psNode->pahFeatures[i], |
961 | 0 | hQuadTree->pUserData, &bounds); |
962 | 0 | else |
963 | 0 | hQuadTree->pfnGetBounds(psNode->pahFeatures[i], &bounds); |
964 | |
|
965 | 0 | if (CPL_RectOverlap(&bounds, pAoi)) |
966 | 0 | return true; |
967 | 0 | } |
968 | 0 | } |
969 | | |
970 | | /* -------------------------------------------------------------------- */ |
971 | | /* Recurse to subnodes if they exist. */ |
972 | | /* -------------------------------------------------------------------- */ |
973 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
974 | 0 | { |
975 | 0 | if (psNode->apSubNode[i]) |
976 | 0 | { |
977 | 0 | if (CPLQuadTreeHasMatch(hQuadTree, psNode->apSubNode[i], pAoi)) |
978 | 0 | { |
979 | 0 | return true; |
980 | 0 | } |
981 | 0 | } |
982 | 0 | } |
983 | | |
984 | 0 | return false; |
985 | 0 | } |
986 | | |
987 | | /** |
988 | | * Returns whether the quadtree has at least one element whose bounding box |
989 | | * intersects the provided area of interest |
990 | | * |
991 | | * @param hQuadTree the quad tree |
992 | | * @param pAoi the pointer to the area of interest |
993 | | */ |
994 | | |
995 | | bool CPLQuadTreeHasMatch(const CPLQuadTree *hQuadTree, const CPLRectObj *pAoi) |
996 | 0 | { |
997 | 0 | CPLAssert(hQuadTree); |
998 | 0 | CPLAssert(pAoi); |
999 | | |
1000 | 0 | return CPLQuadTreeHasMatch(hQuadTree, hQuadTree->psRoot, pAoi); |
1001 | 0 | } |
1002 | | |
1003 | | /************************************************************************/ |
1004 | | /* CPLQuadTreeNodeForeach() */ |
1005 | | /************************************************************************/ |
1006 | | |
1007 | | static bool CPLQuadTreeNodeForeach(const QuadTreeNode *psNode, |
1008 | | CPLQuadTreeForeachFunc pfnForeach, |
1009 | | void *pUserData) |
1010 | 0 | { |
1011 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
1012 | 0 | { |
1013 | 0 | if (!CPLQuadTreeNodeForeach(psNode->apSubNode[i], pfnForeach, |
1014 | 0 | pUserData)) |
1015 | 0 | return false; |
1016 | 0 | } |
1017 | | |
1018 | 0 | for (int i = 0; i < psNode->nFeatures; i++) |
1019 | 0 | { |
1020 | 0 | if (pfnForeach(psNode->pahFeatures[i], pUserData) == FALSE) |
1021 | 0 | return false; |
1022 | 0 | } |
1023 | | |
1024 | 0 | return true; |
1025 | 0 | } |
1026 | | |
1027 | | /************************************************************************/ |
1028 | | /* CPLQuadTreeForeach() */ |
1029 | | /************************************************************************/ |
1030 | | |
1031 | | /** |
1032 | | * Walk through the quadtree and runs the provided function on all the |
1033 | | * elements |
1034 | | * |
1035 | | * This function is provided with the user_data argument of pfnForeach. |
1036 | | * It must return TRUE to go on the walk through the hash set, or FALSE to |
1037 | | * make it stop. |
1038 | | * |
1039 | | * Note : the structure of the quadtree must *NOT* be modified during the |
1040 | | * walk. |
1041 | | * |
1042 | | * @param hQuadTree the quad tree |
1043 | | * @param pfnForeach the function called on each element. |
1044 | | * @param pUserData the user data provided to the function. |
1045 | | */ |
1046 | | |
1047 | | void CPLQuadTreeForeach(const CPLQuadTree *hQuadTree, |
1048 | | CPLQuadTreeForeachFunc pfnForeach, void *pUserData) |
1049 | 0 | { |
1050 | 0 | CPLAssert(hQuadTree); |
1051 | 0 | CPLAssert(pfnForeach); |
1052 | 0 | CPLQuadTreeNodeForeach(hQuadTree->psRoot, pfnForeach, pUserData); |
1053 | 0 | } |
1054 | | |
1055 | | /************************************************************************/ |
1056 | | /* CPLQuadTreeDumpNode() */ |
1057 | | /************************************************************************/ |
1058 | | |
1059 | | static void CPLQuadTreeDumpNode(const QuadTreeNode *psNode, int nIndentLevel, |
1060 | | CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc, |
1061 | | void *pUserData) |
1062 | 0 | { |
1063 | 0 | if (psNode->nNumSubNodes) |
1064 | 0 | { |
1065 | 0 | for (int count = nIndentLevel; --count >= 0;) |
1066 | 0 | { |
1067 | 0 | printf(" "); /*ok*/ |
1068 | 0 | } |
1069 | 0 | printf("SubhQuadTrees :\n"); /*ok*/ |
1070 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
1071 | 0 | { |
1072 | 0 | for (int count = nIndentLevel + 1; --count >= 0;) |
1073 | 0 | { |
1074 | 0 | printf(" "); /*ok*/ |
1075 | 0 | } |
1076 | 0 | printf("SubhQuadTree %d :\n", i + 1); /*ok*/ |
1077 | 0 | CPLQuadTreeDumpNode(psNode->apSubNode[i], nIndentLevel + 2, |
1078 | 0 | pfnDumpFeatureFunc, pUserData); |
1079 | 0 | } |
1080 | 0 | } |
1081 | 0 | if (psNode->nFeatures) |
1082 | 0 | { |
1083 | 0 | for (int count = nIndentLevel; --count >= 0;) |
1084 | 0 | printf(" "); /*ok*/ |
1085 | 0 | printf("Leaves (%d):\n", psNode->nFeatures); /*ok*/ |
1086 | 0 | for (int i = 0; i < psNode->nFeatures; i++) |
1087 | 0 | { |
1088 | 0 | if (pfnDumpFeatureFunc) |
1089 | 0 | { |
1090 | 0 | pfnDumpFeatureFunc(psNode->pahFeatures[i], nIndentLevel + 2, |
1091 | 0 | pUserData); |
1092 | 0 | } |
1093 | 0 | else |
1094 | 0 | { |
1095 | 0 | for (int count = nIndentLevel + 1; --count >= 0;) |
1096 | 0 | { |
1097 | 0 | printf(" "); /*ok*/ |
1098 | 0 | } |
1099 | 0 | printf("%p\n", psNode->pahFeatures[i]); /*ok*/ |
1100 | 0 | } |
1101 | 0 | } |
1102 | 0 | } |
1103 | 0 | } |
1104 | | |
1105 | | /************************************************************************/ |
1106 | | /* CPLQuadTreeDump() */ |
1107 | | /************************************************************************/ |
1108 | | |
1109 | | /** Dump quad tree */ |
1110 | | void CPLQuadTreeDump(const CPLQuadTree *hQuadTree, |
1111 | | CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc, |
1112 | | void *pUserData) |
1113 | 0 | { |
1114 | 0 | CPLQuadTreeDumpNode(hQuadTree->psRoot, 0, pfnDumpFeatureFunc, pUserData); |
1115 | 0 | } |
1116 | | |
1117 | | /************************************************************************/ |
1118 | | /* CPLQuadTreeGetStatsNode() */ |
1119 | | /************************************************************************/ |
1120 | | |
1121 | | static void CPLQuadTreeGetStatsNode(const QuadTreeNode *psNode, int nDepthLevel, |
1122 | | int *pnNodeCount, int *pnMaxDepth, |
1123 | | int *pnMaxBucketCapacity) |
1124 | 0 | { |
1125 | 0 | (*pnNodeCount)++; |
1126 | 0 | if (nDepthLevel > *pnMaxDepth) |
1127 | 0 | *pnMaxDepth = nDepthLevel; |
1128 | 0 | if (psNode->nFeatures > *pnMaxBucketCapacity) |
1129 | 0 | *pnMaxBucketCapacity = psNode->nFeatures; |
1130 | |
|
1131 | 0 | for (int i = 0; i < psNode->nNumSubNodes; i++) |
1132 | 0 | { |
1133 | 0 | CPLQuadTreeGetStatsNode(psNode->apSubNode[i], nDepthLevel + 1, |
1134 | 0 | pnNodeCount, pnMaxDepth, pnMaxBucketCapacity); |
1135 | 0 | } |
1136 | 0 | } |
1137 | | |
1138 | | /************************************************************************/ |
1139 | | /* CPLQuadTreeGetStats() */ |
1140 | | /************************************************************************/ |
1141 | | |
1142 | | /** Get stats */ |
1143 | | void CPLQuadTreeGetStats(const CPLQuadTree *hQuadTree, int *pnFeatureCount, |
1144 | | int *pnNodeCount, int *pnMaxDepth, |
1145 | | int *pnMaxBucketCapacity) |
1146 | 0 | { |
1147 | 0 | CPLAssert(hQuadTree); |
1148 | | |
1149 | 0 | int nFeatureCount = 0; |
1150 | 0 | if (pnFeatureCount == nullptr) |
1151 | 0 | pnFeatureCount = &nFeatureCount; |
1152 | 0 | int nNodeCount = 0; |
1153 | 0 | if (pnNodeCount == nullptr) |
1154 | 0 | pnNodeCount = &nNodeCount; |
1155 | 0 | int nMaxDepth = 0; |
1156 | 0 | if (pnMaxDepth == nullptr) |
1157 | 0 | pnMaxDepth = &nMaxDepth; |
1158 | 0 | int nMaxBucketCapacity = 0; |
1159 | 0 | if (pnMaxBucketCapacity == nullptr) |
1160 | 0 | pnMaxBucketCapacity = &nMaxBucketCapacity; |
1161 | |
|
1162 | 0 | *pnFeatureCount = hQuadTree->nFeatures; |
1163 | 0 | *pnNodeCount = 0; |
1164 | 0 | *pnMaxDepth = 1; |
1165 | 0 | *pnMaxBucketCapacity = 0; |
1166 | |
|
1167 | 0 | CPLQuadTreeGetStatsNode(hQuadTree->psRoot, 0, pnNodeCount, pnMaxDepth, |
1168 | 0 | pnMaxBucketCapacity); |
1169 | | |
1170 | | // TODO(schwehr): If any of the pointers were set to local vars, |
1171 | | // do they need to be reset to a nullptr? |
1172 | 0 | } |