shp 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +34 -0
- data/.travis.yml +4 -0
- data/Gemfile +3 -0
- data/LICENSE +28 -0
- data/README.md +30 -0
- data/Rakefile +23 -0
- data/ext/shp/base.hpp +113 -0
- data/ext/shp/dbf.cpp +381 -0
- data/ext/shp/dbf.hpp +44 -0
- data/ext/shp/extconf.rb +13 -0
- data/ext/shp/shape_object.cpp +58 -0
- data/ext/shp/shape_object.hpp +27 -0
- data/ext/shp/shapefile.cpp +299 -0
- data/ext/shp/shapefile.hpp +35 -0
- data/ext/shp/shapelib/.cvsignore +15 -0
- data/ext/shp/shapelib/ChangeLog +450 -0
- data/ext/shp/shapelib/HOWTO-RELEASE +16 -0
- data/ext/shp/shapelib/LICENSE.LGPL +483 -0
- data/ext/shp/shapelib/Makefile +113 -0
- data/ext/shp/shapelib/README +41 -0
- data/ext/shp/shapelib/README.tree +172 -0
- data/ext/shp/shapelib/contrib/.cvsignore +12 -0
- data/ext/shp/shapelib/contrib/Makefile +66 -0
- data/ext/shp/shapelib/contrib/ShapeFileII.pas +234 -0
- data/ext/shp/shapelib/contrib/Shape_PointInPoly.cpp +238 -0
- data/ext/shp/shapelib/contrib/Shape_PointInPoly_README.txt +59 -0
- data/ext/shp/shapelib/contrib/csv2shp.c +558 -0
- data/ext/shp/shapelib/contrib/dbfcat.c +166 -0
- data/ext/shp/shapelib/contrib/dbfinfo.c +106 -0
- data/ext/shp/shapelib/contrib/makefile.vc +34 -0
- data/ext/shp/shapelib/contrib/my_nan.h +46 -0
- data/ext/shp/shapelib/contrib/shpcat.c +100 -0
- data/ext/shp/shapelib/contrib/shpcentrd.c +159 -0
- data/ext/shp/shapelib/contrib/shpdata.c +129 -0
- data/ext/shp/shapelib/contrib/shpdxf.c +340 -0
- data/ext/shp/shapelib/contrib/shpfix.c +110 -0
- data/ext/shp/shapelib/contrib/shpgeo.c +1595 -0
- data/ext/shp/shapelib/contrib/shpgeo.h +154 -0
- data/ext/shp/shapelib/contrib/shpinfo.c +113 -0
- data/ext/shp/shapelib/contrib/shpproj.c +260 -0
- data/ext/shp/shapelib/contrib/shpsort.c +605 -0
- data/ext/shp/shapelib/contrib/shpsort.txt +44 -0
- data/ext/shp/shapelib/contrib/shpwkb.c +123 -0
- data/ext/shp/shapelib/contrib/tests/shpproj.sh +38 -0
- data/ext/shp/shapelib/dbfopen.c +2221 -0
- data/ext/shp/shapelib/makefile.vc +86 -0
- data/ext/shp/shapelib/makeshape.sh +21 -0
- data/ext/shp/shapelib/mkdist.sh +37 -0
- data/ext/shp/shapelib/mkinstalldirs +38 -0
- data/ext/shp/shapelib/mkrelease.sh +55 -0
- data/ext/shp/shapelib/safileio.c +286 -0
- data/ext/shp/shapelib/shapefil.h +647 -0
- data/ext/shp/shapelib/shapelib.def +46 -0
- data/ext/shp/shapelib/shpopen.c +2388 -0
- data/ext/shp/shapelib/shptree.c +1187 -0
- data/ext/shp/shapelib/shputils.c +1072 -0
- data/ext/shp/shapelib/stream1.out +1465 -0
- data/ext/shp/shapelib/stream1.sh +28 -0
- data/ext/shp/shapelib/stream2.out +530 -0
- data/ext/shp/shapelib/stream2.sh +11 -0
- data/ext/shp/shapelib/stream3.out +37 -0
- data/ext/shp/shapelib/web/.cvsignore +2 -0
- data/ext/shp/shapelib/web/codepage.html +403 -0
- data/ext/shp/shapelib/web/dbf_api.html +436 -0
- data/ext/shp/shapelib/web/index.html +235 -0
- data/ext/shp/shapelib/web/license.html +78 -0
- data/ext/shp/shapelib/web/manifest.html +87 -0
- data/ext/shp/shapelib/web/release.html +80 -0
- data/ext/shp/shapelib/web/shapelib-tools.html +352 -0
- data/ext/shp/shapelib/web/shp_api.html +376 -0
- data/ext/shp/shp.cpp +19 -0
- data/ext/shp/shp.hpp +47 -0
- data/lib/shp.rb +35 -0
- data/lib/shp/version.rb +3 -0
- data/shp.gemspec +23 -0
- data/spec/shp_spec.rb +127 -0
- 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
|
+
}
|