Coverage Report

Created: 2025-06-13 06:18

/src/gdal/port/cpl_quad_tree.cpp
Line
Count
Source (jump to first uncovered line)
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
413
0
            if (psNode->apSubNode[i]->nFeatures == 0 &&
414
0
                psNode->apSubNode[i]->nNumSubNodes == 0)
415
0
            {
416
0
                CPLQuadTreeNodeDestroy(psNode->apSubNode[i]);
417
0
                if (i < psNode->nNumSubNodes - 1)
418
0
                {
419
#ifdef CSA_BUILD
420
                    for (int j = 0; j < psNode->nNumSubNodes - 1 - i; ++j)
421
                        psNode->apSubNode[i + j] = psNode->apSubNode[i + j + 1];
422
#else
423
0
                    memmove(psNode->apSubNode + i, psNode->apSubNode + i + 1,
424
0
                            (psNode->nNumSubNodes - 1 - i) *
425
0
                                sizeof(QuadTreeNode *));
426
0
#endif
427
0
                }
428
0
                i--;
429
0
                psNode->nNumSubNodes--;
430
0
            }
431
0
        }
432
0
    }
433
434
0
    return bRemoved;
435
0
}
436
437
/**
438
 * Remove a feature from a quadtree.
439
 *
440
 * Currently the quadtree is not re-balanced.
441
 *
442
 * @param hQuadTree the quad tree
443
 * @param hFeature the feature to remove
444
 * @param psBounds bounds of the feature (or NULL if pfnGetBounds has been
445
 * filled)
446
 */
447
void CPLQuadTreeRemove(CPLQuadTree *hQuadTree, void *hFeature,
448
                       const CPLRectObj *psBounds)
449
0
{
450
0
    if (psBounds == nullptr && hQuadTree->pfnGetBounds == nullptr &&
451
0
        hQuadTree->pfnGetBoundsEx == nullptr)
452
0
    {
453
0
        CPLError(CE_Failure, CPLE_AppDefined,
454
0
                 "hQuadTree->pfnGetBounds == NULL");
455
0
        return;
456
0
    }
457
0
    CPLRectObj bounds;  // keep variable in this outer scope
458
0
    if (psBounds == nullptr)
459
0
    {
460
0
        if (hQuadTree->pfnGetBoundsEx)
461
0
            hQuadTree->pfnGetBoundsEx(hFeature, hQuadTree->pUserData, &bounds);
462
0
        else
463
0
            hQuadTree->pfnGetBounds(hFeature, &bounds);
464
0
        psBounds = &bounds;
465
0
    }
466
0
    if (CPLQuadTreeRemoveInternal(hQuadTree->psRoot, hFeature, psBounds))
467
0
    {
468
0
        hQuadTree->nFeatures--;
469
0
    }
470
0
}
471
472
/************************************************************************/
473
/*                    CPLQuadTreeNodeDestroy()                          */
474
/************************************************************************/
475
476
static void CPLQuadTreeNodeDestroy(QuadTreeNode *psNode)
477
0
{
478
0
    for (int i = 0; i < psNode->nNumSubNodes; i++)
479
0
    {
480
0
        if (psNode->apSubNode[i])
481
0
            CPLQuadTreeNodeDestroy(psNode->apSubNode[i]);
482
0
    }
483
484
0
    if (psNode->pahFeatures)
485
0
    {
486
0
        CPLFree(psNode->pahFeatures);
487
0
        CPLFree(psNode->pasBounds);
488
0
    }
489
490
0
    CPLFree(psNode);
491
0
}
492
493
/************************************************************************/
494
/*                       CPLQuadTreeDestroy()                           */
495
/************************************************************************/
496
497
/**
498
 * Destroy a quadtree
499
 *
500
 * @param hQuadTree the quad tree to destroy
501
 */
502
503
void CPLQuadTreeDestroy(CPLQuadTree *hQuadTree)
504
0
{
505
0
    CPLAssert(hQuadTree);
506
0
    CPLQuadTreeNodeDestroy(hQuadTree->psRoot);
507
0
    CPLFree(hQuadTree);
508
0
}
509
510
/************************************************************************/
511
/*                     CPLQuadTreeSplitBounds()                         */
512
/************************************************************************/
513
514
static void CPLQuadTreeSplitBounds(double dfSplitRatio, const CPLRectObj *in,
515
                                   CPLRectObj *out1, CPLRectObj *out2)
