LCOV - code coverage report
Current view: top level - port - cpl_quad_tree.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 312 383 81.5 %
Date: 2024-04-29 01:40:10 Functions: 23 27 85.2 %

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

Generated by: LCOV version 1.14