/src/MapServer/src/maptree.c
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: .qix spatial index implementation. Derived from shapelib, and |
6 | | * relicensed with permission of Frank Warmerdam (shapelib author). |
7 | | * Author: Steve Lime |
8 | | * |
9 | | ****************************************************************************** |
10 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
11 | | * |
12 | | * Permission is hereby granted, free of charge, to any person obtaining a |
13 | | * copy of this software and associated documentation files (the "Software"), |
14 | | * to deal in the Software without restriction, including without limitation |
15 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
16 | | * and/or sell copies of the Software, and to permit persons to whom the |
17 | | * Software is furnished to do so, subject to the following conditions: |
18 | | * |
19 | | * The above copyright notice and this permission notice shall be included in |
20 | | * all copies of this Software or works derived from this Software. |
21 | | * |
22 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
23 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
25 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
27 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
28 | | * DEALINGS IN THE SOFTWARE. |
29 | | ****************************************************************************/ |
30 | | |
31 | | #include "mapserver.h" |
32 | | #include "maptree.h" |
33 | | |
34 | | #include <limits.h> |
35 | | |
36 | | #ifdef __BYTE_ORDER__ |
37 | | /* GCC/clang predefined macro */ |
38 | 0 | #define bBigEndian (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) |
39 | | #elif defined(_MSC_VER) |
40 | | /* MSVC doesn't support the C99 trick below, but all Microsoft |
41 | | platforms are little-endian */ |
42 | | #define bBigEndian false |
43 | | #else |
44 | | /* generic check */ |
45 | | #define bBigEndian \ |
46 | | (((union { \ |
47 | | int in; \ |
48 | | char out; \ |
49 | | }){1}) \ |
50 | | .out) |
51 | | #endif |
52 | | |
53 | | /* -------------------------------------------------------------------- */ |
54 | | /* If the following is 0.5, nodes will be split in half. If it */ |
55 | | /* is 0.6 then each subnode will contain 60% of the parent */ |
56 | | /* node, with 20% representing overlap. This can be help to */ |
57 | | /* prevent small objects on a boundary from shifting too high */ |
58 | | /* up the tree. */ |
59 | | /* -------------------------------------------------------------------- */ |
60 | 0 | #define SPLITRATIO 0.55 |
61 | | |
62 | | static int treeAddShapeId(treeObj *tree, int id, rectObj rect); |
63 | | |
64 | 0 | static void SwapWord(int length, void *wordP) { |
65 | 0 | int i; |
66 | 0 | uchar temp; |
67 | |
|
68 | 0 | for (i = 0; i < length / 2; i++) { |
69 | 0 | temp = ((uchar *)wordP)[i]; |
70 | 0 | ((uchar *)wordP)[i] = ((uchar *)wordP)[length - i - 1]; |
71 | 0 | ((uchar *)wordP)[length - i - 1] = temp; |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | static void *SfRealloc(void *pMem, int nNewSize) |
76 | | |
77 | 0 | { |
78 | 0 | if (pMem == NULL) |
79 | 0 | return ((void *)malloc(nNewSize)); |
80 | 0 | else |
81 | 0 | return ((void *)realloc(pMem, nNewSize)); |
82 | 0 | } |
83 | | |
84 | 0 | static treeNodeObj *treeNodeCreate(rectObj rect) { |
85 | 0 | treeNodeObj *node; |
86 | |
|
87 | 0 | node = (treeNodeObj *)msSmallMalloc(sizeof(treeNodeObj)); |
88 | |
|
89 | 0 | node->numshapes = 0; |
90 | 0 | node->ids = NULL; |
91 | |
|
92 | 0 | node->numsubnodes = 0; |
93 | |
|
94 | 0 | memcpy(&(node->rect), &(rect), sizeof(rectObj)); |
95 | |
|
96 | 0 | return node; |
97 | 0 | } |
98 | | |
99 | 0 | SHPTreeHandle msSHPDiskTreeOpen(const char *pszTree, int debug) { |
100 | 0 | char *pszFullname, *pszBasename; |
101 | 0 | SHPTreeHandle psTree; |
102 | |
|
103 | 0 | char pabyBuf[16]; |
104 | 0 | int i; |
105 | | |
106 | | /* -------------------------------------------------------------------- */ |
107 | | /* Initialize the info structure. */ |
108 | | /* -------------------------------------------------------------------- */ |
109 | 0 | psTree = (SHPTreeHandle)msSmallMalloc(sizeof(SHPTreeInfo)); |
110 | | |
111 | | /* -------------------------------------------------------------------- */ |
112 | | /* Compute the base (layer) name. If there is any extension */ |
113 | | /* on the passed in filename we will strip it off. */ |
114 | | /* -------------------------------------------------------------------- */ |
115 | 0 | pszBasename = (char *)msSmallMalloc(strlen(pszTree) + 5); |
116 | 0 | strcpy(pszBasename, pszTree); |
117 | 0 | for (i = strlen(pszBasename) - 1; |
118 | 0 | i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && |
119 | 0 | pszBasename[i] != '\\'; |
120 | 0 | i--) { |
121 | 0 | } |
122 | |
|
123 | 0 | if (pszBasename[i] == '.') |
124 | 0 | pszBasename[i] = '\0'; |
125 | | |
126 | | /* -------------------------------------------------------------------- */ |
127 | | /* Open the .shp and .shx files. Note that files pulled from */ |
128 | | /* a PC to Unix with upper case filenames won't work! */ |
129 | | /* -------------------------------------------------------------------- */ |
130 | 0 | pszFullname = (char *)msSmallMalloc(strlen(pszBasename) + 5); |
131 | 0 | sprintf(pszFullname, "%s%s", pszBasename, MS_INDEX_EXTENSION); |
132 | 0 | psTree->fp = fopen(pszFullname, "rb"); |
133 | 0 | if (psTree->fp == NULL) { |
134 | 0 | sprintf(pszFullname, "%s.QIX", pszBasename); |
135 | 0 | psTree->fp = fopen(pszFullname, "rb"); |
136 | 0 | } |
137 | |
|
138 | 0 | msFree(pszBasename); /* don't need these any more */ |
139 | 0 | msFree(pszFullname); |
140 | |
|
141 | 0 | if (psTree->fp == NULL) { |
142 | 0 | msFree(psTree); |
143 | 0 | return (NULL); |
144 | 0 | } |
145 | | |
146 | 0 | if (fread(pabyBuf, 8, 1, psTree->fp) != 1) { |
147 | 0 | msFree(psTree); |
148 | 0 | return (NULL); |
149 | 0 | } |
150 | | |
151 | 0 | memcpy(&psTree->signature, pabyBuf, 3); |
152 | 0 | if (strncmp(psTree->signature, "SQT", 3)) { |
153 | | /* ---------------------------------------------------------------------- */ |
154 | | /* must check if the 2 first bytes equal 0 of max depth that cannot */ |
155 | | /* be more than 65535. If yes, we must swap all value. The problem */ |
156 | | /* here is if there's no Depth (bytea 5,6,7,8 in the file) all bytes */ |
157 | | /* will be set to 0. So,we will test with the number of shapes (bytes */ |
158 | | /* 1,2,3,4) that cannot be more than 65535 too. */ |
159 | | /* ---------------------------------------------------------------------- */ |
160 | 0 | if (debug) { |
161 | 0 | msDebug("WARNING in msSHPDiskTreeOpen(): %s is in old index format " |
162 | 0 | "which has been deprecated. It is strongly recommended to " |
163 | 0 | "regenerate it in new format.\n", |
164 | 0 | pszTree); |
165 | 0 | } |
166 | 0 | if ((pabyBuf[4] == 0 && pabyBuf[5] == 0 && pabyBuf[6] == 0 && |
167 | 0 | pabyBuf[7] == 0)) { |
168 | 0 | psTree->LSB_order = !(pabyBuf[0] == 0 && pabyBuf[1] == 0); |
169 | 0 | } else { |
170 | 0 | psTree->LSB_order = !(pabyBuf[4] == 0 && pabyBuf[5] == 0); |
171 | 0 | } |
172 | 0 | psTree->needswap = ((psTree->LSB_order) != (!bBigEndian)); |
173 | | |
174 | | /* ---------------------------------------------------------------------- */ |
175 | | /* poor hack to see if this quadtree was created by a computer with a */ |
176 | | /* different Endian */ |
177 | | /* ---------------------------------------------------------------------- */ |
178 | 0 | psTree->version = 0; |
179 | 0 | } else { |
180 | 0 | psTree->needswap = ((pabyBuf[3] == MS_NEW_MSB_ORDER) ^ (bBigEndian)); |
181 | |
|
182 | 0 | psTree->LSB_order = (pabyBuf[3] == MS_NEW_LSB_ORDER); |
183 | 0 | memcpy(&psTree->version, pabyBuf + 4, 1); |
184 | 0 | memcpy(&psTree->flags, pabyBuf + 5, 3); |
185 | |
|
186 | 0 | if (fread(pabyBuf, 8, 1, psTree->fp) != 1) { |
187 | 0 | msFree(psTree); |
188 | 0 | return (NULL); |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | 0 | if (psTree->needswap) |
193 | 0 | SwapWord(4, pabyBuf); |
194 | 0 | memcpy(&psTree->nShapes, pabyBuf, 4); |
195 | |
|
196 | 0 | if (psTree->needswap) |
197 | 0 | SwapWord(4, pabyBuf + 4); |
198 | 0 | memcpy(&psTree->nDepth, pabyBuf + 4, 4); |
199 | |
|
200 | 0 | return (psTree); |
201 | 0 | } |
202 | | |
203 | 0 | void msSHPDiskTreeClose(SHPTreeHandle disktree) { |
204 | 0 | fclose(disktree->fp); |
205 | 0 | free(disktree); |
206 | 0 | } |
207 | | |
208 | 0 | treeObj *msCreateTree(shapefileObj *shapefile, int maxdepth) { |
209 | 0 | int i; |
210 | 0 | treeObj *tree; |
211 | 0 | rectObj bounds; |
212 | |
|
213 | 0 | if (!shapefile) |
214 | 0 | return NULL; |
215 | | |
216 | | /* -------------------------------------------------------------------- */ |
217 | | /* Allocate the tree object */ |
218 | | /* -------------------------------------------------------------------- */ |
219 | 0 | tree = (treeObj *)msSmallMalloc(sizeof(treeObj)); |
220 | |
|
221 | 0 | tree->numshapes = shapefile->numshapes; |
222 | 0 | tree->maxdepth = maxdepth; |
223 | | |
224 | | /* -------------------------------------------------------------------- */ |
225 | | /* If no max depth was defined, try to select a reasonable one */ |
226 | | /* that implies approximately 8 shapes per node. */ |
227 | | /* -------------------------------------------------------------------- */ |
228 | 0 | if (tree->maxdepth == 0) { |
229 | 0 | int numnodes = 1; |
230 | |
|
231 | 0 | while (numnodes * 4 < shapefile->numshapes) { |
232 | 0 | tree->maxdepth += 1; |
233 | 0 | numnodes = numnodes * 2; |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | /* -------------------------------------------------------------------- */ |
238 | | /* Allocate the root node. */ |
239 | | /* -------------------------------------------------------------------- */ |
240 | 0 | tree->root = treeNodeCreate(shapefile->bounds); |
241 | |
|
242 | 0 | for (i = 0; i < shapefile->numshapes; i++) { |
243 | 0 | if (msSHPReadBounds(shapefile->hSHP, i, &bounds) == MS_SUCCESS) |
244 | 0 | treeAddShapeId(tree, i, bounds); |
245 | 0 | } |
246 | |
|
247 | 0 | return tree; |
248 | 0 | } |
249 | | |
250 | 0 | static void destroyTreeNode(treeNodeObj *node) { |
251 | 0 | int i; |
252 | |
|
253 | 0 | for (i = 0; i < node->numsubnodes; i++) { |
254 | 0 | if (node->subnode[i]) |
255 | 0 | destroyTreeNode(node->subnode[i]); |
256 | 0 | } |
257 | |
|
258 | 0 | if (node->ids) |
259 | 0 | free(node->ids); |
260 | |
|
261 | 0 | free(node); |
262 | 0 | } |
263 | | |
264 | 0 | void msDestroyTree(treeObj *tree) { |
265 | 0 | destroyTreeNode(tree->root); |
266 | 0 | free(tree); |
267 | 0 | } |
268 | | |
269 | 0 | static void treeSplitBounds(rectObj *in, rectObj *out1, rectObj *out2) { |
270 | 0 | double range; |
271 | | |
272 | | /* -------------------------------------------------------------------- */ |
273 | | /* The output bounds will be very similar to the input bounds, */ |
274 | | /* so just copy over to start. */ |
275 | | /* -------------------------------------------------------------------- */ |
276 | 0 | memcpy(out1, in, sizeof(rectObj)); |
277 | 0 | memcpy(out2, in, sizeof(rectObj)); |
278 | | |
279 | | /* -------------------------------------------------------------------- */ |
280 | | /* Split in X direction. */ |
281 | | /* -------------------------------------------------------------------- */ |
282 | 0 | if ((in->maxx - in->minx) > (in->maxy - in->miny)) { |
283 | 0 | range = in->maxx - in->minx; |
284 | |
|
285 | 0 | out1->maxx = in->minx + range * SPLITRATIO; |
286 | 0 | out2->minx = in->maxx - range * SPLITRATIO; |
287 | 0 | } |
288 | | |
289 | | /* -------------------------------------------------------------------- */ |
290 | | /* Otherwise split in Y direction. */ |
291 | | /* -------------------------------------------------------------------- */ |
292 | 0 | else { |
293 | 0 | range = in->maxy - in->miny; |
294 | |
|
295 | 0 | out1->maxy = in->miny + range * SPLITRATIO; |
296 | 0 | out2->miny = in->maxy - range * SPLITRATIO; |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | static int treeNodeAddShapeId(treeNodeObj *node, int id, rectObj rect, |
301 | 0 | int maxdepth) { |
302 | 0 | int i; |
303 | | |
304 | | /* -------------------------------------------------------------------- */ |
305 | | /* If there are subnodes, then consider whether this object */ |
306 | | /* will fit in them. */ |
307 | | /* -------------------------------------------------------------------- */ |
308 | 0 | if (maxdepth > 1 && node->numsubnodes > 0) { |
309 | 0 | for (i = 0; i < node->numsubnodes; i++) { |
310 | 0 | if (msRectContained(&rect, &node->subnode[i]->rect)) { |
311 | 0 | return treeNodeAddShapeId(node->subnode[i], id, rect, maxdepth - 1); |
312 | 0 | } |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | | /* -------------------------------------------------------------------- */ |
317 | | /* Otherwise, consider creating four subnodes if could fit into */ |
318 | | /* them, and adding to the appropriate subnode. */ |
319 | | /* -------------------------------------------------------------------- */ |
320 | 0 | #if MAX_SUBNODES == 4 |
321 | 0 | else if (maxdepth > 1 && node->numsubnodes == 0) { |
322 | 0 | rectObj half1, half2, quad1, quad2, quad3, quad4; |
323 | |
|
324 | 0 | treeSplitBounds(&node->rect, &half1, &half2); |
325 | 0 | treeSplitBounds(&half1, &quad1, &quad2); |
326 | 0 | treeSplitBounds(&half2, &quad3, &quad4); |
327 | |
|
328 | 0 | if (msRectContained(&rect, &quad1) || msRectContained(&rect, &quad2) || |
329 | 0 | msRectContained(&rect, &quad3) || msRectContained(&rect, &quad4)) { |
330 | 0 | node->numsubnodes = 4; |
331 | 0 | node->subnode[0] = treeNodeCreate(quad1); |
332 | 0 | node->subnode[1] = treeNodeCreate(quad2); |
333 | 0 | node->subnode[2] = treeNodeCreate(quad3); |
334 | 0 | node->subnode[3] = treeNodeCreate(quad4); |
335 | | |
336 | | /* recurse back on this node now that it has subnodes */ |
337 | 0 | return (treeNodeAddShapeId(node, id, rect, maxdepth)); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | #endif |
341 | | |
342 | | /* -------------------------------------------------------------------- */ |
343 | | /* Otherwise, consider creating two subnodes if could fit into */ |
344 | | /* them, and adding to the appropriate subnode. */ |
345 | | /* -------------------------------------------------------------------- */ |
346 | | #if MAX_SUBNODE == 2 |
347 | | else if (maxdepth > 1 && node->numsubnodes == 0) { |
348 | | rectObj half1, half2; |
349 | | |
350 | | treeSplitBounds(&node->rect, &half1, &half2); |
351 | | |
352 | | if (msRectContained(&rect, &half1)) { |
353 | | node->numsubnodes = 2; |
354 | | node->subnode[0] = treeNodeCreate(half1); |
355 | | node->subnode[1] = treeNodeCreate(half2); |
356 | | |
357 | | return (treeNodeAddShapeId(node->subnode[0], id, rect, maxdepth - 1)); |
358 | | } else if (msRectContained(&rect, &half2)) { |
359 | | node->numsubnodes = 2; |
360 | | node->subnode[0] = treeNodeCreate(&half1); |
361 | | node->subnode[1] = treeNodeCreate(&half2); |
362 | | |
363 | | return (treeNodeAddShapeId(node->subnode[1], id, rect, maxdepth - 1)); |
364 | | } |
365 | | } |
366 | | #endif /* MAX_SUBNODE == 2 */ |
367 | | |
368 | | /* -------------------------------------------------------------------- */ |
369 | | /* If none of that worked, just add it to this nodes list. */ |
370 | | /* -------------------------------------------------------------------- */ |
371 | 0 | node->numshapes++; |
372 | |
|
373 | 0 | node->ids = SfRealloc(node->ids, sizeof(ms_int32) * node->numshapes); |
374 | 0 | node->ids[node->numshapes - 1] = id; |
375 | |
|
376 | 0 | return MS_TRUE; |
377 | 0 | } |
378 | | |
379 | 0 | static int treeAddShapeId(treeObj *tree, int id, rectObj rect) { |
380 | 0 | return (treeNodeAddShapeId(tree->root, id, rect, tree->maxdepth)); |
381 | 0 | } |
382 | | |
383 | | static void treeCollectShapeIds(treeNodeObj *node, rectObj aoi, int numshapes, |
384 | 0 | ms_bitarray status) { |
385 | 0 | int i; |
386 | | |
387 | | /* -------------------------------------------------------------------- */ |
388 | | /* Does this node overlap the area of interest at all? If not, */ |
389 | | /* return without adding to the list at all. */ |
390 | | /* -------------------------------------------------------------------- */ |
391 | 0 | if (!msRectOverlap(&node->rect, &aoi)) |
392 | 0 | return; |
393 | | |
394 | | /* -------------------------------------------------------------------- */ |
395 | | /* Add the local nodes shapeids to the list. */ |
396 | | /* -------------------------------------------------------------------- */ |
397 | 0 | for (i = 0; i < node->numshapes; i++) |
398 | 0 | if (node->ids[i] >= 0 && node->ids[i] < numshapes) |
399 | 0 | msSetBit(status, node->ids[i], 1); |
400 | | |
401 | | /* -------------------------------------------------------------------- */ |
402 | | /* Recurse to subnodes if they exist. */ |
403 | | /* -------------------------------------------------------------------- */ |
404 | 0 | for (i = 0; i < node->numsubnodes; i++) { |
405 | 0 | if (node->subnode[i]) |
406 | 0 | treeCollectShapeIds(node->subnode[i], aoi, numshapes, status); |
407 | 0 | } |
408 | 0 | } |
409 | | |
410 | 0 | ms_bitarray msSearchTree(const treeObj *tree, rectObj aoi) { |
411 | 0 | ms_bitarray status = NULL; |
412 | |
|
413 | 0 | status = msAllocBitArray(tree->numshapes); |
414 | 0 | if (!status) { |
415 | 0 | msSetError(MS_MEMERR, NULL, "msSearchTree()"); |
416 | 0 | return (NULL); |
417 | 0 | } |
418 | | |
419 | 0 | treeCollectShapeIds(tree->root, aoi, tree->numshapes, status); |
420 | |
|
421 | 0 | return (status); |
422 | 0 | } |
423 | | |
424 | 0 | static int treeNodeTrim(treeNodeObj *node) { |
425 | 0 | int i; |
426 | | |
427 | | /* -------------------------------------------------------------------- */ |
428 | | /* Trim subtrees, and free subnodes that come back empty. */ |
429 | | /* -------------------------------------------------------------------- */ |
430 | 0 | for (i = 0; i < node->numsubnodes; i++) { |
431 | 0 | if (treeNodeTrim(node->subnode[i])) { |
432 | 0 | destroyTreeNode(node->subnode[i]); |
433 | 0 | node->subnode[i] = node->subnode[node->numsubnodes - 1]; |
434 | 0 | node->numsubnodes--; |
435 | 0 | i--; /* process the new occupant of this subnode entry */ |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | | /* -------------------------------------------------------------------- */ |
440 | | /* If the current node has 1 subnode and no shapes, promote that */ |
441 | | /* subnode to the current node position. */ |
442 | | /* -------------------------------------------------------------------- */ |
443 | 0 | if (node->numsubnodes == 1 && node->numshapes == 0) { |
444 | 0 | treeNodeObj *psSubNode = node->subnode[0]; |
445 | |
|
446 | 0 | memcpy(&node->rect, &psSubNode->rect, sizeof(psSubNode->rect)); |
447 | 0 | node->numshapes = psSubNode->numshapes; |
448 | 0 | assert(node->ids == NULL); |
449 | 0 | node->ids = psSubNode->ids; |
450 | 0 | node->numsubnodes = psSubNode->numsubnodes; |
451 | 0 | for (i = 0; i < psSubNode->numsubnodes; i++) |
452 | 0 | node->subnode[i] = psSubNode->subnode[i]; |
453 | 0 | free(psSubNode); |
454 | 0 | } |
455 | | |
456 | | /* -------------------------------------------------------------------- */ |
457 | | /* We should be trimmed if we have no subnodes, and no shapes. */ |
458 | | /* -------------------------------------------------------------------- */ |
459 | | |
460 | 0 | return (node->numsubnodes == 0 && node->numshapes == 0); |
461 | 0 | } |
462 | | |
463 | 0 | void msTreeTrim(treeObj *tree) { treeNodeTrim(tree->root); } |
464 | | |
465 | | static void searchDiskTreeNode(SHPTreeHandle disktree, rectObj aoi, |
466 | 0 | ms_bitarray status) { |
467 | 0 | int i; |
468 | 0 | ms_int32 offset; |
469 | 0 | ms_int32 numshapes, numsubnodes; |
470 | 0 | rectObj rect; |
471 | |
|
472 | 0 | int *ids = NULL; |
473 | |
|
474 | 0 | if (fread(&offset, 4, 1, disktree->fp) != 1) |
475 | 0 | goto error; |
476 | 0 | if (disktree->needswap) |
477 | 0 | SwapWord(4, &offset); |
478 | |
|
479 | 0 | if (fread(&rect, sizeof(rectObj), 1, disktree->fp) != 1) |
480 | 0 | goto error; |
481 | 0 | if (disktree->needswap) |
482 | 0 | SwapWord(8, &rect.minx); |
483 | 0 | if (disktree->needswap) |
484 | 0 | SwapWord(8, &rect.miny); |
485 | 0 | if (disktree->needswap) |
486 | 0 | SwapWord(8, &rect.maxx); |
487 | 0 | if (disktree->needswap) |
488 | 0 | SwapWord(8, &rect.maxy); |
489 | |
|
490 | 0 | if (fread(&numshapes, 4, 1, disktree->fp) != 1) |
491 | 0 | goto error; |
492 | 0 | if (disktree->needswap) |
493 | 0 | SwapWord(4, &numshapes); |
494 | 0 | if (numshapes < 0 || numshapes > INT_MAX / 4) |
495 | 0 | goto error; |
496 | | |
497 | 0 | if (!msRectOverlap(&rect, &aoi)) { /* skip rest of this node and sub-nodes */ |
498 | 0 | offset += numshapes * sizeof(ms_int32) + sizeof(ms_int32); |
499 | 0 | if (fseek(disktree->fp, offset, SEEK_CUR) < 0) |
500 | 0 | goto error; |
501 | 0 | return; |
502 | 0 | } |
503 | 0 | if (numshapes > 0) { |
504 | 0 | ids = (int *)msSmallMalloc(numshapes * sizeof(ms_int32)); |
505 | |
|
506 | 0 | if (fread(ids, numshapes * sizeof(ms_int32), 1, disktree->fp) != 1) |
507 | 0 | goto error; |
508 | 0 | if (disktree->needswap) { |
509 | 0 | for (i = 0; i < numshapes; i++) { |
510 | 0 | SwapWord(4, &ids[i]); |
511 | 0 | if (ids[i] >= 0 && ids[i] < disktree->nShapes) |
512 | 0 | msSetBit(status, ids[i], 1); |
513 | 0 | } |
514 | 0 | } else { |
515 | 0 | for (i = 0; i < numshapes; i++) |
516 | 0 | if (ids[i] >= 0 && ids[i] < disktree->nShapes) |
517 | 0 | msSetBit(status, ids[i], 1); |
518 | 0 | } |
519 | 0 | free(ids); |
520 | 0 | ids = NULL; |
521 | 0 | } |
522 | | |
523 | 0 | if (fread(&numsubnodes, 4, 1, disktree->fp) != 1) |
524 | 0 | goto error; |
525 | 0 | if (disktree->needswap) |
526 | 0 | SwapWord(4, &numsubnodes); |
527 | 0 | if (numsubnodes < 0 || numsubnodes > INT_MAX / 4) |
528 | 0 | goto error; |
529 | | |
530 | 0 | for (i = 0; i < numsubnodes; i++) |
531 | 0 | searchDiskTreeNode(disktree, aoi, status); |
532 | |
|
533 | 0 | return; |
534 | | |
535 | 0 | error: |
536 | 0 | msSetError(MS_IOERR, NULL, "searchDiskTreeNode()"); |
537 | 0 | free(ids); |
538 | 0 | return; |
539 | 0 | } |
540 | | |
541 | | ms_bitarray msSearchDiskTree(const char *filename, rectObj aoi, int debug, |
542 | 0 | int numshapes) { |
543 | 0 | SHPTreeHandle disktree; |
544 | 0 | ms_bitarray status = NULL; |
545 | |
|
546 | 0 | disktree = msSHPDiskTreeOpen(filename, debug); |
547 | 0 | if (!disktree) { |
548 | | |
549 | | /* only set this error IF debugging is turned on, gets annoying otherwise */ |
550 | 0 | if (debug) |
551 | 0 | msSetError( |
552 | 0 | MS_NOTFOUND, |
553 | 0 | "Unable to open spatial index for %s. In most cases you can safely " |
554 | 0 | "ignore this message, otherwise check file names and permissions.", |
555 | 0 | "msSearchDiskTree()", filename); |
556 | |
|
557 | 0 | return (NULL); |
558 | 0 | } |
559 | | |
560 | 0 | if (disktree->nShapes != numshapes) { |
561 | 0 | msSetError(MS_SHPERR, "The spatial index file %s is corrupt.", |
562 | 0 | "msSearchDiskTree()", filename); |
563 | 0 | msSHPDiskTreeClose(disktree); |
564 | 0 | return (NULL); |
565 | 0 | } |
566 | | |
567 | 0 | status = msAllocBitArray(disktree->nShapes); |
568 | 0 | if (!status) { |
569 | 0 | msSetError(MS_MEMERR, NULL, "msSearchDiskTree()"); |
570 | 0 | msSHPDiskTreeClose(disktree); |
571 | 0 | return (NULL); |
572 | 0 | } |
573 | | |
574 | 0 | searchDiskTreeNode(disktree, aoi, status); |
575 | |
|
576 | 0 | msSHPDiskTreeClose(disktree); |
577 | 0 | return (status); |
578 | 0 | } |
579 | | |
580 | 0 | treeNodeObj *readTreeNode(SHPTreeHandle disktree) { |
581 | 0 | int i, res; |
582 | 0 | ms_int32 offset; |
583 | 0 | treeNodeObj *node; |
584 | |
|
585 | 0 | node = (treeNodeObj *)msSmallMalloc(sizeof(treeNodeObj)); |
586 | 0 | node->ids = NULL; |
587 | |
|
588 | 0 | res = fread(&offset, 4, 1, disktree->fp); |
589 | 0 | if (!res) { |
590 | 0 | free(node); |
591 | 0 | return NULL; |
592 | 0 | } |
593 | | |
594 | 0 | if (disktree->needswap) |
595 | 0 | SwapWord(4, &offset); |
596 | |
|
597 | 0 | res = fread(&node->rect, sizeof(rectObj), 1, disktree->fp); |
598 | 0 | if (!res) { |
599 | 0 | free(node); |
600 | 0 | return NULL; |
601 | 0 | } |
602 | 0 | if (disktree->needswap) |
603 | 0 | SwapWord(8, &node->rect.minx); |
604 | 0 | if (disktree->needswap) |
605 | 0 | SwapWord(8, &node->rect.miny); |
606 | 0 | if (disktree->needswap) |
607 | 0 | SwapWord(8, &node->rect.maxx); |
608 | 0 | if (disktree->needswap) |
609 | 0 | SwapWord(8, &node->rect.maxy); |
610 | |
|
611 | 0 | res = fread(&node->numshapes, 4, 1, disktree->fp); |
612 | 0 | if (!res) { |
613 | 0 | free(node); |
614 | 0 | return NULL; |
615 | 0 | } |
616 | 0 | if (disktree->needswap) |
617 | 0 | SwapWord(4, &node->numshapes); |
618 | 0 | if (node->numshapes < 0 || node->numshapes > INT_MAX / 4) { |
619 | 0 | free(node); |
620 | 0 | return NULL; |
621 | 0 | } |
622 | 0 | if (node->numshapes > 0) { |
623 | 0 | node->ids = (ms_int32 *)msSmallMalloc(sizeof(ms_int32) * node->numshapes); |
624 | 0 | res = fread(node->ids, node->numshapes * 4, 1, disktree->fp); |
625 | 0 | if (!res) { |
626 | 0 | free(node->ids); |
627 | 0 | free(node); |
628 | 0 | return NULL; |
629 | 0 | } |
630 | 0 | } |
631 | 0 | for (i = 0; i < node->numshapes; i++) { |
632 | 0 | if (disktree->needswap) |
633 | 0 | SwapWord(4, &node->ids[i]); |
634 | 0 | } |
635 | |
|
636 | 0 | res = fread(&node->numsubnodes, 4, 1, disktree->fp); |
637 | 0 | if (!res) { |
638 | 0 | free(node->ids); |
639 | 0 | free(node); |
640 | 0 | return NULL; |
641 | 0 | } |
642 | 0 | if (disktree->needswap) |
643 | 0 | SwapWord(4, &node->numsubnodes); |
644 | |
|
645 | 0 | return node; |
646 | 0 | } |
647 | | |
648 | 0 | treeObj *msReadTree(char *filename, int debug) { |
649 | 0 | treeObj *tree = NULL; |
650 | 0 | SHPTreeHandle disktree; |
651 | |
|
652 | 0 | disktree = msSHPDiskTreeOpen(filename, debug); |
653 | 0 | if (!disktree) { |
654 | 0 | msSetError(MS_IOERR, NULL, "msReadTree()"); |
655 | 0 | return (NULL); |
656 | 0 | } |
657 | | |
658 | 0 | tree = (treeObj *)malloc(sizeof(treeObj)); |
659 | 0 | MS_CHECK_ALLOC(tree, sizeof(treeObj), NULL); |
660 | |
|
661 | 0 | tree->numshapes = disktree->nShapes; |
662 | 0 | tree->maxdepth = disktree->nDepth; |
663 | |
|
664 | 0 | tree->root = readTreeNode(disktree); |
665 | |
|
666 | 0 | msSHPDiskTreeClose(disktree); |
667 | |
|
668 | 0 | return (tree); |
669 | 0 | } |
670 | | |
671 | 0 | static ms_int32 getSubNodeOffset(treeNodeObj *node) { |
672 | 0 | int i; |
673 | 0 | ms_int32 offset = 0; |
674 | |
|
675 | 0 | for (i = 0; i < node->numsubnodes; i++) { |
676 | 0 | if (node->subnode[i]) { |
677 | 0 | offset += |
678 | 0 | sizeof(rectObj) + (node->subnode[i]->numshapes + 3) * sizeof(int); |
679 | 0 | offset += getSubNodeOffset(node->subnode[i]); |
680 | 0 | } |
681 | 0 | } |
682 | | |
683 | | /* offset is the disk offset in the index file on disk */ |
684 | | /* that format is (per node) */ |
685 | | /* int offset 4 bytes */ |
686 | | /* rectObj rect 4 * 8 bytes */ |
687 | | /* int numShapes 4 bytes */ |
688 | | /* int ids[numShapes] 4 * numShapes bytes */ |
689 | | /* int numSubNodes 4 bytes */ |
690 | | /* */ |
691 | |
|
692 | 0 | return (offset); |
693 | 0 | } |
694 | | |
695 | 0 | static void writeTreeNode(SHPTreeHandle disktree, treeNodeObj *node) { |
696 | 0 | int i, j; |
697 | 0 | ms_int32 offset; |
698 | 0 | char *pabyRec = NULL; |
699 | |
|
700 | 0 | offset = getSubNodeOffset(node); |
701 | |
|
702 | 0 | pabyRec = msSmallMalloc(sizeof(rectObj) + (3 * sizeof(ms_int32)) + |
703 | 0 | (node->numshapes * sizeof(ms_int32))); |
704 | |
|
705 | 0 | memcpy(pabyRec, &offset, 4); |
706 | 0 | if (disktree->needswap) |
707 | 0 | SwapWord(4, pabyRec); |
708 | |
|
709 | 0 | memcpy(pabyRec + 4, &node->rect, sizeof(rectObj)); |
710 | 0 | for (i = 0; i < 4; i++) |
711 | 0 | if (disktree->needswap) |
712 | 0 | SwapWord(8, pabyRec + 4 + (8 * i)); |
713 | |
|
714 | 0 | memcpy(pabyRec + 36, &node->numshapes, 4); |
715 | 0 | if (disktree->needswap) |
716 | 0 | SwapWord(4, pabyRec + 36); |
717 | |
|
718 | 0 | j = node->numshapes * sizeof(ms_int32); |
719 | 0 | memcpy(pabyRec + 40, node->ids, j); |
720 | 0 | for (i = 0; i < node->numshapes; i++) |
721 | 0 | if (disktree->needswap) |
722 | 0 | SwapWord(4, pabyRec + 40 + (4 * i)); |
723 | |
|
724 | 0 | memcpy(pabyRec + j + 40, &node->numsubnodes, 4); |
725 | 0 | if (disktree->needswap) |
726 | 0 | SwapWord(4, pabyRec + 40 + j); |
727 | |
|
728 | 0 | fwrite(pabyRec, 44 + j, 1, disktree->fp); |
729 | 0 | free(pabyRec); |
730 | |
|
731 | 0 | for (i = 0; i < node->numsubnodes; i++) { |
732 | 0 | if (node->subnode[i]) |
733 | 0 | writeTreeNode(disktree, node->subnode[i]); |
734 | 0 | } |
735 | |
|
736 | 0 | return; |
737 | 0 | } |
738 | | |
739 | 0 | int msWriteTree(treeObj *tree, char *filename, int B_order) { |
740 | 0 | char signature[3] = {'S', 'Q', 'T'}; |
741 | 0 | char version = 1; |
742 | 0 | char reserved[3] = {0, 0, 0}; |
743 | 0 | SHPTreeHandle disktree; |
744 | 0 | int i; |
745 | 0 | char mtBigEndian; |
746 | 0 | char pabyBuf[32]; |
747 | 0 | char *pszBasename, *pszFullname; |
748 | |
|
749 | 0 | disktree = (SHPTreeHandle)malloc(sizeof(SHPTreeInfo)); |
750 | 0 | MS_CHECK_ALLOC(disktree, sizeof(SHPTreeInfo), MS_FALSE); |
751 | | |
752 | | /* -------------------------------------------------------------------- */ |
753 | | /* Compute the base (layer) name. If there is any extension */ |
754 | | /* on the passed in filename we will strip it off. */ |
755 | | /* -------------------------------------------------------------------- */ |
756 | 0 | pszBasename = (char *)msSmallMalloc(strlen(filename) + 5); |
757 | 0 | strcpy(pszBasename, filename); |
758 | 0 | for (i = strlen(pszBasename) - 1; |
759 | 0 | i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && |
760 | 0 | pszBasename[i] != '\\'; |
761 | 0 | i--) { |
762 | 0 | } |
763 | |
|
764 | 0 | if (pszBasename[i] == '.') |
765 | 0 | pszBasename[i] = '\0'; |
766 | | |
767 | | /* -------------------------------------------------------------------- */ |
768 | | /* Open the .shp and .shx files. Note that files pulled from */ |
769 | | /* a PC to Unix with upper case filenames won't work! */ |
770 | | /* -------------------------------------------------------------------- */ |
771 | 0 | pszFullname = (char *)msSmallMalloc(strlen(pszBasename) + 5); |
772 | 0 | sprintf(pszFullname, "%s%s", pszBasename, MS_INDEX_EXTENSION); |
773 | 0 | disktree->fp = fopen(pszFullname, "wb"); |
774 | |
|
775 | 0 | msFree(pszBasename); /* not needed */ |
776 | 0 | msFree(pszFullname); |
777 | |
|
778 | 0 | if (!disktree->fp) { |
779 | 0 | msFree(disktree); |
780 | 0 | msSetError(MS_IOERR, NULL, "msWriteTree()"); |
781 | 0 | return (MS_FALSE); |
782 | 0 | } |
783 | | |
784 | | /* for efficiency, trim the tree */ |
785 | 0 | msTreeTrim(tree); |
786 | | |
787 | | /* -------------------------------------------------------------------- */ |
788 | | /* Establish the byte order on this machine. */ |
789 | | /* -------------------------------------------------------------------- */ |
790 | 0 | i = 1; |
791 | | /* cppcheck-suppress knownConditionTrueFalse */ |
792 | 0 | if (*((uchar *)&i) == 1) |
793 | 0 | mtBigEndian = MS_FALSE; |
794 | 0 | else |
795 | 0 | mtBigEndian = MS_TRUE; |
796 | |
|
797 | 0 | if (!(mtBigEndian ^ (B_order == MS_LSB_ORDER || B_order == MS_NEW_LSB_ORDER))) |
798 | 0 | disktree->needswap = 1; |
799 | 0 | else |
800 | 0 | disktree->needswap = 0; |
801 | |
|
802 | 0 | if (B_order == MS_NATIVE_ORDER) |
803 | 0 | disktree->needswap = 0; |
804 | | |
805 | | /* write the header */ |
806 | 0 | if (B_order > 0) { |
807 | 0 | memcpy(pabyBuf, &signature, 3); |
808 | 0 | memcpy(&disktree->signature, &signature, 3); |
809 | 0 | pabyBuf[3] = B_order; |
810 | |
|
811 | 0 | memcpy(pabyBuf + 4, &version, 1); |
812 | 0 | memcpy(pabyBuf + 5, &reserved, 3); |
813 | |
|
814 | 0 | memcpy(&disktree->version, &version, 1); |
815 | 0 | memcpy(&disktree->flags, &reserved, 3); |
816 | |
|
817 | 0 | fwrite(pabyBuf, 8, 1, disktree->fp); |
818 | 0 | } |
819 | |
|
820 | 0 | memcpy(pabyBuf, &tree->numshapes, 4); |
821 | 0 | if (disktree->needswap) |
822 | 0 | SwapWord(4, pabyBuf); |
823 | |
|
824 | 0 | memcpy(pabyBuf + 4, &tree->maxdepth, 4); |
825 | 0 | if (disktree->needswap) |
826 | 0 | SwapWord(4, pabyBuf + 4); |
827 | |
|
828 | 0 | i = fwrite(pabyBuf, 8, 1, disktree->fp); |
829 | 0 | if (!i) { |
830 | 0 | fprintf(stderr, "unable to write to index file ... exiting \n"); |
831 | 0 | msSHPDiskTreeClose(disktree); |
832 | 0 | return (MS_FALSE); |
833 | 0 | } |
834 | | |
835 | 0 | writeTreeNode(disktree, tree->root); |
836 | |
|
837 | 0 | msSHPDiskTreeClose(disktree); |
838 | |
|
839 | 0 | return (MS_TRUE); |
840 | 0 | } |
841 | | |
842 | | /* Function to filter search results further against feature bboxes */ |
843 | | void msFilterTreeSearch(shapefileObj *shp, ms_bitarray status, |
844 | 0 | rectObj search_rect) { |
845 | 0 | int i; |
846 | 0 | rectObj shape_rect; |
847 | |
|
848 | 0 | i = msGetNextBit(status, 0, shp->numshapes); |
849 | 0 | while (i >= 0) { |
850 | 0 | if (msSHPReadBounds(shp->hSHP, i, &shape_rect) == MS_SUCCESS) { |
851 | 0 | if (msRectOverlap(&shape_rect, &search_rect) != MS_TRUE) { |
852 | 0 | msSetBit(status, i, 0); |
853 | 0 | } |
854 | 0 | } |
855 | 0 | i = msGetNextBit(status, i + 1, shp->numshapes); |
856 | 0 | } |
857 | 0 | } |