516
0
{
517
    /* -------------------------------------------------------------------- */
518
    /*      The output bounds will be very similar to the input bounds,     */
519
    /*      so just copy over to start.                                     */
520
    /* -------------------------------------------------------------------- */
521
0
    memcpy(out1, in, sizeof(CPLRectObj));
522
0
    memcpy(out2, in, sizeof(CPLRectObj));
523
524
    /* -------------------------------------------------------------------- */
525
    /*      Split in X direction.                                           */
526
    /* -------------------------------------------------------------------- */
527
0
    if ((in->maxx - in->minx) > (in->maxy - in->miny))
528
0
    {
529
0
        const double range = in->maxx - in->minx;
530
531
0
        out1->maxx = in->minx + range * dfSplitRatio;
532
0
        out2->minx = in->maxx - range * dfSplitRatio;
533
0
    }
534
535
    /* -------------------------------------------------------------------- */
536
    /*      Otherwise split in Y direction.                                 */
537
    /* -------------------------------------------------------------------- */
538
0
    else
539
0
    {
540
0
        const double range = in->maxy - in->miny;
541
542
0
        out1->maxy = in->miny + range * dfSplitRatio;
543
0
        out2->miny = in->maxy - range * dfSplitRatio;
544
0
    }
545
0
}
546
547
/************************************************************************/
548
/*                  CPLQuadTreeNodeAddFeatureAlg1()                     */
549
/************************************************************************/
550
551
static void CPLQuadTreeNodeAddFeatureAlg1(CPLQuadTree *hQuadTree,
552
                                          QuadTreeNode *psNode, void *hFeature,
553
                                          const CPLRectObj *pRect)
554
0
{
555
0
    if (psNode->nNumSubNodes == 0)
556
0
    {
557
        // If we have reached the max bucket capacity, try to insert
558
        // in a subnode if possible.
559
0
        if (psNode->nFeatures >= hQuadTree->nBucketCapacity)
560
0
        {
561
0
            CPLRectObj half1 = {0.0, 0.0, 0.0, 0.0};
562
0
            CPLRectObj half2 = {0.0, 0.0, 0.0, 0.0};
563
0
            CPLRectObj quad1 = {0.0, 0.0, 0.0, 0.0};
564
0
            CPLRectObj quad2 = {0.0, 0.0, 0.0, 0.0};
565
0
            CPLRectObj quad3 = {0.0, 0.0, 0.0, 0.0};
566
0
            CPLRectObj quad4 = {0.0, 0.0, 0.0, 0.0};
567
568
0
            CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect,
569
0
                                   &half1, &half2);
570
0
            CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1,
571
0
                                   &quad2);
572
0
            CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3,
573
0
                                   &quad4);
574
575
0
            if (memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 &&
576
0
                memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 &&
577
0
                memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 &&
578
0
                memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 &&
579
0
                (hQuadTree->bForceUseOfSubNodes ||
580
0
                 CPL_RectContained(pRect, &quad1) ||
581
0
                 CPL_RectContained(pRect, &quad2) ||
582
0
                 CPL_RectContained(pRect, &quad3) ||
583
0
                 CPL_RectContained(pRect, &quad4)))
584
0
            {
585
0
                psNode->nNumSubNodes = 4;
586
0
                psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1);
587
0
                psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2);
588
0
                psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3);
589
0
                psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4);
590
591
0
                const int oldNumFeatures = psNode->nFeatures;
592
0
                void **oldFeatures = psNode->pahFeatures;
593
0
                CPLRectObj *pasOldBounds = psNode->pasBounds;
594
0
                psNode->nFeatures = 0;
595
0
                psNode->pahFeatures = nullptr;
596
0
                psNode->pasBounds = nullptr;
597
598
                // Redispatch existing pahFeatures in apSubNodes.
599
0
                for (int i = 0; i < oldNumFeatures; i++)
