LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/shape - shptree.c (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 298 228 76.5 %
Date: 2010-01-09 Functions: 22 18 81.8 %

       1                 : /******************************************************************************
       2                 :  * $Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $
       3                 :  *
       4                 :  * Project:  Shapelib
       5                 :  * Purpose:  Implementation of quadtree building and searching functions.
       6                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       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: shptree.c,v $
      37                 :  * Revision 1.12  2008/11/12 15:39:50  fwarmerdam
      38                 :  * improve safety in face of buggy .shp file.
      39                 :  *
      40                 :  * Revision 1.11  2007/10/27 03:31:14  fwarmerdam
      41                 :  * limit default depth of tree to 12 levels (gdal ticket #1594)
      42                 :  *
      43                 :  * Revision 1.10  2005/01/03 22:30:13  fwarmerdam
      44                 :  * added support for saved quadtrees
      45                 :  *
      46                 :  * Revision 1.9  2003/01/28 15:53:41  warmerda
      47                 :  * Avoid build warnings.
      48                 :  *
      49                 :  * Revision 1.8  2002/05/07 13:07:45  warmerda
      50                 :  * use qsort() - patch from Bernhard Herzog
      51                 :  *
      52                 :  * Revision 1.7  2002/01/15 14:36:07  warmerda
      53                 :  * updated email address
      54                 :  *
      55                 :  * Revision 1.6  2001/05/23 13:36:52  warmerda
      56                 :  * added use of SHPAPI_CALL
      57                 :  *
      58                 :  * Revision 1.5  1999/11/05 14:12:05  warmerda
      59                 :  * updated license terms
      60                 :  *
      61                 :  * Revision 1.4  1999/06/02 18:24:21  warmerda
      62                 :  * added trimming code
      63                 :  *
      64                 :  * Revision 1.3  1999/06/02 17:56:12  warmerda
      65                 :  * added quad'' subnode support for trees
      66                 :  *
      67                 :  * Revision 1.2  1999/05/18 19:11:11  warmerda
      68                 :  * Added example searching capability
      69                 :  *
      70                 :  * Revision 1.1  1999/05/18 17:49:20  warmerda
      71                 :  * New
      72                 :  *
      73                 :  */
      74                 : 
      75                 : #include "shapefil.h"
      76                 : 
      77                 : #include <math.h>
      78                 : #include <assert.h>
      79                 : #include <stdlib.h>
      80                 : #include <string.h>
      81                 : #ifdef USE_CPL
      82                 : #include <cpl_error.h>
      83                 : #endif
      84                 : 
      85               0 : SHP_CVSID("$Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $")
      86                 : 
      87                 : #ifndef TRUE
      88                 : #  define TRUE 1
      89                 : #  define FALSE 0
      90                 : #endif
      91                 : 
      92                 : static int bBigEndian = 0;
      93                 : 
      94                 : 
      95                 : /* -------------------------------------------------------------------- */
      96                 : /*      If the following is 0.5, nodes will be split in half.  If it    */
      97                 : /*      is 0.6 then each subnode will contain 60% of the parent         */
      98                 : /*      node, with 20% representing overlap.  This can be help to       */
      99                 : /*      prevent small objects on a boundary from shifting too high      */
     100                 : /*      up the tree.                                                    */
     101                 : /* -------------------------------------------------------------------- */
     102                 : 
     103                 : #define SHP_SPLIT_RATIO 0.55
     104                 : 
     105                 : /************************************************************************/
     106                 : /*                             SfRealloc()                              */
     107                 : /*                                                                      */
     108                 : /*      A realloc cover function that will access a NULL pointer as     */
     109                 : /*      a valid input.                                                  */
     110                 : /************************************************************************/
     111                 : 
     112           14665 : static void * SfRealloc( void * pMem, int nNewSize )
     113                 : 
     114                 : {
     115           14665 :     if( pMem == NULL )
     116           14654 :         return( (void *) malloc(nNewSize) );
     117                 :     else
     118              11 :         return( (void *) realloc(pMem,nNewSize) );
     119                 : }
     120                 : 
     121                 : /************************************************************************/
     122                 : /*                          SHPTreeNodeInit()                           */
     123                 : /*                                                                      */
     124                 : /*      Initialize a tree node.                                         */
     125                 : /************************************************************************/
     126                 : 
     127          219099 : static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin,
     128                 :                                        double * padfBoundsMax )
     129                 : 
     130                 : {
     131                 :     SHPTreeNode *psTreeNode;
     132                 : 
     133          219099 :     psTreeNode = (SHPTreeNode *) malloc(sizeof(SHPTreeNode));
     134          219099 :   if( NULL == psTreeNode )
     135                 :   {
     136                 : #ifdef USE_CPL
     137               0 :     CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure");
     138                 : #endif
     139               0 :     return NULL;
     140                 :   }
     141                 : 
     142          219099 :     psTreeNode->nShapeCount = 0;
     143          219099 :     psTreeNode->panShapeIds = NULL;
     144          219099 :     psTreeNode->papsShapeObj = NULL;
     145                 : 
     146          219099 :     psTreeNode->nSubNodes = 0;
     147                 : 
     148          219099 :     if( padfBoundsMin != NULL )
     149          219096 :         memcpy( psTreeNode->adfBoundsMin, padfBoundsMin, sizeof(double) * 4 );
     150                 : 
     151          219099 :     if( padfBoundsMax != NULL )
     152          219096 :         memcpy( psTreeNode->adfBoundsMax, padfBoundsMax, sizeof(double) * 4 );
     153                 : 
     154          219099 :     return psTreeNode;
     155                 : }
     156                 : 
     157                 : 
     158                 : /************************************************************************/
     159                 : /*                           SHPCreateTree()                            */
     160                 : /************************************************************************/
     161                 : 
     162                 : SHPTree SHPAPI_CALL1(*)
     163               3 : SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth,
     164                 :                double *padfBoundsMin, double *padfBoundsMax )
     165                 : 
     166                 : {
     167                 :     SHPTree *psTree;
     168                 : 
     169               3 :     if( padfBoundsMin == NULL && hSHP == NULL )
     170               0 :         return NULL;
     171                 : 
     172                 : /* -------------------------------------------------------------------- */
     173                 : /*      Allocate the tree object                                        */
     174                 : /* -------------------------------------------------------------------- */
     175               3 :     psTree = (SHPTree *) malloc(sizeof(SHPTree));
     176               3 :   if( NULL == psTree )
     177                 :   {
     178                 : #ifdef USE_CPL
     179               0 :     CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure");
     180                 : #endif
     181               0 :     return NULL;
     182                 :   }
     183                 : 
     184               3 :     psTree->hSHP = hSHP;
     185               3 :     psTree->nMaxDepth = nMaxDepth;
     186               3 :     psTree->nDimension = nDimension;
     187               3 :     psTree->nTotalCount = 0;
     188                 : 
     189                 : /* -------------------------------------------------------------------- */
     190                 : /*      If no max depth was defined, try to select a reasonable one     */
     191                 : /*      that implies approximately 8 shapes per node.                   */
     192                 : /* -------------------------------------------------------------------- */
     193               3 :     if( psTree->nMaxDepth == 0 && hSHP != NULL )
     194                 :     {
     195               3 :         int nMaxNodeCount = 1;
     196                 :         int nShapeCount;
     197                 : 
     198               3 :         SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL );
     199              20 :         while( nMaxNodeCount*4 < nShapeCount )
     200                 :         {
     201              14 :             psTree->nMaxDepth += 1;
     202              14 :             nMaxNodeCount = nMaxNodeCount * 2;
     203                 :         }
     204                 : 
     205                 : #ifdef USE_CPL
     206               3 :         CPLDebug( "Shape",
     207                 :                   "Estimated spatial index tree depth: %d",
     208                 :                   psTree->nMaxDepth );
     209                 : #endif
     210                 : 
     211                 :         /* NOTE: Due to problems with memory allocation for deep trees,
     212                 :          * automatically estimated depth is limited up to 12 levels.
     213                 :          * See Ticket #1594 for detailed discussion.
     214                 :          */
     215               3 :         if( psTree->nMaxDepth > MAX_DEFAULT_TREE_DEPTH )
     216                 :         {
     217               0 :             psTree->nMaxDepth = MAX_DEFAULT_TREE_DEPTH;
     218                 : 
     219                 : #ifdef USE_CPL
     220               0 :         CPLDebug( "Shape",
     221                 :                   "Falling back to max number of allowed index tree levels (%d).",
     222                 :                   MAX_DEFAULT_TREE_DEPTH );
     223                 : #endif
     224                 :         }
     225                 :     }
     226                 : 
     227                 : /* -------------------------------------------------------------------- */
     228                 : /*      Allocate the root node.                                         */
     229                 : /* -------------------------------------------------------------------- */
     230               3 :     psTree->psRoot = SHPTreeNodeCreate( padfBoundsMin, padfBoundsMax );
     231               3 :   if( NULL == psTree->psRoot )
     232                 :   {
     233               0 :     return NULL;
     234                 :   }
     235                 : 
     236                 : /* -------------------------------------------------------------------- */
     237                 : /*      Assign the bounds to the root node.  If none are passed in,     */
     238                 : /*      use the bounds of the provided file otherwise the create        */
     239                 : /*      function will have already set the bounds.                      */
     240                 : /* -------------------------------------------------------------------- */
     241               3 :   assert( NULL != psTree );
     242               3 :   assert( NULL != psTree->psRoot );
     243                 :   
     244               3 :   if( padfBoundsMin == NULL )
     245                 :     {
     246               6 :         SHPGetInfo( hSHP, NULL, NULL,
     247               3 :                     psTree->psRoot->adfBoundsMin, 
     248               3 :                     psTree->psRoot->adfBoundsMax );
     249                 :     }
     250                 : 
     251                 : /* -------------------------------------------------------------------- */
     252                 : /*      If we have a file, insert all it's shapes into the tree.        */
     253                 : /* -------------------------------------------------------------------- */
     254               3 :     if( hSHP != NULL )
     255                 :     {
     256                 :         int iShape, nShapeCount;
     257                 :         
     258               3 :         SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL );
     259                 : 
     260           14659 :         for( iShape = 0; iShape < nShapeCount; iShape++ )
     261                 :         {
     262                 :             SHPObject *psShape;
     263                 :             
     264           14656 :             psShape = SHPReadObject( hSHP, iShape );
     265           14656 :             if( psShape != NULL )
     266                 :             {
     267           14656 :                 SHPTreeAddShapeId( psTree, psShape );
     268           14656 :                 SHPDestroyObject( psShape );
     269                 :             }
     270                 :         }
     271                 :     }        
     272                 : 
     273               3 :     return psTree;
     274                 : }
     275                 : 
     276                 : /************************************************************************/
     277                 : /*                         SHPDestroyTreeNode()                         */
     278                 : /************************************************************************/
     279                 : 
     280          219099 : static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode )
     281                 : 
     282                 : {
     283                 :     int   i;
     284                 :     
     285          219099 :   assert( NULL != psTreeNode );
     286                 : 
     287          288514 :     for( i = 0; i < psTreeNode->nSubNodes; i++ )
     288                 :     {
     289           69415 :         if( psTreeNode->apsSubNode[i] != NULL )
     290           69415 :             SHPDestroyTreeNode( psTreeNode->apsSubNode[i] );
     291                 :     }
     292                 :     
     293          219099 :     if( psTreeNode->panShapeIds != NULL )
     294           14645 :         free( psTreeNode->panShapeIds );
     295                 : 
     296          219099 :     if( psTreeNode->papsShapeObj != NULL )
     297                 :     {
     298               0 :         for( i = 0; i < psTreeNode->nShapeCount; i++ )
     299                 :         {
     300               0 :             if( psTreeNode->papsShapeObj[i] != NULL )
     301               0 :                 SHPDestroyObject( psTreeNode->papsShapeObj[i] );
     302                 :         }
     303                 : 
     304               0 :         free( psTreeNode->papsShapeObj );
     305                 :     }
     306                 : 
     307          219099 :     free( psTreeNode );
     308          219099 : }
     309                 : 
     310                 : /************************************************************************/
     311                 : /*                           SHPDestroyTree()                           */
     312                 : /************************************************************************/
     313                 : 
     314                 : void SHPAPI_CALL
     315               3 : SHPDestroyTree( SHPTree * psTree )
     316                 : 
     317                 : {
     318               3 :     SHPDestroyTreeNode( psTree->psRoot );
     319               3 :     free( psTree );
     320               3 : }
     321                 : 
     322                 : /************************************************************************/
     323                 : /*                       SHPCheckBoundsOverlap()                        */
     324                 : /*                                                                      */
     325                 : /*      Do the given boxes overlap at all?                              */
     326                 : /************************************************************************/
     327                 : 
     328                 : int SHPAPI_CALL
     329              28 : SHPCheckBoundsOverlap( double * padfBox1Min, double * padfBox1Max,
     330                 :                        double * padfBox2Min, double * padfBox2Max,
     331                 :                        int nDimension )
     332                 : 
     333                 : {
     334                 :     int   iDim;
     335                 : 
     336              67 :     for( iDim = 0; iDim < nDimension; iDim++ )
     337                 :     {
     338              50 :         if( padfBox2Max[iDim] < padfBox1Min[iDim] )
     339              11 :             return FALSE;
     340                 :         
     341              39 :         if( padfBox1Max[iDim] < padfBox2Min[iDim] )
     342               0 :             return FALSE;
     343                 :     }
     344                 : 
     345              17 :     return TRUE;
     346                 : }
     347                 : 
     348                 : /************************************************************************/
     349                 : /*                      SHPCheckObjectContained()                       */
     350                 : /*                                                                      */
     351                 : /*      Does the given shape fit within the indicated extents?          */
     352                 : /************************************************************************/
     353                 : 
     354          621218 : static int SHPCheckObjectContained( SHPObject * psObject, int nDimension,
     355                 :                            double * padfBoundsMin, double * padfBoundsMax )
     356                 : 
     357                 : {
     358         1826822 :     if( psObject->dfXMin < padfBoundsMin[0]
     359         1826822 :         || psObject->dfXMax > padfBoundsMax[0] )
     360          252118 :         return FALSE;
     361                 :     
     362         1107300 :     if( psObject->dfYMin < padfBoundsMin[1]
     363         1107300 :         || psObject->dfYMax > padfBoundsMax[1] )
     364          153268 :         return FALSE;
     365                 : 
     366          215832 :     if( nDimension == 2 )
     367          215832 :         return TRUE;
     368                 :     
     369               0 :     if( psObject->dfZMin < padfBoundsMin[2]
     370               0 :         || psObject->dfZMax < padfBoundsMax[2] )
     371               0 :         return FALSE;
     372                 :         
     373               0 :     if( nDimension == 3 )
     374               0 :         return TRUE;
     375                 : 
     376               0 :     if( psObject->dfMMin < padfBoundsMin[3]
     377               0 :         || psObject->dfMMax < padfBoundsMax[3] )
     378               0 :         return FALSE;
     379                 : 
     380               0 :     return TRUE;
     381                 : }
     382                 : 
     383                 : /************************************************************************/
     384                 : /*                         SHPTreeSplitBounds()                         */
     385                 : /*                                                                      */
     386                 : /*      Split a region into two subregion evenly, cutting along the     */
     387                 : /*      longest dimension.                                              */
     388                 : /************************************************************************/
     389                 : 
     390                 : void SHPAPI_CALL
     391          164325 : SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn,
     392                 :                     double *padfBoundsMin1, double * padfBoundsMax1,
     393                 :                     double *padfBoundsMin2, double * padfBoundsMax2 )
     394                 : 
     395                 : {
     396                 : /* -------------------------------------------------------------------- */
     397                 : /*      The output bounds will be very similar to the input bounds,     */
     398                 : /*      so just copy over to start.                                     */
     399                 : /* -------------------------------------------------------------------- */
     400          164325 :     memcpy( padfBoundsMin1, padfBoundsMinIn, sizeof(double) * 4 );
     401          164325 :     memcpy( padfBoundsMax1, padfBoundsMaxIn, sizeof(double) * 4 );
     402          164325 :     memcpy( padfBoundsMin2, padfBoundsMinIn, sizeof(double) * 4 );
     403          164325 :     memcpy( padfBoundsMax2, padfBoundsMaxIn, sizeof(double) * 4 );
     404                 :     
     405                 : /* -------------------------------------------------------------------- */
     406                 : /*      Split in X direction.                                           */
     407                 : /* -------------------------------------------------------------------- */
     408          328650 :     if( (padfBoundsMaxIn[0] - padfBoundsMinIn[0])
     409          164325 :               > (padfBoundsMaxIn[1] - padfBoundsMinIn[1]) )
     410                 :     {
     411          109548 :         double  dfRange = padfBoundsMaxIn[0] - padfBoundsMinIn[0];
     412                 : 
     413          109548 :         padfBoundsMax1[0] = padfBoundsMinIn[0] + dfRange * SHP_SPLIT_RATIO;
     414          109548 :         padfBoundsMin2[0] = padfBoundsMaxIn[0] - dfRange * SHP_SPLIT_RATIO;
     415                 :     }
     416                 : 
     417                 : /* -------------------------------------------------------------------- */
     418                 : /*      Otherwise split in Y direction.                                 */
     419                 : /* -------------------------------------------------------------------- */
     420                 :     else
     421                 :     {
     422           54777 :         double  dfRange = padfBoundsMaxIn[1] - padfBoundsMinIn[1];
     423                 : 
     424           54777 :         padfBoundsMax1[1] = padfBoundsMinIn[1] + dfRange * SHP_SPLIT_RATIO;
     425           54777 :         padfBoundsMin2[1] = padfBoundsMaxIn[1] - dfRange * SHP_SPLIT_RATIO;
     426                 :     }
     427          164325 : }
     428                 : 
     429                 : /************************************************************************/
     430                 : /*                       SHPTreeNodeAddShapeId()                        */
     431                 : /************************************************************************/
     432                 : 
     433                 : static int
     434          230488 : SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject,
     435                 :                        int nMaxDepth, int nDimension )
     436                 : 
     437                 : {
     438                 :     int   i;
     439                 :     
     440                 : /* -------------------------------------------------------------------- */
     441                 : /*      If there are subnodes, then consider wiether this object        */
     442                 : /*      will fit in them.                                               */
     443                 : /* -------------------------------------------------------------------- */
     444          230491 :     if( nMaxDepth > 1 && psTreeNode->nSubNodes > 0 )
     445                 :     {
     446          452571 :         for( i = 0; i < psTreeNode->nSubNodes; i++ )
     447                 :         {
     448          905136 :             if( SHPCheckObjectContained(psObject, nDimension,
     449          452568 :                                       psTreeNode->apsSubNode[i]->adfBoundsMin,
     450          452568 :                                       psTreeNode->apsSubNode[i]->adfBoundsMax))
     451                 :             {
     452          161058 :                 return SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[i],
     453                 :                                               psObject, nMaxDepth-1,
     454                 :                                               nDimension );
     455                 :             }
     456                 :         }
     457                 :     }
     458                 : 
     459                 : /* -------------------------------------------------------------------- */
     460                 : /*      Otherwise, consider creating four subnodes if could fit into    */
     461                 : /*      them, and adding to the appropriate subnode.                    */
     462                 : /* -------------------------------------------------------------------- */
     463                 : #if MAX_SUBNODE == 4
     464           69427 :     else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 )
     465                 :     {
     466                 :         double  adfBoundsMinH1[4], adfBoundsMaxH1[4];
     467                 :         double  adfBoundsMinH2[4], adfBoundsMaxH2[4];
     468                 :         double  adfBoundsMin1[4], adfBoundsMax1[4];
     469                 :         double  adfBoundsMin2[4], adfBoundsMax2[4];
     470                 :         double  adfBoundsMin3[4], adfBoundsMax3[4];
     471                 :         double  adfBoundsMin4[4], adfBoundsMax4[4];
     472                 : 
     473           54775 :         SHPTreeSplitBounds( psTreeNode->adfBoundsMin,
     474                 :                             psTreeNode->adfBoundsMax,
     475                 :                             adfBoundsMinH1, adfBoundsMaxH1,
     476                 :                             adfBoundsMinH2, adfBoundsMaxH2 );
     477                 : 
     478           54775 :         SHPTreeSplitBounds( adfBoundsMinH1, adfBoundsMaxH1,
     479                 :                             adfBoundsMin1, adfBoundsMax1,
     480                 :                             adfBoundsMin2, adfBoundsMax2 );
     481                 : 
     482           54775 :         SHPTreeSplitBounds( adfBoundsMinH2, adfBoundsMaxH2,
     483                 :                             adfBoundsMin3, adfBoundsMax3,
     484                 :                             adfBoundsMin4, adfBoundsMax4 );
     485                 : 
     486          168650 :         if( SHPCheckObjectContained(psObject, nDimension,
     487                 :                                     adfBoundsMin1, adfBoundsMax1)
     488           54775 :             || SHPCheckObjectContained(psObject, nDimension,
     489                 :                                     adfBoundsMin2, adfBoundsMax2)
     490           49676 :             || SHPCheckObjectContained(psObject, nDimension,
     491                 :                                     adfBoundsMin3, adfBoundsMax3)
     492           39873 :             || SHPCheckObjectContained(psObject, nDimension,
     493           24326 :                                     adfBoundsMin4, adfBoundsMax4) )
     494                 :         {
     495           54774 :             psTreeNode->nSubNodes = 4;
     496           54774 :             psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1,
     497                 :                                                            adfBoundsMax1 );
     498           54774 :             psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2,
     499                 :                                                            adfBoundsMax2 );
     500           54774 :             psTreeNode->apsSubNode[2] = SHPTreeNodeCreate( adfBoundsMin3,
     501                 :                                                            adfBoundsMax3 );
     502           54774 :             psTreeNode->apsSubNode[3] = SHPTreeNodeCreate( adfBoundsMin4,
     503                 :                                                            adfBoundsMax4 );
     504                 : 
     505                 :             /* recurse back on this node now that it has subnodes */
     506           54774 :             return( SHPTreeNodeAddShapeId( psTreeNode, psObject,
     507                 :                                            nMaxDepth, nDimension ) );
     508                 :         }
     509                 :     }
     510                 : #endif /* MAX_SUBNODE == 4 */
     511                 : 
     512                 : /* -------------------------------------------------------------------- */
     513                 : /*      Otherwise, consider creating two subnodes if could fit into     */
     514                 : /*      them, and adding to the appropriate subnode.                    */
     515                 : /* -------------------------------------------------------------------- */
     516                 : #if MAX_SUBNODE == 2
     517                 :     else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 )
     518                 :     {
     519                 :         double  adfBoundsMin1[4], adfBoundsMax1[4];
     520                 :         double  adfBoundsMin2[4], adfBoundsMax2[4];
     521                 : 
     522                 :         SHPTreeSplitBounds( psTreeNode->adfBoundsMin, psTreeNode->adfBoundsMax,
     523                 :                             adfBoundsMin1, adfBoundsMax1,
     524                 :                             adfBoundsMin2, adfBoundsMax2 );
     525                 : 
     526                 :         if( SHPCheckObjectContained(psObject, nDimension,
     527                 :                                  adfBoundsMin1, adfBoundsMax1))
     528                 :         {
     529                 :             psTreeNode->nSubNodes = 2;
     530                 :             psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1,
     531                 :                                                            adfBoundsMax1 );
     532                 :             psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2,
     533                 :                                                            adfBoundsMax2 );
     534                 : 
     535                 :             return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[0], psObject,
     536                 :                                            nMaxDepth - 1, nDimension ) );
     537                 :         }
     538                 :         else if( SHPCheckObjectContained(psObject, nDimension,
     539                 :                                          adfBoundsMin2, adfBoundsMax2) )
     540                 :         {
     541                 :             psTreeNode->nSubNodes = 2;
     542                 :             psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1,
     543                 :                                                            adfBoundsMax1 );
     544                 :             psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2,
     545                 :                                                            adfBoundsMax2 );
     546                 : 
     547                 :             return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[1], psObject,
     548                 :                                            nMaxDepth - 1, nDimension ) );
     549                 :         }
     550                 :     }
     551                 : #endif /* MAX_SUBNODE == 2 */
     552                 : 
     553                 : /* -------------------------------------------------------------------- */
     554                 : /*      If none of that worked, just add it to this nodes list.         */
     555                 : /* -------------------------------------------------------------------- */
     556           14656 :     psTreeNode->nShapeCount++;
     557                 : 
     558           29312 :     psTreeNode->panShapeIds = (int *) 
     559           14656 :         SfRealloc( psTreeNode->panShapeIds,
     560           14656 :                    sizeof(int) * psTreeNode->nShapeCount );
     561           14656 :     psTreeNode->panShapeIds[psTreeNode->nShapeCount-1] = psObject->nShapeId;
     562                 : 
     563           14656 :     if( psTreeNode->papsShapeObj != NULL )
     564                 :     {
     565               0 :         psTreeNode->papsShapeObj = (SHPObject **)
     566               0 :             SfRealloc( psTreeNode->papsShapeObj,
     567               0 :                        sizeof(void *) * psTreeNode->nShapeCount );
     568               0 :         psTreeNode->papsShapeObj[psTreeNode->nShapeCount-1] = NULL;
     569                 :     }
     570                 : 
     571           14656 :     return TRUE;
     572                 : }
     573                 : 
     574                 : /************************************************************************/
     575                 : /*                         SHPTreeAddShapeId()                          */
     576                 : /*                                                                      */
     577                 : /*      Add a shape to the tree, but don't keep a pointer to the        */
     578                 : /*      object data, just keep the shapeid.                             */
     579                 : /************************************************************************/
     580                 : 
     581                 : int SHPAPI_CALL
     582           14656 : SHPTreeAddShapeId( SHPTree * psTree, SHPObject * psObject )
     583                 : 
     584                 : {
     585           14656 :     psTree->nTotalCount++;
     586                 : 
     587           14656 :     return( SHPTreeNodeAddShapeId( psTree->psRoot, psObject,
     588                 :                                    psTree->nMaxDepth, psTree->nDimension ) );
     589                 : }
     590                 : 
     591                 : /************************************************************************/
     592                 : /*                      SHPTreeCollectShapesIds()                       */
     593                 : /*                                                                      */
     594                 : /*      Work function implementing SHPTreeFindLikelyShapes() on a       */
     595                 : /*      tree node by tree node basis.                                   */
     596                 : /************************************************************************/
     597                 : 
     598                 : void SHPAPI_CALL
     599               0 : SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode,
     600                 :                         double * padfBoundsMin, double * padfBoundsMax,
     601                 :                         int * pnShapeCount, int * pnMaxShapes,
     602                 :                         int ** ppanShapeList )
     603                 : 
     604                 : {
     605                 :     int   i;
     606                 :     
     607                 : /* -------------------------------------------------------------------- */
     608                 : /*      Does this node overlap the area of interest at all?  If not,    */
     609                 : /*      return without adding to the list at all.                       */
     610                 : /* -------------------------------------------------------------------- */
     611               0 :     if( !SHPCheckBoundsOverlap( psTreeNode->adfBoundsMin,
     612                 :                                 psTreeNode->adfBoundsMax,
     613                 :                                 padfBoundsMin,
     614                 :                                 padfBoundsMax,
     615                 :                                 hTree->nDimension ) )
     616               0 :         return;
     617                 : 
     618                 : /* -------------------------------------------------------------------- */
     619                 : /*      Grow the list to hold the shapes on this node.                  */
     620                 : /* -------------------------------------------------------------------- */
     621               0 :     if( *pnShapeCount + psTreeNode->nShapeCount > *pnMaxShapes )
     622                 :     {
     623               0 :         *pnMaxShapes = (*pnShapeCount + psTreeNode->nShapeCount) * 2 + 20;
     624               0 :         *ppanShapeList = (int *)
     625               0 :             SfRealloc(*ppanShapeList,sizeof(int) * *pnMaxShapes);
     626                 :     }
     627                 : 
     628                 : /* -------------------------------------------------------------------- */
     629                 : /*      Add the local nodes shapeids to the list.                       */
     630                 : /* -------------------------------------------------------------------- */
     631               0 :     for( i = 0; i < psTreeNode->nShapeCount; i++ )
     632                 :     {
     633               0 :         (*ppanShapeList)[(*pnShapeCount)++] = psTreeNode->panShapeIds[i];
     634                 :     }
     635                 :     
     636                 : /* -------------------------------------------------------------------- */
     637                 : /*      Recurse to subnodes if they exist.                              */
     638                 : /* -------------------------------------------------------------------- */
     639               0 :     for( i = 0; i < psTreeNode->nSubNodes; i++ )
     640                 :     {
     641               0 :         if( psTreeNode->apsSubNode[i] != NULL )
     642               0 :             SHPTreeCollectShapeIds( hTree, psTreeNode->apsSubNode[i],
     643                 :                                     padfBoundsMin, padfBoundsMax,
     644                 :                                     pnShapeCount, pnMaxShapes,
     645                 :                                     ppanShapeList );
     646                 :     }
     647                 : }
     648                 : 
     649                 : /************************************************************************/
     650                 : /*                      SHPTreeFindLikelyShapes()                       */
     651                 : /*                                                                      */
     652                 : /*      Find all shapes within tree nodes for which the tree node       */
     653                 : /*      bounding box overlaps the search box.  The return value is      */
     654                 : /*      an array of shapeids terminated by a -1.  The shapeids will     */
     655                 : /*      be in order, as hopefully this will result in faster (more      */
     656                 : /*      sequential) reading from the file.                              */
     657                 : /************************************************************************/
     658                 : 
     659                 : /* helper for qsort */
     660                 : static int
     661              72 : compare_ints( const void * a, const void * b)
     662                 : {
     663              72 :     return (*(int*)a) - (*(int*)b);
     664                 : }
     665                 : 
     666                 : int SHPAPI_CALL1(*)
     667               0 : SHPTreeFindLikelyShapes( SHPTree * hTree,
     668                 :                          double * padfBoundsMin, double * padfBoundsMax,
     669                 :                          int * pnShapeCount )
     670                 : 
     671                 : {
     672               0 :     int *panShapeList=NULL, nMaxShapes = 0;
     673                 : 
     674                 : /* -------------------------------------------------------------------- */
     675                 : /*      Perform the search by recursive descent.                        */
     676                 : /* -------------------------------------------------------------------- */
     677               0 :     *pnShapeCount = 0;
     678                 : 
     679               0 :     SHPTreeCollectShapeIds( hTree, hTree->psRoot,
     680                 :                             padfBoundsMin, padfBoundsMax,
     681                 :                             pnShapeCount, &nMaxShapes,
     682                 :                             &panShapeList );
     683                 : 
     684                 : /* -------------------------------------------------------------------- */
     685                 : /*      Sort the id array                                               */
     686                 : /* -------------------------------------------------------------------- */
     687                 : 
     688               0 :     qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints);
     689                 : 
     690               0 :     return panShapeList;
     691                 : }
     692                 : 
     693                 : /************************************************************************/
     694                 : /*                          SHPTreeNodeTrim()                           */
     695                 : /*                                                                      */
     696                 : /*      This is the recurve version of SHPTreeTrimExtraNodes() that     */
     697                 : /*      walks the tree cleaning it up.                                  */
     698                 : /************************************************************************/
     699                 : 
     700          219099 : static int SHPTreeNodeTrim( SHPTreeNode * psTreeNode )
     701                 : 
     702                 : {
     703                 :     int   i;
     704                 : 
     705                 : /* -------------------------------------------------------------------- */
     706                 : /*      Trim subtrees, and free subnodes that come back empty.          */
     707                 : /* -------------------------------------------------------------------- */
     708          438195 :     for( i = 0; i < psTreeNode->nSubNodes; i++ )
     709                 :     {
     710          219096 :         if( SHPTreeNodeTrim( psTreeNode->apsSubNode[i] ) )
     711                 :         {
     712          149681 :             SHPDestroyTreeNode( psTreeNode->apsSubNode[i] );
     713                 : 
     714          299362 :             psTreeNode->apsSubNode[i] =
     715          149681 :                 psTreeNode->apsSubNode[psTreeNode->nSubNodes-1];
     716                 : 
     717          149681 :             psTreeNode->nSubNodes--;
     718                 : 
     719          149681 :             i--; /* process the new occupant of this subnode entry */
     720                 :         }
     721                 :     }
     722                 : 
     723                 : /* -------------------------------------------------------------------- */
     724                 : /*      We should be trimmed if we have no subnodes, and no shapes.     */
     725                 : /* -------------------------------------------------------------------- */
     726          219099 :     return( psTreeNode->nSubNodes == 0 && psTreeNode->nShapeCount == 0 );
     727                 : }
     728                 : 
     729                 : /************************************************************************/
     730                 : /*                       SHPTreeTrimExtraNodes()                        */
     731                 : /*                                                                      */
     732                 : /*      Trim empty nodes from the tree.  Note that we never trim an     */
     733                 : /*      empty root node.                                                */
     734                 : /************************************************************************/
     735                 : 
     736                 : void SHPAPI_CALL
     737               3 : SHPTreeTrimExtraNodes( SHPTree * hTree )
     738                 : 
     739                 : {
     740               3 :     SHPTreeNodeTrim( hTree->psRoot );
     741               3 : }
     742                 : 
     743                 : /************************************************************************/
     744                 : /*                              SwapWord()                              */
     745                 : /*                                                                      */
     746                 : /*      Swap a 2, 4 or 8 byte word.                                     */
     747                 : /************************************************************************/
     748                 : 
     749               0 : static void SwapWord( int length, void * wordP )
     750                 : 
     751                 : {
     752                 :     int   i;
     753                 :     unsigned char temp;
     754                 : 
     755               0 :     for( i=0; i < length/2; i++ )
     756                 :     {
     757               0 :   temp = ((unsigned char *) wordP)[i];
     758               0 :   ((unsigned char *)wordP)[i] = ((unsigned char *) wordP)[length-i-1];
     759               0 :   ((unsigned char *) wordP)[length-i-1] = temp;
     760                 :     }
     761               0 : }
     762                 : 
     763                 : /************************************************************************/
     764                 : /*                       SHPSearchDiskTreeNode()                        */
     765                 : /************************************************************************/
     766                 : 
     767                 : static int
     768              28 : SHPSearchDiskTreeNode( FILE *fp, double *padfBoundsMin, double *padfBoundsMax,
     769                 :                        int **ppanResultBuffer, int *pnBufferMax, 
     770                 :                        int *pnResultCount, int bNeedSwap )
     771                 : 
     772                 : {
     773                 :     int i;
     774                 :     int offset;
     775                 :     int numshapes, numsubnodes;
     776                 :     double adfNodeBoundsMin[2], adfNodeBoundsMax[2];
     777                 : 
     778                 : /* -------------------------------------------------------------------- */
     779                 : /*      Read and unswap first part of node info.                        */
     780                 : /* -------------------------------------------------------------------- */
     781              28 :     fread( &offset, 4, 1, fp );
     782              28 :     if ( bNeedSwap ) SwapWord ( 4, &offset );
     783                 : 
     784              28 :     fread( adfNodeBoundsMin, sizeof(double), 2, fp );
     785              28 :     fread( adfNodeBoundsMax, sizeof(double), 2, fp );
     786              28 :     if ( bNeedSwap )
     787                 :     {
     788               0 :         SwapWord( 8, adfNodeBoundsMin + 0 );
     789               0 :         SwapWord( 8, adfNodeBoundsMin + 1 );
     790               0 :         SwapWord( 8, adfNodeBoundsMax + 0 );
     791               0 :         SwapWord( 8, adfNodeBoundsMax + 1 );
     792                 :     }
     793                 :       
     794              28 :     fread( &numshapes, 4, 1, fp );
     795              28 :     if ( bNeedSwap ) SwapWord ( 4, &numshapes );
     796                 : 
     797                 : /* -------------------------------------------------------------------- */
     798                 : /*      If we don't overlap this node at all, we can just fseek()       */
     799                 : /*      pass this node info and all subnodes.                           */
     800                 : /* -------------------------------------------------------------------- */
     801              28 :     if( !SHPCheckBoundsOverlap( adfNodeBoundsMin, adfNodeBoundsMax, 
     802                 :                                 padfBoundsMin, padfBoundsMax, 2 ) )
     803                 :     {
     804              11 :         offset += numshapes*sizeof(int) + sizeof(int);
     805              11 :         fseek(fp, offset, SEEK_CUR);
     806              11 :         return TRUE;
     807                 :     }
     808                 : 
     809                 : /* -------------------------------------------------------------------- */
     810                 : /*      Add all the shapeids at this node to our list.                  */
     811                 : /* -------------------------------------------------------------------- */
     812              17 :     if(numshapes > 0) 
     813                 :     {
     814              17 :         if( *pnResultCount + numshapes > *pnBufferMax )
     815                 :         {
     816               9 :             *pnBufferMax = (int) ((*pnResultCount + numshapes + 100) * 1.25);
     817              18 :             *ppanResultBuffer = (int *) 
     818              18 :                 SfRealloc( *ppanResultBuffer, *pnBufferMax * sizeof(int) );
     819                 :         }
     820                 : 
     821              17 :         fread( *ppanResultBuffer + *pnResultCount, 
     822                 :                sizeof(int), numshapes, fp );
     823                 : 
     824              17 :         if (bNeedSwap )
     825                 :         {
     826               0 :             for( i=0; i<numshapes; i++ )
     827               0 :                 SwapWord( 4, *ppanResultBuffer + *pnResultCount + i );
     828                 :         }
     829                 : 
     830              17 :         *pnResultCount += numshapes; 
     831                 :     } 
     832                 : 
     833                 : /* -------------------------------------------------------------------- */
     834                 : /*      Process the subnodes.                                           */
     835                 : /* -------------------------------------------------------------------- */
     836              17 :     fread( &numsubnodes, 4, 1, fp );
     837              17 :     if ( bNeedSwap  ) SwapWord ( 4, &numsubnodes );
     838                 : 
     839              35 :     for(i=0; i<numsubnodes; i++)
     840                 :     {
     841              18 :         if( !SHPSearchDiskTreeNode( fp, padfBoundsMin, padfBoundsMax, 
     842                 :                                     ppanResultBuffer, pnBufferMax, 
     843                 :                                     pnResultCount, bNeedSwap ) )
     844               0 :             return FALSE;
     845                 :     }
     846                 : 
     847              17 :     return TRUE;
     848                 : }
     849                 : 
     850                 : /************************************************************************/
     851                 : /*                         SHPSearchDiskTree()                          */
     852                 : /************************************************************************/
     853                 : 
     854                 : int SHPAPI_CALL1(*) 
     855              10 : SHPSearchDiskTree( FILE *fp, 
     856                 :                    double *padfBoundsMin, double *padfBoundsMax,
     857                 :                    int *pnShapeCount )
     858                 : 
     859                 : {
     860              10 :     int i, bNeedSwap, nBufferMax = 0;
     861                 :     unsigned char abyBuf[16];
     862              10 :     int *panResultBuffer = NULL;
     863                 : 
     864              10 :     *pnShapeCount = 0;
     865                 : 
     866                 : /* -------------------------------------------------------------------- */
     867                 : /*  Establish the byte order on this machine.             */
     868                 : /* -------------------------------------------------------------------- */
     869              10 :     i = 1;
     870              10 :     if( *((unsigned char *) &i) == 1 )
     871              10 :         bBigEndian = FALSE;
     872                 :     else
     873               0 :         bBigEndian = TRUE;
     874                 : 
     875                 : /* -------------------------------------------------------------------- */
     876                 : /*      Read the header.                                                */
     877                 : /* -------------------------------------------------------------------- */
     878              10 :     fseek( fp, 0, SEEK_SET );
     879              10 :     fread( abyBuf, 16, 1, fp );
     880                 : 
     881              10 :     if( memcmp( abyBuf, "SQT", 3 ) != 0 )
     882               0 :         return NULL;
     883                 : 
     884              40 :     if( (abyBuf[3] == 2 && bBigEndian)
     885              20 :         || (abyBuf[3] == 1 && !bBigEndian) )
     886              10 :         bNeedSwap = FALSE;
     887                 :     else
     888               0 :         bNeedSwap = TRUE;
     889                 : 
     890                 : /* -------------------------------------------------------------------- */
     891                 : /*      Search through root node and it's decendents.                   */
     892                 : /* -------------------------------------------------------------------- */
     893              10 :     if( !SHPSearchDiskTreeNode( fp, padfBoundsMin, padfBoundsMax, 
     894                 :                                 &panResultBuffer, &nBufferMax, 
     895                 :                                 pnShapeCount, bNeedSwap ) )
     896                 :     {
     897               0 :         if( panResultBuffer != NULL )
     898               0 :             free( panResultBuffer );
     899               0 :         *pnShapeCount = 0;
     900               0 :         return NULL;
     901                 :     }
     902                 : /* -------------------------------------------------------------------- */
     903                 : /*      Sort the id array                                               */
     904                 : /* -------------------------------------------------------------------- */
     905              10 :     qsort(panResultBuffer, *pnShapeCount, sizeof(int), compare_ints);
     906                 :     
     907              10 :     return panResultBuffer;
     908                 : }
     909                 : 
     910                 : /************************************************************************/
     911                 : /*                        SHPGetSubNodeOffset()                         */
     912                 : /*                                                                      */
     913                 : /*      Determine how big all the subnodes of this node (and their      */
     914                 : /*      children) will be.  This will allow disk based searchers to     */
     915                 : /*      seek past them all efficiently.                                 */
     916                 : /************************************************************************/
     917                 : 
     918          696276 : static int SHPGetSubNodeOffset( SHPTreeNode *node) 
     919                 : {
     920                 :     int i;
     921          696276 :     long offset=0;
     922                 : 
     923         1323134 :     for(i=0; i<node->nSubNodes; i++ ) 
     924                 :     {
     925          626858 :         if(node->apsSubNode[i]) 
     926                 :         {
     927          626858 :             offset += 4*sizeof(double) 
     928          626858 :                 + (node->apsSubNode[i]->nShapeCount+3)*sizeof(int);
     929          626858 :             offset += SHPGetSubNodeOffset(node->apsSubNode[i]);
     930                 :         }
     931                 :     }
     932                 : 
     933          696276 :     return(offset);
     934                 : }
     935                 : 
     936                 : /************************************************************************/
     937                 : /*                          SHPWriteTreeNode()                          */
     938                 : /************************************************************************/
     939                 : 
     940           69418 : static void SHPWriteTreeNode( FILE *fp, SHPTreeNode *node) 
     941                 : {
     942                 :     int i,j;
     943                 :     int offset;
     944           69418 :     unsigned char *pabyRec = NULL;
     945           69418 :   assert( NULL != node );
     946                 : 
     947           69418 :     offset = SHPGetSubNodeOffset(node);
     948                 :   
     949           69418 :     pabyRec = (unsigned char *) 
     950                 :         malloc(sizeof(double) * 4
     951                 :                + (3 * sizeof(int)) + (node->nShapeCount * sizeof(int)) );
     952           69418 :   if( NULL == pabyRec )
     953                 :   {
     954                 : #ifdef USE_CPL
     955               0 :     CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure");
     956                 : #endif
     957               0 :     assert( 0 );
     958                 :   }
     959           69418 :   assert( NULL != pabyRec );
     960                 : 
     961           69418 :     memcpy( pabyRec, &offset, 4);
     962                 : 
     963                 :     /* minx, miny, maxx, maxy */
     964           69418 :     memcpy( pabyRec+ 4, node->adfBoundsMin+0, sizeof(double) );
     965           69418 :     memcpy( pabyRec+12, node->adfBoundsMin+1, sizeof(double) );
     966           69418 :     memcpy( pabyRec+20, node->adfBoundsMax+0, sizeof(double) );
     967           69418 :     memcpy( pabyRec+28, node->adfBoundsMax+1, sizeof(double) );
     968                 : 
     969           69418 :     memcpy( pabyRec+36, &node->nShapeCount, 4);
     970           69418 :     j = node->nShapeCount * sizeof(int);
     971           69418 :     memcpy( pabyRec+40, node->panShapeIds, j);
     972           69418 :     memcpy( pabyRec+j+40, &node->nSubNodes, 4);
     973                 : 
     974           69418 :     fwrite( pabyRec, 44+j, 1, fp );
     975           69418 :     free (pabyRec);
     976                 :   
     977          138833 :     for(i=0; i<node->nSubNodes; i++ ) 
     978                 :     {
     979           69415 :         if(node->apsSubNode[i])
     980           69415 :             SHPWriteTreeNode( fp, node->apsSubNode[i]);
     981                 :     }
     982           69418 : }
     983                 : 
     984                 : /************************************************************************/
     985                 : /*                            SHPWriteTree()                            */
     986                 : /************************************************************************/
     987                 : 
     988               3 : int SHPWriteTree(SHPTree *tree, const char *filename )
     989                 : {
     990               3 :     char    signature[4] = "SQT";
     991                 :     int           i;
     992                 :     char    abyBuf[32];
     993                 :     FILE                *fp;
     994                 :   
     995                 : /* -------------------------------------------------------------------- */
     996                 : /*      Open the output file.                                           */
     997                 : /* -------------------------------------------------------------------- */
     998               3 :     fp = fopen(filename, "wb");
     999               3 :     if( fp == NULL ) 
    1000                 :     {
    1001               0 :         return FALSE;
    1002                 :     }
    1003                 : 
    1004                 : /* -------------------------------------------------------------------- */
    1005                 : /*  Establish the byte order on this machine.             */
    1006                 : /* -------------------------------------------------------------------- */
    1007               3 :     i = 1;
    1008               3 :     if( *((unsigned char *) &i) == 1 )
    1009               3 :         bBigEndian = FALSE;
    1010                 :     else
    1011               0 :         bBigEndian = TRUE;
    1012                 :   
    1013                 : /* -------------------------------------------------------------------- */
    1014                 : /*      Write the header.                                               */
    1015                 : /* -------------------------------------------------------------------- */
    1016               3 :     memcpy( abyBuf+0, signature, 3 );
    1017                 :     
    1018               3 :     if( bBigEndian )
    1019               0 :         abyBuf[3] = 2; /* New MSB */
    1020                 :     else
    1021               3 :         abyBuf[3] = 1; /* New LSB */
    1022                 : 
    1023               3 :     abyBuf[4] = 1; /* version */
    1024               3 :     abyBuf[5] = 0; /* next 3 reserved */
    1025               3 :     abyBuf[6] = 0;
    1026               3 :     abyBuf[7] = 0;
    1027                 : 
    1028               3 :     fwrite( abyBuf, 8, 1, fp );
    1029                 : 
    1030               3 :     fwrite( &(tree->nTotalCount), 4, 1, fp );
    1031                 : 
    1032                 :     /* write maxdepth */
    1033                 : 
    1034               3 :     fwrite( &(tree->nMaxDepth), 4, 1, fp );
    1035                 : 
    1036                 : /* -------------------------------------------------------------------- */
    1037                 : /*      Write all the nodes "in order".                                 */
    1038                 : /* -------------------------------------------------------------------- */
    1039                 : 
    1040               3 :     SHPWriteTreeNode( fp, tree->psRoot );  
    1041                 :     
    1042               3 :     fclose( fp );
    1043                 : 
    1044               3 :     return TRUE;
    1045                 : }

Generated by: LCOV version 1.7