shapes 0.1

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