600
0
                {
601
0
                    if (hQuadTree->pfnGetBounds == nullptr &&
602
0
                        hQuadTree->pfnGetBoundsEx == nullptr)
603
0
                        CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode,
604
0
                                                      oldFeatures[i],
605
0
                                                      &pasOldBounds[i]);
606
0
                    else
607
0
                    {
608
0
                        CPLRectObj bounds;
609
0
                        if (hQuadTree->pfnGetBoundsEx)
610
0
                            hQuadTree->pfnGetBoundsEx(
611
0
                                oldFeatures[i], hQuadTree->pUserData, &bounds);
612
0
                        else
613
0
                            hQuadTree->pfnGetBounds(oldFeatures[i], &bounds);
614
0
                        CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode,
615
0
                                                      oldFeatures[i], &bounds);
616
0
                    }
617
0
                }
618
619
0
                CPLFree(oldFeatures);
620
0
                CPLFree(pasOldBounds);
621
622
                /* recurse back on this psNode now that it has apSubNodes */
623
0
                CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, hFeature,
624
0
                                              pRect);
625
0
                return;
626
0
            }
627
0
        }
628
0
    }
629
0
    else
630
0
    {
631
        /* --------------------------------------------------------------------
632
         */
633
        /*      If there are apSubNodes, then consider whether this object */
634
        /*      will fit in them. */
635
        /* --------------------------------------------------------------------
636
         */
637
0
        for (int i = 0; i < psNode->nNumSubNodes; i++)
638
0
        {
639
0
            if (CPL_RectContained(pRect, &psNode->apSubNode[i]->rect))
640
0
            {
641
0
                CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode->apSubNode[i],
642
0
                                              hFeature, pRect);
643
0
                return;
644
0
            }
645
0
        }
646
0
        if (hQuadTree->bForceUseOfSubNodes)
647
0
        {
648
0
            bool overlaps[4];
649
0
            bool overlapAll = true;
650
0
            for (int i = 0; i < psNode->nNumSubNodes; i++)
651
0
            {
652
0
                overlaps[i] =
653
0
                    CPL_RectOverlap(pRect, &psNode->apSubNode[i]->rect);
654
0
                if (!overlaps[i])
655
0
                    overlapAll = false;
656
0
            }
657
0
            if (!overlapAll)
658
0
            {
659
0
                for (int i = 0; i < psNode->nNumSubNodes; i++)
660
0
                {
661
0
                    if (overlaps[i])
662
0
                    {
663
0
                        CPLRectObj intersection;
664
0
                        intersection.minx = std::max(
665
0
                            pRect->minx, psNode->apSubNode[i]->rect.minx);
666
0
                        intersection.miny = std::max(
667
0
                            pRect->miny, psNode->apSubNode[i]->rect.miny);
668
0
                        intersection.maxx = std::min(
669
0
                            pRect->maxx, psNode->apSubNode[i]->rect.maxx);
670
0
                        intersection.maxy = std::min(
671
0
                            pRect->maxy, psNode->apSubNode[i]->rect.maxy);
672
0
                        CPLQuadTreeNodeAddFeatureAlg1(hQuadTree,
673
0
                                                      psNode->apSubNode[i],
674
0
                                                      hFeature, &intersection);
675
0
                    }
676
0
                }
677
0
                return;
678
0
            }
679
0
        }
680
0
    }
681
682
    /* -------------------------------------------------------------------- */
683
    /*      If none of that worked, just add it to this psNodes list.         */
684
    /* -------------------------------------------------------------------- */
685
0
    psNode->nFeatures++;
686
687
0
    if (psNode->nFeatures == 1)
688
0
    {
689
0
        CPLAssert(psNode->pahFeatures == nullptr);
690
0
        psNode->pahFeatures = static_cast<void **>(
691
0
            CPLMalloc(hQuadTree->nBucketCapacity * sizeof(void *)));
692
0
        if (hQuadTree->pfnGetBounds == nullptr &&
693
0
            hQuadTree->pfnGetBoundsEx == nullptr)
694
0
            psNode->pasBounds = static_cast<CPLRectObj *>(
695
0
                CPLMalloc(hQuadTree->nBucketCapacity * sizeof(CPLRectObj)));
696
0
    }
