shp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +34 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +28 -0
  6. data/README.md +30 -0
  7. data/Rakefile +23 -0
  8. data/ext/shp/base.hpp +113 -0
  9. data/ext/shp/dbf.cpp +381 -0
  10. data/ext/shp/dbf.hpp +44 -0
  11. data/ext/shp/extconf.rb +13 -0
  12. data/ext/shp/shape_object.cpp +58 -0
  13. data/ext/shp/shape_object.hpp +27 -0
  14. data/ext/shp/shapefile.cpp +299 -0
  15. data/ext/shp/shapefile.hpp +35 -0
  16. data/ext/shp/shapelib/.cvsignore +15 -0
  17. data/ext/shp/shapelib/ChangeLog +450 -0
  18. data/ext/shp/shapelib/HOWTO-RELEASE +16 -0
  19. data/ext/shp/shapelib/LICENSE.LGPL +483 -0
  20. data/ext/shp/shapelib/Makefile +113 -0
  21. data/ext/shp/shapelib/README +41 -0
  22. data/ext/shp/shapelib/README.tree +172 -0
  23. data/ext/shp/shapelib/contrib/.cvsignore +12 -0
  24. data/ext/shp/shapelib/contrib/Makefile +66 -0
  25. data/ext/shp/shapelib/contrib/ShapeFileII.pas +234 -0
  26. data/ext/shp/shapelib/contrib/Shape_PointInPoly.cpp +238 -0
  27. data/ext/shp/shapelib/contrib/Shape_PointInPoly_README.txt +59 -0
  28. data/ext/shp/shapelib/contrib/csv2shp.c +558 -0
  29. data/ext/shp/shapelib/contrib/dbfcat.c +166 -0
  30. data/ext/shp/shapelib/contrib/dbfinfo.c +106 -0
  31. data/ext/shp/shapelib/contrib/makefile.vc +34 -0
  32. data/ext/shp/shapelib/contrib/my_nan.h +46 -0
  33. data/ext/shp/shapelib/contrib/shpcat.c +100 -0
  34. data/ext/shp/shapelib/contrib/shpcentrd.c +159 -0
  35. data/ext/shp/shapelib/contrib/shpdata.c +129 -0
  36. data/ext/shp/shapelib/contrib/shpdxf.c +340 -0
  37. data/ext/shp/shapelib/contrib/shpfix.c +110 -0
  38. data/ext/shp/shapelib/contrib/shpgeo.c +1595 -0
  39. data/ext/shp/shapelib/contrib/shpgeo.h +154 -0
  40. data/ext/shp/shapelib/contrib/shpinfo.c +113 -0
  41. data/ext/shp/shapelib/contrib/shpproj.c +260 -0
  42. data/ext/shp/shapelib/contrib/shpsort.c +605 -0
  43. data/ext/shp/shapelib/contrib/shpsort.txt +44 -0
  44. data/ext/shp/shapelib/contrib/shpwkb.c +123 -0
  45. data/ext/shp/shapelib/contrib/tests/shpproj.sh +38 -0
  46. data/ext/shp/shapelib/dbfopen.c +2221 -0
  47. data/ext/shp/shapelib/makefile.vc +86 -0
  48. data/ext/shp/shapelib/makeshape.sh +21 -0
  49. data/ext/shp/shapelib/mkdist.sh +37 -0
  50. data/ext/shp/shapelib/mkinstalldirs +38 -0
  51. data/ext/shp/shapelib/mkrelease.sh +55 -0
  52. data/ext/shp/shapelib/safileio.c +286 -0
  53. data/ext/shp/shapelib/shapefil.h +647 -0
  54. data/ext/shp/shapelib/shapelib.def +46 -0
  55. data/ext/shp/shapelib/shpopen.c +2388 -0
  56. data/ext/shp/shapelib/shptree.c +1187 -0
  57. data/ext/shp/shapelib/shputils.c +1072 -0
  58. data/ext/shp/shapelib/stream1.out +1465 -0
  59. data/ext/shp/shapelib/stream1.sh +28 -0
  60. data/ext/shp/shapelib/stream2.out +530 -0
  61. data/ext/shp/shapelib/stream2.sh +11 -0
  62. data/ext/shp/shapelib/stream3.out +37 -0
  63. data/ext/shp/shapelib/web/.cvsignore +2 -0
  64. data/ext/shp/shapelib/web/codepage.html +403 -0
  65. data/ext/shp/shapelib/web/dbf_api.html +436 -0
  66. data/ext/shp/shapelib/web/index.html +235 -0
  67. data/ext/shp/shapelib/web/license.html +78 -0
  68. data/ext/shp/shapelib/web/manifest.html +87 -0
  69. data/ext/shp/shapelib/web/release.html +80 -0
  70. data/ext/shp/shapelib/web/shapelib-tools.html +352 -0
  71. data/ext/shp/shapelib/web/shp_api.html +376 -0
  72. data/ext/shp/shp.cpp +19 -0
  73. data/ext/shp/shp.hpp +47 -0
  74. data/lib/shp.rb +35 -0
  75. data/lib/shp/version.rb +3 -0
  76. data/shp.gemspec +23 -0
  77. data/spec/shp_spec.rb +127 -0
  78. metadata +176 -0
@@ -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
+ }