Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}