697
0
    else if (psNode->nFeatures > hQuadTree->nBucketCapacity)
698
0
    {
699
0
        psNode->pahFeatures = static_cast<void **>(CPLRealloc(
700
0
            psNode->pahFeatures, sizeof(void *) * psNode->nFeatures));
701
0
        if (hQuadTree->pfnGetBounds == nullptr &&
702
0
            hQuadTree->pfnGetBoundsEx == nullptr)
703
0
            psNode->pasBounds = static_cast<CPLRectObj *>(CPLRealloc(
704
0
                psNode->pasBounds, sizeof(CPLRectObj) * psNode->nFeatures));
705
0
    }
706
0
    psNode->pahFeatures[psNode->nFeatures - 1] = hFeature;
707
0
    if (hQuadTree->pfnGetBounds == nullptr &&
708
0
        hQuadTree->pfnGetBoundsEx == nullptr)
709
0
        psNode->pasBounds[psNode->nFeatures - 1] = *pRect;
710
711
0
    return;
712
0
}
713
714
/************************************************************************/
715
/*                  CPLQuadTreeNodeAddFeatureAlg2()                     */
716
/************************************************************************/
717
718
static void CPLQuadTreeNodeAddFeatureAlg2(CPLQuadTree *hQuadTree,
719
                                          QuadTreeNode *psNode, void *hFeature,
720
                                          const CPLRectObj *pRect,
721
                                          int nMaxDepth)
722
0
{
723
    /* -------------------------------------------------------------------- */
724
    /*      If there are apSubNodes, then consider whether this object      */
725
    /*      will fit in them.                                               */
726
    /* -------------------------------------------------------------------- */
727
0
    if (nMaxDepth > 1 && psNode->nNumSubNodes > 0)
728
0
    {
729
0
        for (int i = 0; i < psNode->nNumSubNodes; i++)
730
0
        {
731
0
            if (CPL_RectContained(pRect, &psNode->apSubNode[i]->rect))
732
0
            {
733
0
                CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, psNode->apSubNode[i],
734
0
                                              hFeature, pRect, nMaxDepth - 1);
735
0
                return;
736
0
            }
737
0
        }
738
0
    }
739
740
    /* -------------------------------------------------------------------- */
741
    /*      Otherwise, consider creating four apSubNodes if could fit into  */
742
    /*      them, and adding to the appropriate apSubNode.                  */
743
    /* -------------------------------------------------------------------- */
744
0
    else if (nMaxDepth > 1 && psNode->nNumSubNodes == 0)
745
0
    {
746
0
        CPLRectObj half1, half2, quad1, quad2, quad3, quad4;
747
748
0
        CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect, &half1,
749
0
                               &half2);
750
0
        CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1, &quad2);
751
0
        CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3, &quad4);
752
753
0
        if (memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 &&
754
0
            memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 &&
755
0
            memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 &&
756
0
            memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 &&
757
0
            (CPL_RectContained(pRect, &quad1) ||
758
0
             CPL_RectContained(pRect, &quad2) ||
759
0
             CPL_RectContained(pRect, &quad3) ||
760
0
             CPL_RectContained(pRect, &quad4)))
761
0
        {
762
0
            psNode->nNumSubNodes = 4;
763
0
            psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1);
764
0
            psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2);
765
0
            psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3);
766
0
            psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4);
767
768
            /* recurse back on this psNode now that it has apSubNodes */
769
0
            CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, psNode, hFeature, pRect,
770
0
                                          nMaxDepth);
771
0
            return;
772
0
        }
773
0
    }
774
775
    /* -------------------------------------------------------------------- */
776
    /*      If none of that worked, just add it to this psNodes list.       */
777
    /* -------------------------------------------------------------------- */
778
0
    psNode->nFeatures++;
779
780
0
    psNode->pahFeatures = static_cast<void **>(
781
0
        CPLRealloc(psNode->pahFeatures, sizeof(void *) * psNode->nFeatures));
