/[thuban]/branches/WIP-pyshapelib-bramz/libraries/shapelib/shptree.c
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/libraries/shapelib/shptree.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2734 - (show annotations)
Thu Mar 1 12:42:59 2007 UTC (18 years ago) by bramz
File MIME type: text/plain
File size: 27355 byte(s)
made a copy
1 /******************************************************************************
2 * $Id$
3 *
4 * Project: Shapelib
5 * Purpose: Implementation of quadtree building and searching functions.
6 * Author: Frank Warmerdam, [email protected]
7 *
8 ******************************************************************************
9 * Copyright (c) 1999, Frank Warmerdam
10 *
11 * This software is available under the following "MIT Style" license,
12 * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This
13 * option is discussed in more detail in shapelib.html.
14 *
15 * --
16 *
17 * Permission is hereby granted, free of charge, to any person obtaining a
18 * copy of this software and associated documentation files (the "Software"),
19 * to deal in the Software without restriction, including without limitation
20 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21 * and/or sell copies of the Software, and to permit persons to whom the
22 * Software is furnished to do so, subject to the following conditions:
23 *
24 * The above copyright notice and this permission notice shall be included
25 * in all copies or substantial portions of the Software.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33 * DEALINGS IN THE SOFTWARE.
34 ******************************************************************************
35 *
36 * $Log$
37 * Revision 1.2 2003/10/02 15:15:16 bh
38 * Update to shapelib 1.2.10
39 *
40 * Revision 1.9 2003/01/28 15:53:41 warmerda
41 * Avoid build warnings.
42 *
43 * Revision 1.8 2002/05/07 13:07:45 warmerda
44 * use qsort() - patch from Bernhard Herzog
45 *
46 * Revision 1.7 2002/01/15 14:36:07 warmerda
47 * updated email address
48 *
49 * Revision 1.6 2001/05/23 13:36:52 warmerda
50 * added use of SHPAPI_CALL
51 *
52 * Revision 1.5 1999/11/05 14:12:05 warmerda
53 * updated license terms
54 *
55 * Revision 1.4 1999/06/02 18:24:21 warmerda
56 * added trimming code
57 *
58 * Revision 1.3 1999/06/02 17:56:12 warmerda
59 * added quad'' subnode support for trees
60 *
61 * Revision 1.2 1999/05/18 19:11:11 warmerda
62 * Added example searching capability
63 *
64 * Revision 1.1 1999/05/18 17:49:20 warmerda
65 * New
66 *
67 */
68
69 static char rcsid[] =
70 "$Id$";
71
72 #include "shapefil.h"
73
74 #include <math.h>
75 #include <assert.h>
76 #include <stdlib.h>
77 #include <string.h>
78
79 #ifndef TRUE
80 # define TRUE 1
81 # define FALSE 0
82 #endif
83
84
85 /* -------------------------------------------------------------------- */
86 /* If the following is 0.5, nodes will be split in half. If it */
87 /* is 0.6 then each subnode will contain 60% of the parent */
88 /* node, with 20% representing overlap. This can be help to */
89 /* prevent small objects on a boundary from shifting too high */
90 /* up the tree. */
91 /* -------------------------------------------------------------------- */
92
93 #define SHP_SPLIT_RATIO 0.55
94
95 /************************************************************************/
96 /* SfRealloc() */
97 /* */
98 /* A realloc cover function that will access a NULL pointer as */
99 /* a valid input. */
100 /************************************************************************/
101
102 static void * SfRealloc( void * pMem, int nNewSize )
103
104 {
105 if( pMem == NULL )
106 return( (void *) malloc(nNewSize) );
107 else
108 return( (void *) realloc(pMem,nNewSize) );
109 }
110
111 /************************************************************************/
112 /* SHPTreeNodeInit() */
113 /* */
114 /* Initialize a tree node. */
115 /************************************************************************/
116
117 static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin,
118 double * padfBoundsMax )
119
120 {
121 SHPTreeNode *psTreeNode;
122
123 psTreeNode = (SHPTreeNode *) malloc(sizeof(SHPTreeNode));
124
125 psTreeNode->nShapeCount = 0;
126 psTreeNode->panShapeIds = NULL;
127 psTreeNode->papsShapeObj = NULL;
128
129 psTreeNode->nSubNodes = 0;
130
131 if( padfBoundsMin != NULL )
132 memcpy( psTreeNode->adfBoundsMin, padfBoundsMin, sizeof(double) * 4 );
133
134 if( padfBoundsMax != NULL )
135 memcpy( psTreeNode->adfBoundsMax, padfBoundsMax, sizeof(double) * 4 );
136
137 return psTreeNode;
138 }
139
140
141 /************************************************************************/
142 /* SHPCreateTree() */
143 /************************************************************************/
144
145 SHPTree SHPAPI_CALL1(*)
146 SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth,
147 double *padfBoundsMin, double *padfBoundsMax )
148
149 {
150 SHPTree *psTree;
151
152 if( padfBoundsMin == NULL && hSHP == NULL )
153 return NULL;
154
155 /* -------------------------------------------------------------------- */
156 /* Allocate the tree object */
157 /* -------------------------------------------------------------------- */
158 psTree = (SHPTree *) malloc(sizeof(SHPTree));
159
160 psTree->hSHP = hSHP;
161 psTree->nMaxDepth = nMaxDepth;
162 psTree->nDimension = nDimension;
163
164 /* -------------------------------------------------------------------- */
165 /* If no max depth was defined, try to select a reasonable one */
166 /* that implies approximately 8 shapes per node. */
167 /* -------------------------------------------------------------------- */
168 if( psTree->nMaxDepth == 0 && hSHP != NULL )
169 {
170 int nMaxNodeCount = 1;
171 int nShapeCount;
172
173 SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL );
174 while( nMaxNodeCount*4 < nShapeCount )
175 {
176 psTree->nMaxDepth += 1;
177 nMaxNodeCount = nMaxNodeCount * 2;
178 }
179 }
180
181 /* -------------------------------------------------------------------- */
182 /* Allocate the root node. */
183 /* -------------------------------------------------------------------- */
184 psTree->psRoot = SHPTreeNodeCreate( padfBoundsMin, padfBoundsMax );
185
186 /* -------------------------------------------------------------------- */
187 /* Assign the bounds to the root node. If none are passed in, */
188 /* use the bounds of the provided file otherwise the create */
189 /* function will have already set the bounds. */
190 /* -------------------------------------------------------------------- */
191 if( padfBoundsMin == NULL )
192 {
193 SHPGetInfo( hSHP, NULL, NULL,
194 psTree->psRoot->adfBoundsMin,
195 psTree->psRoot->adfBoundsMax );
196 }
197
198 /* -------------------------------------------------------------------- */
199 /* If we have a file, insert all it's shapes into the tree. */
200 /* -------------------------------------------------------------------- */
201 if( hSHP != NULL )
202 {
203 int iShape, nShapeCount;
204
205 SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL );
206
207 for( iShape = 0; iShape < nShapeCount; iShape++ )
208 {
209 SHPObject *psShape;
210
211 psShape = SHPReadObject( hSHP, iShape );
212 SHPTreeAddShapeId( psTree, psShape );
213 SHPDestroyObject( psShape );
214 }
215 }
216
217 return psTree;
218 }
219
220 /************************************************************************/
221 /* SHPDestroyTreeNode() */
222 /************************************************************************/
223
224 static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode )
225
226 {
227 int i;
228
229 for( i = 0; i < psTreeNode->nSubNodes; i++ )
230 {
231 if( psTreeNode->apsSubNode[i] != NULL )
232 SHPDestroyTreeNode( psTreeNode->apsSubNode[i] );
233 }
234
235 if( psTreeNode->panShapeIds != NULL )
236 free( psTreeNode->panShapeIds );
237
238 if( psTreeNode->papsShapeObj != NULL )
239 {
240 for( i = 0; i < psTreeNode->nShapeCount; i++ )
241 {
242 if( psTreeNode->papsShapeObj[i] != NULL )
243 SHPDestroyObject( psTreeNode->papsShapeObj[i] );
244 }
245
246 free( psTreeNode->papsShapeObj );
247 }
248
249 free( psTreeNode );
250 }
251
252 /************************************************************************/
253 /* SHPDestroyTree() */
254 /************************************************************************/
255
256 void SHPAPI_CALL
257 SHPDestroyTree( SHPTree * psTree )
258
259 {
260 SHPDestroyTreeNode( psTree->psRoot );
261 free( psTree );
262 }
263
264 /************************************************************************/
265 /* SHPCheckBoundsOverlap() */
266 /* */
267 /* Do the given boxes overlap at all? */
268 /************************************************************************/
269
270 int SHPAPI_CALL
271 SHPCheckBoundsOverlap( double * padfBox1Min, double * padfBox1Max,
272 double * padfBox2Min, double * padfBox2Max,
273 int nDimension )
274
275 {
276 int iDim;
277
278 for( iDim = 0; iDim < nDimension; iDim++ )
279 {
280 if( padfBox2Max[iDim] < padfBox1Min[iDim] )
281 return FALSE;
282
283 if( padfBox1Max[iDim] < padfBox2Min[iDim] )
284 return FALSE;
285 }
286
287 return TRUE;
288 }
289
290 /************************************************************************/
291 /* SHPCheckObjectContained() */
292 /* */
293 /* Does the given shape fit within the indicated extents? */
294 /************************************************************************/
295
296 static int SHPCheckObjectContained( SHPObject * psObject, int nDimension,
297 double * padfBoundsMin, double * padfBoundsMax )
298
299 {
300 if( psObject->dfXMin < padfBoundsMin[0]
301 || psObject->dfXMax > padfBoundsMax[0] )
302 return FALSE;
303
304 if( psObject->dfYMin < padfBoundsMin[1]
305 || psObject->dfYMax > padfBoundsMax[1] )
306 return FALSE;
307
308 if( nDimension == 2 )
309 return TRUE;
310
311 if( psObject->dfZMin < padfBoundsMin[2]
312 || psObject->dfZMax < padfBoundsMax[2] )
313 return FALSE;
314
315 if( nDimension == 3 )
316 return TRUE;
317
318 if( psObject->dfMMin < padfBoundsMin[3]
319 || psObject->dfMMax < padfBoundsMax[3] )
320 return FALSE;
321
322 return TRUE;
323 }
324
325 /************************************************************************/
326 /* SHPTreeSplitBounds() */
327 /* */
328 /* Split a region into two subregion evenly, cutting along the */
329 /* longest dimension. */
330 /************************************************************************/
331
332 void SHPAPI_CALL
333 SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn,
334 double *padfBoundsMin1, double * padfBoundsMax1,
335 double *padfBoundsMin2, double * padfBoundsMax2 )
336
337 {
338 /* -------------------------------------------------------------------- */
339 /* The output bounds will be very similar to the input bounds, */
340 /* so just copy over to start. */
341 /* -------------------------------------------------------------------- */
342 memcpy( padfBoundsMin1, padfBoundsMinIn, sizeof(double) * 4 );
343 memcpy( padfBoundsMax1, padfBoundsMaxIn, sizeof(double) * 4 );
344 memcpy( padfBoundsMin2, padfBoundsMinIn, sizeof(double) * 4 );
345 memcpy( padfBoundsMax2, padfBoundsMaxIn, sizeof(double) * 4 );
346
347 /* -------------------------------------------------------------------- */
348 /* Split in X direction. */
349 /* -------------------------------------------------------------------- */
350 if( (padfBoundsMaxIn[0] - padfBoundsMinIn[0])
351 > (padfBoundsMaxIn[1] - padfBoundsMinIn[1]) )
352 {
353 double dfRange = padfBoundsMaxIn[0] - padfBoundsMinIn[0];
354
355 padfBoundsMax1[0] = padfBoundsMinIn[0] + dfRange * SHP_SPLIT_RATIO;
356 padfBoundsMin2[0] = padfBoundsMaxIn[0] - dfRange * SHP_SPLIT_RATIO;
357 }
358
359 /* -------------------------------------------------------------------- */
360 /* Otherwise split in Y direction. */
361 /* -------------------------------------------------------------------- */
362 else
363 {
364 double dfRange = padfBoundsMaxIn[1] - padfBoundsMinIn[1];
365
366 padfBoundsMax1[1] = padfBoundsMinIn[1] + dfRange * SHP_SPLIT_RATIO;
367 padfBoundsMin2[1] = padfBoundsMaxIn[1] - dfRange * SHP_SPLIT_RATIO;
368 }
369 }
370
371 /************************************************************************/
372 /* SHPTreeNodeAddShapeId() */
373 /************************************************************************/
374
375 static int
376 SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject,
377 int nMaxDepth, int nDimension )
378
379 {
380 int i;
381
382 /* -------------------------------------------------------------------- */
383 /* If there are subnodes, then consider wiether this object */
384 /* will fit in them. */
385 /* -------------------------------------------------------------------- */
386 if( nMaxDepth > 1 && psTreeNode->nSubNodes > 0 )
387 {
388 for( i = 0; i < psTreeNode->nSubNodes; i++ )
389 {
390 if( SHPCheckObjectContained(psObject, nDimension,
391 psTreeNode->apsSubNode[i]->adfBoundsMin,
392 psTreeNode->apsSubNode[i]->adfBoundsMax))
393 {
394 return SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[i],
395 psObject, nMaxDepth-1,
396 nDimension );
397 }
398 }
399 }
400
401 /* -------------------------------------------------------------------- */
402 /* Otherwise, consider creating four subnodes if could fit into */
403 /* them, and adding to the appropriate subnode. */
404 /* -------------------------------------------------------------------- */
405 #if MAX_SUBNODE == 4
406 else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 )
407 {
408 double adfBoundsMinH1[4], adfBoundsMaxH1[4];
409 double adfBoundsMinH2[4], adfBoundsMaxH2[4];
410 double adfBoundsMin1[4], adfBoundsMax1[4];
411 double adfBoundsMin2[4], adfBoundsMax2[4];
412 double adfBoundsMin3[4], adfBoundsMax3[4];
413 double adfBoundsMin4[4], adfBoundsMax4[4];
414
415 SHPTreeSplitBounds( psTreeNode->adfBoundsMin,
416 psTreeNode->adfBoundsMax,
417 adfBoundsMinH1, adfBoundsMaxH1,
418 adfBoundsMinH2, adfBoundsMaxH2 );
419
420 SHPTreeSplitBounds( adfBoundsMinH1, adfBoundsMaxH1,
421 adfBoundsMin1, adfBoundsMax1,
422 adfBoundsMin2, adfBoundsMax2 );
423
424 SHPTreeSplitBounds( adfBoundsMinH2, adfBoundsMaxH2,
425 adfBoundsMin3, adfBoundsMax3,
426 adfBoundsMin4, adfBoundsMax4 );
427
428 if( SHPCheckObjectContained(psObject, nDimension,
429 adfBoundsMin1, adfBoundsMax1)
430 || SHPCheckObjectContained(psObject, nDimension,
431 adfBoundsMin2, adfBoundsMax2)
432 || SHPCheckObjectContained(psObject, nDimension,
433 adfBoundsMin3, adfBoundsMax3)
434 || SHPCheckObjectContained(psObject, nDimension,
435 adfBoundsMin4, adfBoundsMax4) )
436 {
437 psTreeNode->nSubNodes = 4;
438 psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1,
439 adfBoundsMax1 );
440 psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2,
441 adfBoundsMax2 );
442 psTreeNode->apsSubNode[2] = SHPTreeNodeCreate( adfBoundsMin3,
443 adfBoundsMax3 );
444 psTreeNode->apsSubNode[3] = SHPTreeNodeCreate( adfBoundsMin4,
445 adfBoundsMax4 );
446
447 /* recurse back on this node now that it has subnodes */
448 return( SHPTreeNodeAddShapeId( psTreeNode, psObject,
449 nMaxDepth, nDimension ) );
450 }
451 }
452 #endif /* MAX_SUBNODE == 4 */
453
454 /* -------------------------------------------------------------------- */
455 /* Otherwise, consider creating two subnodes if could fit into */
456 /* them, and adding to the appropriate subnode. */
457 /* -------------------------------------------------------------------- */
458 #if MAX_SUBNODE == 2
459 else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 )
460 {
461 double adfBoundsMin1[4], adfBoundsMax1[4];
462 double adfBoundsMin2[4], adfBoundsMax2[4];
463
464 SHPTreeSplitBounds( psTreeNode->adfBoundsMin, psTreeNode->adfBoundsMax,
465 adfBoundsMin1, adfBoundsMax1,
466 adfBoundsMin2, adfBoundsMax2 );
467
468 if( SHPCheckObjectContained(psObject, nDimension,
469 adfBoundsMin1, adfBoundsMax1))
470 {
471 psTreeNode->nSubNodes = 2;
472 psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1,
473 adfBoundsMax1 );
474 psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2,
475 adfBoundsMax2 );
476
477 return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[0], psObject,
478 nMaxDepth - 1, nDimension ) );
479 }
480 else if( SHPCheckObjectContained(psObject, nDimension,
481 adfBoundsMin2, adfBoundsMax2) )
482 {
483 psTreeNode->nSubNodes = 2;
484 psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1,
485 adfBoundsMax1 );
486 psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2,
487 adfBoundsMax2 );
488
489 return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[1], psObject,
490 nMaxDepth - 1, nDimension ) );
491 }
492 }
493 #endif /* MAX_SUBNODE == 2 */
494
495 /* -------------------------------------------------------------------- */
496 /* If none of that worked, just add it to this nodes list. */
497 /* -------------------------------------------------------------------- */
498 psTreeNode->nShapeCount++;
499
500 psTreeNode->panShapeIds =
501 SfRealloc( psTreeNode->panShapeIds,
502 sizeof(int) * psTreeNode->nShapeCount );
503 psTreeNode->panShapeIds[psTreeNode->nShapeCount-1] = psObject->nShapeId;
504
505 if( psTreeNode->papsShapeObj != NULL )
506 {
507 psTreeNode->papsShapeObj =
508 SfRealloc( psTreeNode->papsShapeObj,
509 sizeof(void *) * psTreeNode->nShapeCount );
510 psTreeNode->papsShapeObj[psTreeNode->nShapeCount-1] = NULL;
511 }
512
513 return TRUE;
514 }
515
516 /************************************************************************/
517 /* SHPTreeAddShapeId() */
518 /* */
519 /* Add a shape to the tree, but don't keep a pointer to the */
520 /* object data, just keep the shapeid. */
521 /************************************************************************/
522
523 int SHPAPI_CALL
524 SHPTreeAddShapeId( SHPTree * psTree, SHPObject * psObject )
525
526 {
527 return( SHPTreeNodeAddShapeId( psTree->psRoot, psObject,
528 psTree->nMaxDepth, psTree->nDimension ) );
529 }
530
531 /************************************************************************/
532 /* SHPTreeCollectShapesIds() */
533 /* */
534 /* Work function implementing SHPTreeFindLikelyShapes() on a */
535 /* tree node by tree node basis. */
536 /************************************************************************/
537
538 void SHPAPI_CALL
539 SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode,
540 double * padfBoundsMin, double * padfBoundsMax,
541 int * pnShapeCount, int * pnMaxShapes,
542 int ** ppanShapeList )
543
544 {
545 int i;
546
547 /* -------------------------------------------------------------------- */
548 /* Does this node overlap the area of interest at all? If not, */
549 /* return without adding to the list at all. */
550 /* -------------------------------------------------------------------- */
551 if( !SHPCheckBoundsOverlap( psTreeNode->adfBoundsMin,
552 psTreeNode->adfBoundsMax,
553 padfBoundsMin,
554 padfBoundsMax,
555 hTree->nDimension ) )
556 return;
557
558 /* -------------------------------------------------------------------- */
559 /* Grow the list to hold the shapes on this node. */
560 /* -------------------------------------------------------------------- */
561 if( *pnShapeCount + psTreeNode->nShapeCount > *pnMaxShapes )
562 {
563 *pnMaxShapes = (*pnShapeCount + psTreeNode->nShapeCount) * 2 + 20;
564 *ppanShapeList = (int *)
565 SfRealloc(*ppanShapeList,sizeof(int) * *pnMaxShapes);
566 }
567
568 /* -------------------------------------------------------------------- */
569 /* Add the local nodes shapeids to the list. */
570 /* -------------------------------------------------------------------- */
571 for( i = 0; i < psTreeNode->nShapeCount; i++ )
572 {
573 (*ppanShapeList)[(*pnShapeCount)++] = psTreeNode->panShapeIds[i];
574 }
575
576 /* -------------------------------------------------------------------- */
577 /* Recurse to subnodes if they exist. */
578 /* -------------------------------------------------------------------- */
579 for( i = 0; i < psTreeNode->nSubNodes; i++ )
580 {
581 if( psTreeNode->apsSubNode[i] != NULL )
582 SHPTreeCollectShapeIds( hTree, psTreeNode->apsSubNode[i],
583 padfBoundsMin, padfBoundsMax,
584 pnShapeCount, pnMaxShapes,
585 ppanShapeList );
586 }
587 }
588
589 /************************************************************************/
590 /* SHPTreeFindLikelyShapes() */
591 /* */
592 /* Find all shapes within tree nodes for which the tree node */
593 /* bounding box overlaps the search box. The return value is */
594 /* an array of shapeids terminated by a -1. The shapeids will */
595 /* be in order, as hopefully this will result in faster (more */
596 /* sequential) reading from the file. */
597 /************************************************************************/
598
599 /* helper for qsort */
600 static int
601 compare_ints( const void * a, const void * b)
602 {
603 return (*(int*)a) - (*(int*)b);
604 }
605
606 int SHPAPI_CALL1(*)
607 SHPTreeFindLikelyShapes( SHPTree * hTree,
608 double * padfBoundsMin, double * padfBoundsMax,
609 int * pnShapeCount )
610
611 {
612 int *panShapeList=NULL, nMaxShapes = 0;
613
614 /* -------------------------------------------------------------------- */
615 /* Perform the search by recursive descent. */
616 /* -------------------------------------------------------------------- */
617 *pnShapeCount = 0;
618
619 SHPTreeCollectShapeIds( hTree, hTree->psRoot,
620 padfBoundsMin, padfBoundsMax,
621 pnShapeCount, &nMaxShapes,
622 &panShapeList );
623
624 /* -------------------------------------------------------------------- */
625 /* Sort the id array */
626 /* -------------------------------------------------------------------- */
627
628 qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints);
629
630 return panShapeList;
631 }
632
633 /************************************************************************/
634 /* SHPTreeNodeTrim() */
635 /* */
636 /* This is the recurve version of SHPTreeTrimExtraNodes() that */
637 /* walks the tree cleaning it up. */
638 /************************************************************************/
639
640 static int SHPTreeNodeTrim( SHPTreeNode * psTreeNode )
641
642 {
643 int i;
644
645 /* -------------------------------------------------------------------- */
646 /* Trim subtrees, and free subnodes that come back empty. */
647 /* -------------------------------------------------------------------- */
648 for( i = 0; i < psTreeNode->nSubNodes; i++ )
649 {
650 if( SHPTreeNodeTrim( psTreeNode->apsSubNode[i] ) )
651 {
652 SHPDestroyTreeNode( psTreeNode->apsSubNode[i] );
653
654 psTreeNode->apsSubNode[i] =
655 psTreeNode->apsSubNode[psTreeNode->nSubNodes-1];
656
657 psTreeNode->nSubNodes--;
658
659 i--; /* process the new occupant of this subnode entry */
660 }
661 }
662
663 /* -------------------------------------------------------------------- */
664 /* We should be trimmed if we have no subnodes, and no shapes. */
665 /* -------------------------------------------------------------------- */
666 return( psTreeNode->nSubNodes == 0 && psTreeNode->nShapeCount == 0 );
667 }
668
669 /************************************************************************/
670 /* SHPTreeTrimExtraNodes() */
671 /* */
672 /* Trim empty nodes from the tree. Note that we never trim an */
673 /* empty root node. */
674 /************************************************************************/
675
676 void SHPAPI_CALL
677 SHPTreeTrimExtraNodes( SHPTree * hTree )
678
679 {
680 SHPTreeNodeTrim( hTree->psRoot );
681 }
682

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26