mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-01 14:22:41 -06:00
852 lines
32 KiB
C++
852 lines
32 KiB
C++
/******************************************************************************
|
|
* $Id: cpl_quad_tree.cpp 27044 2014-03-16 23:41:27Z rouault $
|
|
*
|
|
* Project: CPL - Common Portability Library
|
|
* Purpose: Implementation of quadtree building and searching functions.
|
|
* Derived from shapelib and mapserver implementations
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
* Even Rouault, <even dot rouault at mines dash paris dot org>
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 1999-2008, Frank Warmerdam
|
|
* Copyright (c) 2008-2014, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
******************************************************************************
|
|
*
|
|
*/
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_quad_tree.h"
|
|
|
|
CPL_CVSID("$Id: cpl_quad_tree.cpp 27044 2014-03-16 23:41:27Z rouault $");
|
|
|
|
#define MAX_DEFAULT_TREE_DEPTH 12
|
|
#define MAX_SUBNODES 4
|
|
|
|
typedef struct _QuadTreeNode QuadTreeNode;
|
|
|
|
struct _QuadTreeNode
|
|
{
|
|
/* area covered by this psNode */
|
|
CPLRectObj rect;
|
|
|
|
int nFeatures; /* number of shapes stored at this psNode. */
|
|
|
|
int nNumSubNodes; /* number of active subnodes */
|
|
|
|
void **pahFeatures; /* list of shapes stored at this psNode. */
|
|
CPLRectObj *pasBounds;
|
|
|
|
QuadTreeNode *apSubNode[MAX_SUBNODES];
|
|
};
|
|
|
|
struct _CPLQuadTree
|
|
{
|
|
QuadTreeNode *psRoot;
|
|
CPLQuadTreeGetBoundsFunc pfnGetBounds;
|
|
int nFeatures;
|
|
int nMaxDepth;
|
|
int nBucketCapacity;
|
|
double dfSplitRatio;
|
|
};
|
|
|
|
|
|
static void CPLQuadTreeAddFeatureInternal(CPLQuadTree *hQuadTree,
|
|
void* hFeature,
|
|
const CPLRectObj *pRect);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If the following is 0.5, psNodes will be split in half. If it */
|
|
/* is 0.6 then each apSubNode will contain 60% of the parent */
|
|
/* psNode, with 20% representing overlap. This can be help to */
|
|
/* prevent small objects on a boundary from shifting too high */
|
|
/* up the hQuadTree. */
|
|
/* -------------------------------------------------------------------- */
|
|
#define DEFAULT_SPLIT_RATIO 0.55
|
|
|
|
/*
|
|
** Returns TRUE if rectangle a is contained in rectangle b
|
|
*/
|
|
static CPL_INLINE int CPL_RectContained(const CPLRectObj *a, const CPLRectObj *b)
|
|
{
|
|
if(a->minx >= b->minx && a->maxx <= b->maxx)
|
|
if(a->miny >= b->miny && a->maxy <= b->maxy)
|
|
return(TRUE);
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
** Returns TRUE if rectangles a and b overlap
|
|
*/
|
|
static CPL_INLINE int CPL_RectOverlap(const CPLRectObj *a, const CPLRectObj *b)
|
|
{
|
|
if(a->minx > b->maxx) return(FALSE);
|
|
if(a->maxx < b->minx) return(FALSE);
|
|
if(a->miny > b->maxy) return(FALSE);
|
|
if(a->maxy < b->miny) return(FALSE);
|
|
return(TRUE);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeNodeCreate() */
|
|
/************************************************************************/
|
|
|
|
static QuadTreeNode *CPLQuadTreeNodeCreate(const CPLRectObj* pRect)
|
|
{
|
|
QuadTreeNode *psNode;
|
|
|
|
psNode = (QuadTreeNode *) CPLMalloc(sizeof(QuadTreeNode));
|
|
|
|
psNode->nFeatures = 0;
|
|
psNode->pahFeatures = NULL;
|
|
psNode->pasBounds = NULL;
|
|
|
|
psNode->nNumSubNodes = 0;
|
|
|
|
memcpy(&(psNode->rect), pRect, sizeof(CPLRectObj));
|
|
|
|
return psNode;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeCreate() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Create a new quadtree
|
|
*
|
|
* @param pGlobalBounds a pointer to the global extent of all
|
|
* the elements that will be inserted
|
|
* @param pfnGetBounds a user provided function to get the bounding box of
|
|
* the inserted elements. If it is set to NULL, then
|
|
* CPLQuadTreeInsertWithBounds() must be used, and
|
|
* extra memory will be used to keep features bounds in the
|
|
* quad tree.
|
|
*
|
|
* @return a newly allocated quadtree
|
|
*/
|
|
|
|
CPLQuadTree *CPLQuadTreeCreate(const CPLRectObj* pGlobalBounds, CPLQuadTreeGetBoundsFunc pfnGetBounds)
|
|
{
|
|
CPLQuadTree *hQuadTree;
|
|
|
|
CPLAssert(pGlobalBounds);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate the hQuadTree object */
|
|
/* -------------------------------------------------------------------- */
|
|
hQuadTree = (CPLQuadTree *) CPLMalloc(sizeof(CPLQuadTree));
|
|
|
|
hQuadTree->nFeatures = 0;
|
|
hQuadTree->pfnGetBounds = pfnGetBounds;
|
|
hQuadTree->nMaxDepth = 0;
|
|
hQuadTree->nBucketCapacity = 8;
|
|
|
|
hQuadTree->dfSplitRatio = DEFAULT_SPLIT_RATIO;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate the psRoot psNode. */
|
|
/* -------------------------------------------------------------------- */
|
|
hQuadTree->psRoot = CPLQuadTreeNodeCreate(pGlobalBounds);
|
|
|
|
return hQuadTree;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeGetAdvisedMaxDepth() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Returns the optimal depth of a quadtree to hold nExpectedFeatures
|
|
*
|
|
* @param nExpectedFeatures the expected maximum number of elements to be inserted
|
|
*
|
|
* @return the optimal depth of a quadtree to hold nExpectedFeatures
|
|
*/
|
|
|
|
int CPLQuadTreeGetAdvisedMaxDepth(int nExpectedFeatures)
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to select a reasonable one */
|
|
/* that implies approximately 8 shapes per node. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nMaxDepth = 0;
|
|
int nMaxNodeCount = 1;
|
|
|
|
while( nMaxNodeCount*4 < nExpectedFeatures )
|
|
{
|
|
nMaxDepth += 1;
|
|
nMaxNodeCount = nMaxNodeCount * 2;
|
|
}
|
|
|
|
CPLDebug( "CPLQuadTree",
|
|
"Estimated spatial index tree depth: %d",
|
|
nMaxDepth );
|
|
|
|
/* NOTE: Due to problems with memory allocation for deep trees,
|
|
* automatically estimated depth is limited up to 12 levels.
|
|
* See Ticket #1594 for detailed discussion.
|
|
*/
|
|
if( nMaxDepth > MAX_DEFAULT_TREE_DEPTH )
|
|
{
|
|
nMaxDepth = MAX_DEFAULT_TREE_DEPTH;
|
|
|
|
CPLDebug( "CPLQuadTree",
|
|
"Falling back to max number of allowed index tree levels (%d).",
|
|
MAX_DEFAULT_TREE_DEPTH );
|
|
|
|
}
|
|
|
|
return nMaxDepth;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeSetMaxDepth() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Set the maximum depth of a quadtree. By default, quad trees have
|
|
* no maximum depth, but a maximum bucket capacity.
|
|
*
|
|
* @param hQuadTree the quad tree
|
|
* @param nMaxDepth the maximum depth allowed
|
|
*/
|
|
|
|
void CPLQuadTreeSetMaxDepth(CPLQuadTree *hQuadTree, int nMaxDepth)
|
|
{
|
|
hQuadTree->nMaxDepth = nMaxDepth;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeSetBucketCapacity() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Set the maximum capacity of a node of a quadtree. The default value is 8.
|
|
* Note that the maximum capacity will only be honoured if the features
|
|
* inserted have a point geometry. Otherwise it may be exceeded.
|
|
*
|
|
* @param hQuadTree the quad tree
|
|
* @param nBucketCapacity the maximum capactiy of a node of a quadtree
|
|
*/
|
|
|
|
void CPLQuadTreeSetBucketCapacity(CPLQuadTree *hQuadTree, int nBucketCapacity)
|
|
{
|
|
if( nBucketCapacity > 0 )
|
|
hQuadTree->nBucketCapacity = nBucketCapacity;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeInsert() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Insert a feature into a quadtree
|
|
*
|
|
* @param hQuadTree the quad tree
|
|
* @param hFeature the feature to insert
|
|
*/
|
|
|
|
void CPLQuadTreeInsert(CPLQuadTree * hQuadTree, void* hFeature)
|
|
{
|
|
CPLRectObj bounds;
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"hQuadTree->pfnGetBounds == NULL");
|
|
return;
|
|
}
|
|
hQuadTree->nFeatures ++;
|
|
hQuadTree->pfnGetBounds(hFeature, &bounds);
|
|
CPLQuadTreeAddFeatureInternal(hQuadTree, hFeature, &bounds);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeInsertWithBounds() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Insert a feature into a quadtree
|
|
*
|
|
* @param hQuadTree the quad tree
|
|
* @param hFeature the feature to insert
|
|
* @param psBounds bounds of the feature
|
|
*/
|
|
void CPLQuadTreeInsertWithBounds(CPLQuadTree *hQuadTree,
|
|
void* hFeature,
|
|
const CPLRectObj* psBounds)
|
|
{
|
|
hQuadTree->nFeatures ++;
|
|
CPLQuadTreeAddFeatureInternal(hQuadTree, hFeature, psBounds);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeNodeDestroy() */
|
|
/************************************************************************/
|
|
|
|
static void CPLQuadTreeNodeDestroy(QuadTreeNode *psNode)
|
|
{
|
|
int i;
|
|
|
|
for(i=0; i<psNode->nNumSubNodes; i++ )
|
|
{
|
|
if(psNode->apSubNode[i])
|
|
CPLQuadTreeNodeDestroy(psNode->apSubNode[i]);
|
|
}
|
|
|
|
if(psNode->pahFeatures)
|
|
{
|
|
CPLFree(psNode->pahFeatures);
|
|
CPLFree(psNode->pasBounds);
|
|
}
|
|
|
|
CPLFree(psNode);
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeDestroy() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Destroy a quadtree
|
|
*
|
|
* @param hQuadTree the quad tree to destroy
|
|
*/
|
|
|
|
void CPLQuadTreeDestroy(CPLQuadTree *hQuadTree)
|
|
{
|
|
CPLAssert(hQuadTree);
|
|
CPLQuadTreeNodeDestroy(hQuadTree->psRoot);
|
|
CPLFree(hQuadTree);
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeSplitBounds() */
|
|
/************************************************************************/
|
|
|
|
static void CPLQuadTreeSplitBounds( double dfSplitRatio,
|
|
const CPLRectObj *in,
|
|
CPLRectObj *out1,
|
|
CPLRectObj *out2)
|
|
{
|
|
double range;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* The output bounds will be very similar to the input bounds, */
|
|
/* so just copy over to start. */
|
|
/* -------------------------------------------------------------------- */
|
|
memcpy(out1, in, sizeof(CPLRectObj));
|
|
memcpy(out2, in, sizeof(CPLRectObj));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Split in X direction. */
|
|
/* -------------------------------------------------------------------- */
|
|
if((in->maxx - in->minx) > (in->maxy - in->miny)) {
|
|
range = in->maxx - in->minx;
|
|
|
|
out1->maxx = in->minx + range * dfSplitRatio;
|
|
out2->minx = in->maxx - range * dfSplitRatio;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Otherwise split in Y direction. */
|
|
/* -------------------------------------------------------------------- */
|
|
else {
|
|
range = in->maxy - in->miny;
|
|
|
|
out1->maxy = in->miny + range * dfSplitRatio;
|
|
out2->miny = in->maxy - range * dfSplitRatio;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeNodeAddFeatureAlg1() */
|
|
/************************************************************************/
|
|
|
|
static void CPLQuadTreeNodeAddFeatureAlg1( CPLQuadTree* hQuadTree,
|
|
QuadTreeNode *psNode,
|
|
void* hFeature,
|
|
const CPLRectObj* pRect)
|
|
{
|
|
int i;
|
|
if (psNode->nNumSubNodes == 0)
|
|
{
|
|
/* If we have reached the max bucket capacity, try to insert */
|
|
/* in a subnode if possible */
|
|
if (psNode->nFeatures >= hQuadTree->nBucketCapacity)
|
|
{
|
|
CPLRectObj half1, half2, quad1, quad2, quad3, quad4;
|
|
|
|
CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect, &half1, &half2);
|
|
CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1, &quad2);
|
|
CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3, &quad4);
|
|
|
|
if (memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 &&
|
|
memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 &&
|
|
memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 &&
|
|
memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 &&
|
|
(CPL_RectContained(pRect, &quad1) ||
|
|
CPL_RectContained(pRect, &quad2) ||
|
|
CPL_RectContained(pRect, &quad3) ||
|
|
CPL_RectContained(pRect, &quad4)))
|
|
{
|
|
psNode->nNumSubNodes = 4;
|
|
psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1);
|
|
psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2);
|
|
psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3);
|
|
psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4);
|
|
|
|
int oldNumFeatures = psNode->nFeatures;
|
|
void** oldFeatures = psNode->pahFeatures;
|
|
CPLRectObj* pasOldBounds = psNode->pasBounds;
|
|
psNode->nFeatures = 0;
|
|
psNode->pahFeatures = NULL;
|
|
psNode->pasBounds = NULL;
|
|
|
|
/* redispatch existing pahFeatures in apSubNodes */
|
|
int i;
|
|
for(i=0;i<oldNumFeatures;i++)
|
|
{
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, oldFeatures[i], &pasOldBounds[i]);
|
|
else
|
|
{
|
|
CPLRectObj bounds;
|
|
hQuadTree->pfnGetBounds(oldFeatures[i], &bounds);
|
|
CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, oldFeatures[i], &bounds);
|
|
}
|
|
}
|
|
|
|
CPLFree(oldFeatures);
|
|
CPLFree(pasOldBounds);
|
|
|
|
/* recurse back on this psNode now that it has apSubNodes */
|
|
CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, hFeature, pRect);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* If there are apSubNodes, then consider whether this object */
|
|
/* will fit in them. */
|
|
/* -------------------------------------------------------------------- */
|
|
for(i=0; i<psNode->nNumSubNodes; i++ )
|
|
{
|
|
if( CPL_RectContained(pRect, &psNode->apSubNode[i]->rect))
|
|
{
|
|
CPLQuadTreeNodeAddFeatureAlg1( hQuadTree, psNode->apSubNode[i], hFeature, pRect);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If none of that worked, just add it to this psNodes list. */
|
|
/* -------------------------------------------------------------------- */
|
|
psNode->nFeatures++;
|
|
|
|
if( psNode->nFeatures == 1 )
|
|
{
|
|
CPLAssert( psNode->pahFeatures == NULL );
|
|
psNode->pahFeatures = (void**) CPLMalloc( hQuadTree->nBucketCapacity * sizeof(void*) );
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
psNode->pasBounds = (CPLRectObj*) CPLMalloc( hQuadTree->nBucketCapacity * sizeof(CPLRectObj) );
|
|
}
|
|
else if( psNode->nFeatures > hQuadTree->nBucketCapacity )
|
|
{
|
|
psNode->pahFeatures = (void**) CPLRealloc( psNode->pahFeatures, sizeof(void*) * psNode->nFeatures );
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
psNode->pasBounds = (CPLRectObj*) CPLRealloc( psNode->pasBounds, sizeof(CPLRectObj) * psNode->nFeatures );
|
|
}
|
|
psNode->pahFeatures[psNode->nFeatures-1] = hFeature;
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
psNode->pasBounds[psNode->nFeatures-1] = *pRect;
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeNodeAddFeatureAlg2() */
|
|
/************************************************************************/
|
|
|
|
static void CPLQuadTreeNodeAddFeatureAlg2( CPLQuadTree *hQuadTree,
|
|
QuadTreeNode *psNode,
|
|
void* hFeature,
|
|
const CPLRectObj* pRect,
|
|
int nMaxDepth)
|
|
{
|
|
int i;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If there are apSubNodes, then consider whether this object */
|
|
/* will fit in them. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nMaxDepth > 1 && psNode->nNumSubNodes > 0 )
|
|
{
|
|
for(i=0; i<psNode->nNumSubNodes; i++ )
|
|
{
|
|
if( CPL_RectContained(pRect, &psNode->apSubNode[i]->rect))
|
|
{
|
|
CPLQuadTreeNodeAddFeatureAlg2( hQuadTree, psNode->apSubNode[i],
|
|
hFeature, pRect, nMaxDepth-1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Otherwise, consider creating four apSubNodes if could fit into */
|
|
/* them, and adding to the appropriate apSubNode. */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( nMaxDepth > 1 && psNode->nNumSubNodes == 0 )
|
|
{
|
|
CPLRectObj half1, half2, quad1, quad2, quad3, quad4;
|
|
|
|
CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect, &half1, &half2);
|
|
CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1, &quad2);
|
|
CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3, &quad4);
|
|
|
|
if( memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 &&
|
|
memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 &&
|
|
memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 &&
|
|
memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 &&
|
|
(CPL_RectContained(pRect, &quad1) ||
|
|
CPL_RectContained(pRect, &quad2) ||
|
|
CPL_RectContained(pRect, &quad3) ||
|
|
CPL_RectContained(pRect, &quad4)) )
|
|
{
|
|
psNode->nNumSubNodes = 4;
|
|
psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1);
|
|
psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2);
|
|
psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3);
|
|
psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4);
|
|
|
|
/* recurse back on this psNode now that it has apSubNodes */
|
|
CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, psNode, hFeature, pRect, nMaxDepth);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If none of that worked, just add it to this psNodes list. */
|
|
/* -------------------------------------------------------------------- */
|
|
psNode->nFeatures++;
|
|
|
|
psNode->pahFeatures =
|
|
(void**) CPLRealloc( psNode->pahFeatures,
|
|
sizeof(void*) * psNode->nFeatures );
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
{
|
|
psNode->pasBounds =
|
|
(CPLRectObj*) CPLRealloc( psNode->pasBounds,
|
|
sizeof(CPLRectObj) * psNode->nFeatures );
|
|
}
|
|
psNode->pahFeatures[psNode->nFeatures-1] = hFeature;
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
{
|
|
psNode->pasBounds[psNode->nFeatures-1] = *pRect;
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeAddFeatureInternal() */
|
|
/************************************************************************/
|
|
|
|
static void CPLQuadTreeAddFeatureInternal(CPLQuadTree *hQuadTree,
|
|
void* hFeature,
|
|
const CPLRectObj *pRect)
|
|
{
|
|
if (hQuadTree->nMaxDepth == 0)
|
|
{
|
|
CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, hQuadTree->psRoot,
|
|
hFeature, pRect);
|
|
}
|
|
else
|
|
{
|
|
CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, hQuadTree->psRoot,
|
|
hFeature, pRect, hQuadTree->nMaxDepth);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeCollectFeatures() */
|
|
/************************************************************************/
|
|
|
|
static void CPLQuadTreeCollectFeatures(const CPLQuadTree *hQuadTree,
|
|
const QuadTreeNode *psNode,
|
|
const CPLRectObj* pAoi,
|
|
int* pnFeatureCount,
|
|
int* pnMaxFeatures,
|
|
void*** pppFeatureList)
|
|
{
|
|
int i;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Does this psNode overlap the area of interest at all? If not, */
|
|
/* return without adding to the list at all. */
|
|
/* -------------------------------------------------------------------- */
|
|
if(!CPL_RectOverlap(&psNode->rect, pAoi))
|
|
return;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Grow the list to hold the features on this psNode. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( *pnFeatureCount + psNode->nFeatures > *pnMaxFeatures )
|
|
{
|
|
*pnMaxFeatures = (*pnFeatureCount + psNode->nFeatures) * 2 + 20;
|
|
*pppFeatureList = (void**)
|
|
CPLRealloc(*pppFeatureList,sizeof(void*) * *pnMaxFeatures);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Add the local features to the list. */
|
|
/* -------------------------------------------------------------------- */
|
|
for(i=0; i<psNode->nFeatures; i++)
|
|
{
|
|
if( hQuadTree->pfnGetBounds == NULL )
|
|
{
|
|
if (CPL_RectOverlap(&psNode->pasBounds[i], pAoi))
|
|
(*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i];
|
|
}
|
|
else
|
|
{
|
|
CPLRectObj bounds;
|
|
hQuadTree->pfnGetBounds(psNode->pahFeatures[i], &bounds);
|
|
if (CPL_RectOverlap(&bounds, pAoi))
|
|
(*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i];
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Recurse to subnodes if they exist. */
|
|
/* -------------------------------------------------------------------- */
|
|
for(i=0; i<psNode->nNumSubNodes; i++)
|
|
{
|
|
if(psNode->apSubNode[i])
|
|
CPLQuadTreeCollectFeatures(hQuadTree, psNode->apSubNode[i], pAoi,
|
|
pnFeatureCount, pnMaxFeatures, pppFeatureList);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeSearch() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Returns all the elements inserted whose bounding box intersects the
|
|
* provided area of interest
|
|
*
|
|
* @param hQuadTree the quad tree
|
|
* @param pAoi the pointer to the area of interest
|
|
* @param pnFeatureCount the user data provided to the function.
|
|
*
|
|
* @return an array of features that must be freed with CPLFree
|
|
*/
|
|
|
|
void** CPLQuadTreeSearch(const CPLQuadTree *hQuadTree,
|
|
const CPLRectObj* pAoi,
|
|
int* pnFeatureCount)
|
|
{
|
|
void** ppFeatureList = NULL;
|
|
int nMaxFeatures = 0;
|
|
int nFeatureCount = 0;
|
|
|
|
CPLAssert(hQuadTree);
|
|
CPLAssert(pAoi);
|
|
|
|
if (pnFeatureCount == NULL)
|
|
pnFeatureCount = &nFeatureCount;
|
|
|
|
*pnFeatureCount = 0;
|
|
CPLQuadTreeCollectFeatures(hQuadTree, hQuadTree->psRoot, pAoi,
|
|
pnFeatureCount, &nMaxFeatures, &ppFeatureList);
|
|
|
|
return(ppFeatureList);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeNodeForeach() */
|
|
/************************************************************************/
|
|
|
|
static int CPLQuadTreeNodeForeach(const QuadTreeNode *psNode,
|
|
CPLQuadTreeForeachFunc pfnForeach,
|
|
void* pUserData)
|
|
{
|
|
int i;
|
|
for(i=0; i<psNode->nNumSubNodes; i++ )
|
|
{
|
|
if (CPLQuadTreeNodeForeach(psNode->apSubNode[i], pfnForeach, pUserData) == FALSE)
|
|
return FALSE;
|
|
}
|
|
|
|
for(i=0; i<psNode->nFeatures; i++)
|
|
{
|
|
if (pfnForeach(psNode->pahFeatures[i], pUserData) == FALSE)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeForeach() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Walk through the quadtree and runs the provided function on all the
|
|
* elements
|
|
*
|
|
* This function is provided with the user_data argument of pfnForeach.
|
|
* It must return TRUE to go on the walk through the hash set, or FALSE to
|
|
* make it stop.
|
|
*
|
|
* Note : the structure of the quadtree must *NOT* be modified during the
|
|
* walk.
|
|
*
|
|
* @param hQuadTree the quad tree
|
|
* @param pfnForeach the function called on each element.
|
|
* @param pUserData the user data provided to the function.
|
|
*/
|
|
|
|
void CPLQuadTreeForeach(const CPLQuadTree *hQuadTree,
|
|
CPLQuadTreeForeachFunc pfnForeach,
|
|
void* pUserData)
|
|
{
|
|
CPLAssert(hQuadTree);
|
|
CPLAssert(pfnForeach);
|
|
CPLQuadTreeNodeForeach(hQuadTree->psRoot, pfnForeach, pUserData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeDumpNode() */
|
|
/************************************************************************/
|
|
|
|
static void CPLQuadTreeDumpNode(const QuadTreeNode *psNode,
|
|
int nIndentLevel,
|
|
CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc,
|
|
void* pUserData)
|
|
{
|
|
int i;
|
|
int count;
|
|
if (psNode->nNumSubNodes)
|
|
{
|
|
for(count=nIndentLevel;--count>=0;)
|
|
printf(" ");
|
|
printf("SubhQuadTrees :\n");
|
|
for(i=0; i<psNode->nNumSubNodes; i++ )
|
|
{
|
|
for(count=nIndentLevel+1;--count>=0;)
|
|
printf(" ");
|
|
printf("SubhQuadTree %d :\n", i+1);
|
|
CPLQuadTreeDumpNode(psNode->apSubNode[i], nIndentLevel + 2,
|
|
pfnDumpFeatureFunc, pUserData);
|
|
}
|
|
}
|
|
if (psNode->nFeatures)
|
|
{
|
|
for(count=nIndentLevel;--count>=0;)
|
|
printf(" ");
|
|
printf("Leaves (%d):\n", psNode->nFeatures);
|
|
for(i=0; i<psNode->nFeatures; i++)
|
|
{
|
|
if (pfnDumpFeatureFunc)
|
|
pfnDumpFeatureFunc(psNode->pahFeatures[i], nIndentLevel + 2,
|
|
pUserData);
|
|
else
|
|
{
|
|
for(count=nIndentLevel + 1;--count>=0;)
|
|
printf(" ");
|
|
printf("%p\n", psNode->pahFeatures[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeDump() */
|
|
/************************************************************************/
|
|
|
|
void CPLQuadTreeDump(const CPLQuadTree *hQuadTree,
|
|
CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc,
|
|
void* pUserData)
|
|
{
|
|
CPLQuadTreeDumpNode(hQuadTree->psRoot, 0, pfnDumpFeatureFunc, pUserData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeGetStatsNode() */
|
|
/************************************************************************/
|
|
|
|
static
|
|
void CPLQuadTreeGetStatsNode(const QuadTreeNode *psNode,
|
|
int nDepthLevel,
|
|
int* pnNodeCount,
|
|
int* pnMaxDepth,
|
|
int* pnMaxBucketCapacity)
|
|
{
|
|
int i;
|
|
(*pnNodeCount) ++;
|
|
if (nDepthLevel > *pnMaxDepth)
|
|
*pnMaxDepth = nDepthLevel;
|
|
if (psNode->nFeatures > *pnMaxBucketCapacity)
|
|
*pnMaxBucketCapacity = psNode->nFeatures;
|
|
for(i=0; i<psNode->nNumSubNodes; i++ )
|
|
{
|
|
CPLQuadTreeGetStatsNode(psNode->apSubNode[i], nDepthLevel + 1,
|
|
pnNodeCount, pnMaxDepth, pnMaxBucketCapacity);
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLQuadTreeGetStats() */
|
|
/************************************************************************/
|
|
|
|
void CPLQuadTreeGetStats(const CPLQuadTree *hQuadTree,
|
|
int* pnFeatureCount,
|
|
int* pnNodeCount,
|
|
int* pnMaxDepth,
|
|
int* pnMaxBucketCapacity)
|
|
{
|
|
int nFeatureCount, nNodeCount, nMaxDepth, nMaxBucketCapacity;
|
|
CPLAssert(hQuadTree);
|
|
if (pnFeatureCount == NULL)
|
|
pnFeatureCount = &nFeatureCount;
|
|
if (pnNodeCount == NULL)
|
|
pnNodeCount = &nNodeCount;
|
|
if (pnMaxDepth == NULL)
|
|
pnMaxDepth = &nMaxDepth;
|
|
if (pnMaxBucketCapacity == NULL)
|
|
pnMaxBucketCapacity = &nMaxBucketCapacity;
|
|
|
|
*pnFeatureCount = hQuadTree->nFeatures;
|
|
*pnNodeCount = 0;
|
|
*pnMaxDepth = 1;
|
|
*pnMaxBucketCapacity = 0;
|
|
|
|
CPLQuadTreeGetStatsNode(hQuadTree->psRoot, 0, pnNodeCount, pnMaxDepth, pnMaxBucketCapacity);
|
|
}
|