782
0
    if (hQuadTree->pfnGetBounds == nullptr &&
783
0
        hQuadTree->pfnGetBoundsEx == nullptr)
784
0
    {
785
0
        psNode->pasBounds = static_cast<CPLRectObj *>(CPLRealloc(
786
0
            psNode->pasBounds, sizeof(CPLRectObj) * psNode->nFeatures));
787
0
    }
788
0
    psNode->pahFeatures[psNode->nFeatures - 1] = hFeature;
789
0
    if (hQuadTree->pfnGetBounds == nullptr &&
790
0
        hQuadTree->pfnGetBoundsEx == nullptr)
791
0
    {
792
0
        psNode->pasBounds[psNode->nFeatures - 1] = *pRect;
793
0
    }
794
0
}
795
796
/************************************************************************/
797
/*                  CPLQuadTreeAddFeatureInternal()                     */
798
/************************************************************************/
799
800
static void CPLQuadTreeAddFeatureInternal(CPLQuadTree *hQuadTree,
801
                                          void *hFeature,
802
                                          const CPLRectObj *pRect)
803
0
{
804
0
    if (hQuadTree->nMaxDepth == 0)
805
0
    {
806
0
        CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, hQuadTree->psRoot, hFeature,
807
0
                                      pRect);
808
0
    }
809
0
    else
810
0
    {
811
0
        CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, hQuadTree->psRoot, hFeature,
812
0
                                      pRect, hQuadTree->nMaxDepth);
813
0
    }
814
0
}
815
816
/************************************************************************/
817
/*                     CPLQuadTreeCollectFeatures()                     */
818
/************************************************************************/
819
820
static void CPLQuadTreeCollectFeatures(const CPLQuadTree *hQuadTree,
821
                                       const QuadTreeNode *psNode,
822
                                       const CPLRectObj *pAoi,
823
                                       int *pnFeatureCount, int *pnMaxFeatures,
824
                                       void ***pppFeatureList)
825
0
{
826
    /* -------------------------------------------------------------------- */
827
    /*      Does this psNode overlap the area of interest at all?  If not,  */
828
    /*      return without adding to the list at all.                       */
829
    /* -------------------------------------------------------------------- */
830
0
    if (!CPL_RectOverlap(&psNode->rect, pAoi))
831
0
        return;
832
833
    /* -------------------------------------------------------------------- */
834
    /*      Grow the list to hold the features on this psNode.              */
835
    /* -------------------------------------------------------------------- */
836
0
    if (*pnFeatureCount + psNode->nFeatures > *pnMaxFeatures)
837
0
    {
838
        // TODO(schwehr): Symbolic constant.
839
0
        *pnMaxFeatures = (*pnFeatureCount + psNode->nFeatures) * 2 + 20;
840
0
        *pppFeatureList = static_cast<void **>(
841
0
            CPLRealloc(*pppFeatureList, sizeof(void *) * *pnMaxFeatures));
842
0
    }
843
844
    /* -------------------------------------------------------------------- */
845
    /*      Add the local features to the list.                             */
846
    /* -------------------------------------------------------------------- */
847
0
    for (int i = 0; i < psNode->nFeatures; i++)
848
0
    {
849
0
        if (hQuadTree->pfnGetBounds == nullptr &&
850
0
            hQuadTree->pfnGetBoundsEx == nullptr)
851
0
        {
852
0
            if (CPL_RectOverlap(&psNode->pasBounds[i], pAoi))
853
0
                (*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i];
854
0
        }
855
0
        else
856
0
        {
857
0
            CPLRectObj bounds;
858
0
            if (hQuadTree->pfnGetBoundsEx)
859
0
                hQuadTree->pfnGetBoundsEx(psNode->pahFeatures[i],
860
0
                                          hQuadTree->pUserData, &bounds);
861
0
            else
862
0
                hQuadTree->pfnGetBounds(psNode->pahFeatures[i], &bounds);
863
864
0
            if (CPL_RectOverlap(&bounds, pAoi))
865
0
                (*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i];
866
0
        }
867
0
    }
868
869
    /* -------------------------------------------------------------------- */
870
    /*      Recurse to subnodes if they exist.                              */
871
    /* -------------------------------------------------------------------- */
872
0
    for (int i = 0; i < psNode->nNumSubNodes; i++)
873
0
    {
874
0
        if (psNode->apSubNode[i])
875
0
            CPLQuadTreeCollectFeatures(hQuadTree, psNode->apSubNode[i], pAoi,
876
0
                                       pnFeatureCount, pnMaxFeatures,
877
0
                                       pppFeatureList);
878
0
    }
879
0
}
880
881
/************************************************************************/
882
/*                         CPLQuadTreeSearch()                          */
883
/************************************************************************/
884
885
/**
886
 * Returns all the elements inserted whose bounding box intersects the
887
 * provided area of interest
888
 *
889
 * @param hQuadTree the quad tree
890
 * @param pAoi the pointer to the area of interest
891
 * @param pnFeatureCount the user data provided to the function.
892
 *
893
 * @return an array of features that must be freed with CPLFree
894
 */
