Coverage Report

Created: 2026-07-14 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}