895
896
void **CPLQuadTreeSearch(const CPLQuadTree *hQuadTree, const CPLRectObj *pAoi,
897
                         int *pnFeatureCount)
898
0
{
899
0
    CPLAssert(hQuadTree);
900
0
    CPLAssert(pAoi);
901
902
0
    int nFeatureCount = 0;
903
0
    if (pnFeatureCount == nullptr)
904
0
        pnFeatureCount = &nFeatureCount;
905
906
0
    *pnFeatureCount = 0;
907
908
0
    int nMaxFeatures = 0;
909
0
    void **ppFeatureList = nullptr;
910
0
    CPLQuadTreeCollectFeatures(hQuadTree, hQuadTree->psRoot, pAoi,
911
0
                               pnFeatureCount, &nMaxFeatures, &ppFeatureList);
912
913
0
    return ppFeatureList;
914
0
}
915
916
/************************************************************************/
917
/*                         CPLQuadTreeHasMatch()                        */
918
/************************************************************************/
919
920
static bool CPLQuadTreeHasMatch(const CPLQuadTree *hQuadTree,
921
                                const QuadTreeNode *psNode,
922
                                const CPLRectObj *pAoi)
923
0
{
924
    /* -------------------------------------------------------------------- */
925
    /*      Does this psNode overlap the area of interest at all?           */
926
    /* -------------------------------------------------------------------- */
927
0
    if (!CPL_RectOverlap(&psNode->rect, pAoi))
928
0
        return false;
929
930
    /* -------------------------------------------------------------------- */
931
    /*      Check the local features.                                       */
932
    /* -------------------------------------------------------------------- */
933
0
    for (int i = 0; i < psNode->nFeatures; i++)
934
0
    {
935
0
        if (hQuadTree->pfnGetBounds == nullptr &&
936
0
            hQuadTree->pfnGetBoundsEx == nullptr)
937
0
        {
938
0
            if (CPL_RectOverlap(&psNode->pasBounds[i], pAoi))
939
0
                return true;
940
0
        }
941
0
        else
942
0
        {
943
0
            CPLRectObj bounds;
944
0
            if (hQuadTree->pfnGetBoundsEx)
945
0
                hQuadTree->pfnGetBoundsEx(psNode->pahFeatures[i],
946
0
                                          hQuadTree->pUserData, &bounds);
947
0
            else
948
0
                hQuadTree->pfnGetBounds(psNode->pahFeatures[i], &bounds);
949
950
0
            if (CPL_RectOverlap(&bounds, pAoi))
951
0
                return true;
952
0
        }
953
0
    }
954
955
    /* -------------------------------------------------------------------- */
956
    /*      Recurse to subnodes if they exist.                              */
957
    /* -------------------------------------------------------------------- */
958
0
    for (int i = 0; i < psNode->nNumSubNodes; i++)
959
0
    {
960
0
        if (psNode->apSubNode[i])
961
0
        {
962
0
            if (CPLQuadTreeHasMatch(hQuadTree, psNode->apSubNode[i], pAoi))
963
0
            {
964
0
                return true;
965
0
            }
966
0
        }
967
0
    }
968
969
0
    return false;
970
0
}
971
972
/**
973
 * Returns whether the quadtree has at least one element whose bounding box
974
 * intersects the provided area of interest
975
 *
976
 * @param hQuadTree the quad tree
977
 * @param pAoi the pointer to the area of interest
978
 */
979
980
bool CPLQuadTreeHasMatch(const CPLQuadTree *hQuadTree, const CPLRectObj *pAoi)
981
0
{
982
0
    CPLAssert(hQuadTree);
983
0
    CPLAssert(pAoi);
984
985
0
    return CPLQuadTreeHasMatch(hQuadTree, hQuadTree->psRoot, pAoi);
986
0
}
987
988
/************************************************************************/
989
/*                    CPLQuadTreeNodeForeach()                          */
990
/************************************************************************/
991
992
static bool CPLQuadTreeNodeForeach(const QuadTreeNode *psNode,
993
                                   CPLQuadTreeForeachFunc pfnForeach,
994
                                   void *pUserData)
995
0
{
996
0
    for (int i = 0; i < psNode->nNumSubNodes; i++)
997
0
    {
998
0
        if (!CPLQuadTreeNodeForeach(psNode->apSubNode[i], pfnForeach,
999
0
                                    pUserData))
1000
0
            return false;
1001
0
    }
1002
1003
0
    for (int i = 0; i < psNode->nFeatures; i++)
1004
0
    {
1005
0
        if (pfnForeach(psNode->pahFeatures[i], pUserData) == FALSE)
1006
0
            return false;
1007
0
    }
1008
1009
0
    return true;
1010
0
}
1011
1012
/************************************************************************/
1013
/*                       CPLQuadTreeForeach()                           */
1014
/************************************************************************/
1015
1016
/**
1017
 * Walk through the quadtree and runs the provided function on all the
1018
 * elements
1019
 *
1020
 * This function is provided with the user_data argument of pfnForeach.
1021
 * It must return TRUE to go on the walk through the hash set, or FALSE to
1022
 * make it stop.
1023
 *
1024
 * Note : the structure of the quadtree must *NOT* be modified during the
1025
 * walk.
1026
 *
1027
 * @param hQuadTree the quad tree
1028
 * @param pfnForeach the function called on each element.
1029
 * @param pUserData the user data provided to the function.
1030
 */
1031
1032
void CPLQuadTreeForeach(const CPLQuadTree *hQuadTree,
1033
                        CPLQuadTreeForeachFunc pfnForeach, void *pUserData)
1034
0
{
1035
0
    CPLAssert(hQuadTree);
1036
0
    CPLAssert(pfnForeach);
1037
0
    CPLQuadTreeNodeForeach(hQuadTree->psRoot, pfnForeach, pUserData);
1038
0
}
1039
1040
/************************************************************************/
1041
/*                       CPLQuadTreeDumpNode()                          */
1042
/************************************************************************/
1043
1044
static void CPLQuadTreeDumpNode(const QuadTreeNode *psNode, int nIndentLevel,
1045
                                CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc,
1046
                                void *pUserData)
1047
0
{
1048
0
    if (psNode->nNumSubNodes)
1049
0
    {
1050
0
        for (int count = nIndentLevel; --count >= 0;)
1051
0
        {
1052
0
            printf("  "); /*ok*/
1053
0
        }
1054
0
        printf("SubhQuadTrees :\n"); /*ok*/
1055
0
        for (int i = 0; i < psNode->nNumSubNodes; i++)
1056
0
        {
1057
0
            for (int count = nIndentLevel + 1; --count >= 0;)
1058
0
            {
1059
0
                printf("  "); /*ok*/
1060
0
            }
1061
0
            printf("SubhQuadTree %d :\n", i + 1); /*ok*/
1062
0
            CPLQuadTreeDumpNode(psNode->apSubNode[i], nIndentLevel + 2,
1063
0
                                pfnDumpFeatureFunc, pUserData);
1064
0
        }
1065
0
    }
1066
0
    if (psNode->nFeatures)
1067
0
    {
1068
0
        for (int count = nIndentLevel; --count >= 0;)
1069
0
            printf("  ");                            /*ok*/
1070
0
        printf("Leaves (%d):\n", psNode->nFeatures); /*ok*/
1071
0
        for (int i = 0; i < psNode->nFeatures; i++)
1072
0
        {
1073
0
            if (pfnDumpFeatureFunc)
1074
0
            {
1075
0
                pfnDumpFeatureFunc(psNode->pahFeatures[i], nIndentLevel + 2,
1076
0
                                   pUserData);
1077
0
            }
1078
0
            else
1079
0
            {
1080
0
                for (int count = nIndentLevel + 1; --count >= 0;)
1081
0
                {
1082
0
                    printf("  "); /*ok*/
1083
0
                }
1084
0
                printf("%p\n", psNode->pahFeatures[i]); /*ok*/
1085
0
            }
1086
0
        }
1087
0
    }
1088
0
}
1089
1090
/************************************************************************/
1091
/*                         CPLQuadTreeDump()                            */
1092
/************************************************************************/
1093
1094
/** Dump quad tree */
1095
void CPLQuadTreeDump(const CPLQuadTree *hQuadTree,
1096
                     CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc,
1097
                     void *pUserData)
1098
0
{
1099
0
    CPLQuadTreeDumpNode(hQuadTree->psRoot, 0, pfnDumpFeatureFunc, pUserData);
1100
0
}
1101
1102
/************************************************************************/
1103
/*                  CPLQuadTreeGetStatsNode()                           */
1104
/************************************************************************/
1105
1106
static void CPLQuadTreeGetStatsNode(const QuadTreeNode *psNode, int nDepthLevel,
1107
                                    int *pnNodeCount, int *pnMaxDepth,
1108
                                    int *pnMaxBucketCapacity)
1109
0
{
1110
0
    (*pnNodeCount)++;
1111
0
    if (nDepthLevel > *pnMaxDepth)
1112
0
        *pnMaxDepth = nDepthLevel;
1113
0
    if (psNode->nFeatures > *pnMaxBucketCapacity)
1114
0
        *pnMaxBucketCapacity = psNode->nFeatures;
1115
1116
0
    for (int i = 0; i < psNode->nNumSubNodes; i++)
1117
0
    {
1118
0
        CPLQuadTreeGetStatsNode(psNode->apSubNode[i], nDepthLevel + 1,
1119
0
                                pnNodeCount, pnMaxDepth, pnMaxBucketCapacity);
1120
0
    }
1121
0
}
1122
1123
/************************************************************************/
1124
/*                    CPLQuadTreeGetStats()                             */
1125
/************************************************************************/
1126
1127
/** Get stats */
1128
void CPLQuadTreeGetStats(const CPLQuadTree *hQuadTree, int *pnFeatureCount,
1129
                         int *pnNodeCount, int *pnMaxDepth,
1130
                         int *pnMaxBucketCapacity)
1131
0
{
1132
0
    CPLAssert(hQuadTree);
1133
1134
0
    int nFeatureCount = 0;
1135
0
    if (pnFeatureCount == nullptr)
1136
0
        pnFeatureCount = &nFeatureCount;
1137
0
    int nNodeCount = 0;
1138
0
    if (pnNodeCount == nullptr)
1139
0
        pnNodeCount = &nNodeCount;
1140
0
    int nMaxDepth = 0;
1141
0
    if (pnMaxDepth == nullptr)
1142
0
        pnMaxDepth = &nMaxDepth;
1143
0
    int nMaxBucketCapacity = 0;
1144
0
    if (pnMaxBucketCapacity == nullptr)
1145
0
        pnMaxBucketCapacity = &nMaxBucketCapacity;
1146
1147
0
    *pnFeatureCount = hQuadTree->nFeatures;
1148
0
    *pnNodeCount = 0;
1149
0
    *pnMaxDepth = 1;
1150
0
    *pnMaxBucketCapacity = 0;
1151
1152
0
    CPLQuadTreeGetStatsNode(hQuadTree->psRoot, 0, pnNodeCount, pnMaxDepth,
1153
0
                            pnMaxBucketCapacity);
1154
1155
    // TODO(schwehr): If any of the pointers were set to local vars,
1156
    // do they need to be reset to a nullptr?
1157
0
}