Coverage Report

Created: 2025-06-13 06:29

/src/MapServer/src/mapquery.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Layer query support.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 ****************************************************************************/
29
30
#include "mapserver.h"
31
#include "mapows.h"
32
33
#include "limits.h"
34
35
/* This object is used by the various mapQueryXXXXX() functions. It stores
36
 * the total amount of shapes and their RAM footprint, when they are cached
37
 * in the resultCacheObj* of layers. This is the total number across all
38
 * queried layers. However this is isn't persistent across several calls to
39
 * mapQueryXXXXX(), if the resultCacheObj* objects weren't cleaned up. This
40
 * is not needed in the context of WFS, for which this is used for now.
41
 */
42
typedef struct {
43
  int cachedShapeCountWarningEmitted;
44
  int cachedShapeCount;
45
  int cachedShapeRAMWarningEmitted;
46
  int cachedShapeRAM;
47
} queryCacheObj;
48
49
16.3k
int msInitQuery(queryObj *query) {
50
16.3k
  if (!query)
51
0
    return MS_FAILURE;
52
53
16.3k
  msFreeQuery(query); /* clean up anything previously allocated */
54
55
16.3k
  query->type = MS_QUERY_IS_NULL; /* nothing defined */
56
16.3k
  query->mode = MS_QUERY_SINGLE;
57
58
16.3k
  query->layer = query->slayer = -1;
59
60
16.3k
  query->point.x = query->point.y = -1;
61
16.3k
  query->buffer = 0.0;
62
16.3k
  query->maxresults = 0; /* only used by msQueryByPoint() apparently */
63
64
16.3k
  query->rect.minx = query->rect.miny = query->rect.maxx = query->rect.maxy =
65
16.3k
      -1;
66
16.3k
  query->shape = NULL;
67
68
16.3k
  query->shapeindex = query->tileindex = -1;
69
16.3k
  query->clear_resultcache =
70
16.3k
      MS_TRUE; /* index queries allow the old results to persist */
71
72
16.3k
  query->maxfeatures = -1;
73
16.3k
  query->startindex = -1;
74
16.3k
  query->only_cache_result_count = 0;
75
76
16.3k
  query->filteritem = NULL;
77
16.3k
  msInitExpression(&query->filter);
78
79
16.3k
  query->cache_shapes = MS_FALSE;
80
16.3k
  query->max_cached_shape_count = 0;
81
16.3k
  query->max_cached_shape_ram_amount = 0;
82
83
16.3k
  return MS_SUCCESS;
84
16.3k
}
85
86
32.6k
void msFreeQuery(queryObj *query) {
87
32.6k
  if (query->shape) {
88
0
    msFreeShape(query->shape);
89
0
    free(query->shape);
90
0
  }
91
92
32.6k
  if (query->filteritem)
93
0
    free(query->filteritem);
94
32.6k
  msFreeExpression(&query->filter);
95
32.6k
}
96
97
/*
98
** Wrapper for all query functions, just feed is a mapObj with a populated
99
*queryObj...
100
*/
101
0
int msExecuteQuery(mapObj *map) {
102
0
  int tmp = -1, status;
103
104
  /* handle slayer/qlayer management for feature queries */
105
0
  if (map->query.slayer >= 0) {
106
0
    tmp = map->query.layer;
107
0
    map->query.layer = map->query.slayer;
108
0
  }
109
110
0
  switch (map->query.type) {
111
0
  case MS_QUERY_BY_POINT:
112
0
    status = msQueryByPoint(map);
113
0
    break;
114
0
  case MS_QUERY_BY_RECT:
115
0
    status = msQueryByRect(map);
116
0
    break;
117
0
  case MS_QUERY_BY_FILTER:
118
0
    status = msQueryByFilter(map);
119
0
    break;
120
0
  case MS_QUERY_BY_SHAPE:
121
0
    status = msQueryByShape(map);
122
0
    break;
123
0
  case MS_QUERY_BY_INDEX:
124
0
    status = msQueryByIndex(map);
125
0
    break;
126
0
  default:
127
0
    msSetError(MS_QUERYERR, "Malformed queryObj.", "msExecuteQuery()");
128
0
    return (MS_FAILURE);
129
0
  }
130
131
0
  if (map->query.slayer >= 0) {
132
0
    map->query.layer = tmp; /* restore layer */
133
0
    if (status == MS_SUCCESS)
134
0
      status = msQueryByFeatures(map);
135
0
  }
136
137
0
  return status;
138
0
}
139
140
/*
141
** msIsLayerQueryable()  returns MS_TRUE/MS_FALSE
142
*/
143
0
int msIsLayerQueryable(layerObj *lp) {
144
0
  int i;
145
146
0
  if (lp->type == MS_LAYER_TILEINDEX)
147
0
    return MS_FALSE;
148
149
0
  if (lp->template && strlen(lp->template) > 0)
150
0
    return MS_TRUE;
151
152
0
  for (i = 0; i < lp->numclasses; i++) {
153
0
    if (lp->class[i] -> template &&strlen(lp->class[i] -> template) > 0)
154
0
      return MS_TRUE;
155
0
  }
156
157
0
  return MS_FALSE;
158
0
}
159
160
0
static void initQueryCache(queryCacheObj *queryCache) {
161
0
  queryCache->cachedShapeCountWarningEmitted = MS_FALSE;
162
0
  queryCache->cachedShapeCount = 0;
163
0
  queryCache->cachedShapeRAMWarningEmitted = MS_FALSE;
164
0
  queryCache->cachedShapeRAM = 0;
165
0
}
166
167
/** Check whether we should store the shape in resultCacheObj* given the
168
 * limits allowed in map->query.max_cached_shape_count and
169
 * map->query.max_cached_shape_ram_amount.
170
 */
171
static int canCacheShape(mapObj *map, queryCacheObj *queryCache,
172
0
                         int shape_ram_size) {
173
0
  if (!map->query.cache_shapes)
174
0
    return MS_FALSE;
175
0
  if (queryCache->cachedShapeCountWarningEmitted ||
176
0
      (map->query.max_cached_shape_count > 0 &&
177
0
       queryCache->cachedShapeCount >= map->query.max_cached_shape_count)) {
178
0
    if (!queryCache->cachedShapeCountWarningEmitted) {
179
0
      queryCache->cachedShapeCountWarningEmitted = MS_TRUE;
180
0
      if (map->debug >= MS_DEBUGLEVEL_V) {
181
0
        msDebug("map->query.max_cached_shape_count = %d reached. "
182
0
                "Next features will not be cached.\n",
183
0
                map->query.max_cached_shape_count);
184
0
      }
185
0
    }
186
0
    return MS_FALSE;
187
0
  }
188
0
  if (queryCache->cachedShapeRAMWarningEmitted ||
189
0
      (map->query.max_cached_shape_ram_amount > 0 &&
190
0
       queryCache->cachedShapeRAM + shape_ram_size >
191
0
           map->query.max_cached_shape_ram_amount)) {
192
0
    if (!queryCache->cachedShapeRAMWarningEmitted) {
193
0
      queryCache->cachedShapeRAMWarningEmitted = MS_TRUE;
194
0
      if (map->debug >= MS_DEBUGLEVEL_V) {
195
0
        msDebug("map->query.max_cached_shape_ram_amount = %d reached after %d "
196
0
                "cached features. "
197
0
                "Next features will not be cached.\n",
198
0
                map->query.max_cached_shape_ram_amount,
199
0
                queryCache->cachedShapeCount);
200
0
      }
201
0
    }
202
0
    return MS_FALSE;
203
0
  }
204
0
  return MS_TRUE;
205
0
}
206
207
static int addResult(mapObj *map, resultCacheObj *cache,
208
0
                     queryCacheObj *queryCache, shapeObj *shape) {
209
0
  int i;
210
0
  int shape_ram_size = (map->query.max_cached_shape_ram_amount > 0)
211
0
                           ? msGetShapeRAMSize(shape)
212
0
                           : 0;
213
0
  int store_shape = canCacheShape(map, queryCache, shape_ram_size);
214
215
0
  if (cache->numresults == cache->cachesize) { /* just add it to the end */
216
0
    if (cache->cachesize == 0)
217
0
      cache->results =
218
0
          (resultObj *)malloc(sizeof(resultObj) * MS_RESULTCACHEINCREMENT);
219
0
    else
220
0
      cache->results = (resultObj *)realloc(
221
0
          cache->results,
222
0
          sizeof(resultObj) * (cache->cachesize + MS_RESULTCACHEINCREMENT));
223
0
    if (!cache->results) {
224
0
      msSetError(MS_MEMERR, "Realloc() error.", "addResult()");
225
0
      return (MS_FAILURE);
226
0
    }
227
0
    cache->cachesize += MS_RESULTCACHEINCREMENT;
228
0
  }
229
230
0
  i = cache->numresults;
231
232
0
  cache->results[i].classindex = shape->classindex;
233
0
  cache->results[i].tileindex = shape->tileindex;
234
0
  cache->results[i].shapeindex = shape->index;
235
0
  cache->results[i].resultindex = shape->resultindex;
236
0
  if (store_shape) {
237
0
    cache->results[i].shape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
238
0
    msInitShape(cache->results[i].shape);
239
0
    msCopyShape(shape, cache->results[i].shape);
240
0
    queryCache->cachedShapeCount++;
241
0
    queryCache->cachedShapeRAM += shape_ram_size;
242
0
  } else
243
0
    cache->results[i].shape = NULL;
244
0
  cache->numresults++;
245
246
0
  cache->previousBounds = cache->bounds;
247
0
  if (cache->numresults == 1)
248
0
    cache->bounds = shape->bounds;
249
0
  else
250
0
    msMergeRect(&(cache->bounds), &(shape->bounds));
251
252
0
  return (MS_SUCCESS);
253
0
}
254
255
/*
256
** Serialize a query result set to disk.
257
*/
258
0
static int saveQueryResults(mapObj *map, char *filename) {
259
0
  FILE *stream;
260
0
  int i, j, n = 0;
261
262
0
  if (!filename) {
263
0
    msSetError(MS_MISCERR, "No filename provided to save query results to.",
264
0
               "saveQueryResults()");
265
0
    return MS_FAILURE;
266
0
  }
267
268
0
  stream = fopen(filename, "w");
269
0
  if (!stream) {
270
0
    msSetError(MS_IOERR, "(%s)", "saveQueryResults()", filename);
271
0
    return MS_FAILURE;
272
0
  }
273
274
0
  fprintf(stream, "%s - Generated by msSaveQuery()\n",
275
0
          MS_QUERY_RESULTS_MAGIC_STRING);
276
277
  /* count the number of layers with results */
278
0
  for (i = 0; i < map->numlayers; i++)
279
0
    if (GET_LAYER(map, i)->resultcache)
280
0
      n++;
281
0
  fwrite(&n, sizeof(int), 1, stream);
282
283
  /* now write the result set for each layer */
284
0
  for (i = 0; i < map->numlayers; i++) {
285
0
    if (GET_LAYER(map, i)->resultcache) {
286
0
      fwrite(&i, sizeof(int), 1, stream); /* layer index */
287
0
      fwrite(&(GET_LAYER(map, i)->resultcache->numresults), sizeof(int), 1,
288
0
             stream); /* number of results */
289
0
      fwrite(&(GET_LAYER(map, i)->resultcache->bounds), sizeof(rectObj), 1,
290
0
             stream); /* bounding box */
291
0
      for (j = 0; j < GET_LAYER(map, i)->resultcache->numresults; j++)
292
0
        fwrite(&(GET_LAYER(map, i)->resultcache->results[j]), sizeof(resultObj),
293
0
               1, stream); /* each result */
294
0
    }
295
0
  }
296
297
0
  fclose(stream);
298
0
  return MS_SUCCESS;
299
0
}
300
301
0
static int loadQueryResults(mapObj *map, FILE *stream) {
302
0
  int i, j, k, n = 0;
303
304
0
  if (1 != fread(&n, sizeof(int), 1, stream) || n > INT_MAX - 1) {
305
0
    msSetError(MS_MISCERR, "failed to read query count from query file stream",
306
0
               "loadQueryResults()");
307
0
    return MS_FAILURE;
308
0
  }
309
310
  /* now load the result set for each layer found in the query file */
311
0
  for (i = 0; i < n; i++) {
312
0
    if (1 != fread(&j, sizeof(int), 1, stream)) { /* layer index */
313
0
      msSetError(MS_MISCERR,
314
0
                 "failed to read layer index from query file stream",
315
0
                 "loadQueryResults()");
316
0
      return MS_FAILURE;
317
0
    }
318
319
0
    if (j < 0 || j > map->numlayers) {
320
0
      msSetError(MS_MISCERR, "Invalid layer index loaded from query file.",
321
0
                 "loadQueryResults()");
322
0
      return MS_FAILURE;
323
0
    }
324
325
    /* inialize the results for this layer */
326
0
    GET_LAYER(map, j)->resultcache = (resultCacheObj *)malloc(
327
0
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
328
0
    MS_CHECK_ALLOC(GET_LAYER(map, j)->resultcache, sizeof(resultCacheObj),
329
0
                   MS_FAILURE);
330
331
0
    if (1 != fread(&(GET_LAYER(map, j)->resultcache->numresults), sizeof(int),
332
0
                   1, stream) ||
333
0
        (GET_LAYER(map, j)->resultcache->numresults <
334
0
         0)) { /* number of results */
335
0
      msSetError(MS_MISCERR,
336
0
                 "failed to read number of results from query file stream",
337
0
                 "loadQueryResults()");
338
0
      free(GET_LAYER(map, j)->resultcache);
339
0
      GET_LAYER(map, j)->resultcache = NULL;
340
0
      return MS_FAILURE;
341
0
    }
342
0
    GET_LAYER(map, j)->resultcache->cachesize =
343
0
        GET_LAYER(map, j)->resultcache->numresults;
344
345
0
    if (1 != fread(&(GET_LAYER(map, j)->resultcache->bounds), sizeof(rectObj),
346
0
                   1, stream)) { /* bounding box */
347
0
      msSetError(MS_MISCERR, "failed to read bounds from query file stream",
348
0
                 "loadQueryResults()");
349
0
      free(GET_LAYER(map, j)->resultcache);
350
0
      GET_LAYER(map, j)->resultcache = NULL;
351
0
      return MS_FAILURE;
352
0
    }
353
354
0
    GET_LAYER(map, j)->resultcache->results = (resultObj *)malloc(
355
0
        sizeof(resultObj) * GET_LAYER(map, j)->resultcache->numresults);
356
0
    if (GET_LAYER(map, j)->resultcache->results == NULL) {
357
0
      msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
358
0
                 "loadQueryResults()", __FILE__, __LINE__,
359
0
                 (unsigned int)(sizeof(resultObj) *
360
0
                                GET_LAYER(map, j)->resultcache->numresults));
361
0
      free(GET_LAYER(map, j)->resultcache);
362
0
      GET_LAYER(map, j)->resultcache = NULL;
363
0
      return MS_FAILURE;
364
0
    }
365
366
0
    for (k = 0; k < GET_LAYER(map, j)->resultcache->numresults; k++) {
367
0
      if (1 != fread(&(GET_LAYER(map, j)->resultcache->results[k]),
368
0
                     sizeof(resultObj), 1, stream)) { /* each result */
369
0
        msSetError(MS_MISCERR,
370
0
                   "failed to read result %d from query file stream",
371
0
                   "loadQueryResults()", k);
372
0
        free(GET_LAYER(map, j)->resultcache->results);
373
0
        free(GET_LAYER(map, j)->resultcache);
374
0
        GET_LAYER(map, j)->resultcache = NULL;
375
0
        return MS_FAILURE;
376
0
      }
377
0
      if (!GET_LAYER(map, j)->tileindex)
378
0
        GET_LAYER(map, j)->resultcache->results[k].tileindex =
379
0
            -1; /* reset the tile index for non-tiled layers */
380
0
      GET_LAYER(map, j)->resultcache->results[k].resultindex =
381
0
          -1; /* all results loaded this way have a -1 result (set) index */
382
0
    }
383
0
  }
384
385
0
  return MS_SUCCESS;
386
0
}
387
388
/*
389
** Serialize the parameters necessary to duplicate a query to disk. (TODO: add
390
*filter query...)
391
*/
392
0
static int saveQueryParams(mapObj *map, char *filename) {
393
0
  FILE *stream;
394
395
0
  if (!filename) {
396
0
    msSetError(MS_MISCERR, "No filename provided to save query to.",
397
0
               "saveQueryParams()");
398
0
    return MS_FAILURE;
399
0
  }
400
401
0
  stream = fopen(filename, "w");
402
0
  if (!stream) {
403
0
    msSetError(MS_IOERR, "(%s)", "saveQueryParams()", filename);
404
0
    return MS_FAILURE;
405
0
  }
406
407
0
  fprintf(stream, "%s - Generated by msSaveQuery()\n",
408
0
          MS_QUERY_PARAMS_MAGIC_STRING);
409
410
0
  fprintf(stream, "%d %d %d %d\n", map->query.mode, map->query.type,
411
0
          map->query.layer, map->query.slayer); /* all queries */
412
0
  fprintf(stream, "%.15g %.15g %g %d\n", map->query.point.x, map->query.point.y,
413
0
          map->query.buffer, map->query.maxresults); /* by point */
414
0
  fprintf(stream, "%.15g %.15g %.15g %.15g\n", map->query.rect.minx,
415
0
          map->query.rect.miny, map->query.rect.maxx,
416
0
          map->query.rect.maxy); /* by rect */
417
0
  fprintf(stream, "%ld %ld %d\n", map->query.shapeindex, map->query.tileindex,
418
0
          map->query.clear_resultcache); /* by index */
419
420
0
  fprintf(stream, "%s\n",
421
0
          (map->query.filteritem) ? map->query.filteritem
422
0
                                  : "NULL"); /* by filter */
423
0
  fprintf(stream, "%s\n",
424
0
          (map->query.filter.string) ? map->query.filter.string : "NULL");
425
426
0
  if (map->query.shape) { /* by shape */
427
0
    int i, j;
428
0
    shapeObj *s = map->query.shape;
429
430
0
    fprintf(stream, "%d\n", s->type);
431
0
    fprintf(stream, "%d\n", s->numlines);
432
0
    for (i = 0; i < s->numlines; i++) {
433
0
      fprintf(stream, "%d\n", s->line[i].numpoints);
434
0
      for (j = 0; j < s->line[i].numpoints; j++)
435
0
        fprintf(stream, "%.15g %.15g\n", s->line[i].point[j].x,
436
0
                s->line[i].point[j].y);
437
0
    }
438
0
  } else {
439
0
    fprintf(stream, "%d\n", MS_SHAPE_NULL); /* NULL shape */
440
0
  }
441
442
0
  fclose(stream);
443
0
  return MS_SUCCESS;
444
0
}
445
446
0
static int loadQueryParams(mapObj *map, FILE *stream) {
447
0
  char buffer[MS_BUFFER_LENGTH];
448
0
  int lineno;
449
450
0
  int shapetype, numlines, numpoints;
451
452
0
  msInitQuery(&(map->query)); /* this will free any previous query as well */
453
454
0
  lineno = 2; /* line 1 is the magic string */
455
0
  while (fgets(buffer, MS_BUFFER_LENGTH, stream) != NULL) {
456
0
    switch (lineno) {
457
0
    case 2:
458
0
      if (sscanf(buffer, "%d %d %d %d\n", &(map->query.mode),
459
0
                 &(map->query.type), &(map->query.layer),
460
0
                 &(map->query.slayer)) != 4)
461
0
        goto parse_error;
462
0
      break;
463
0
    case 3:
464
0
      if (sscanf(buffer, "%lf %lf %lf %d\n", &map->query.point.x,
465
0
                 &map->query.point.y, &map->query.buffer,
466
0
                 &map->query.maxresults) != 4)
467
0
        goto parse_error;
468
0
      break;
469
0
    case 4:
470
0
      if (sscanf(buffer, "%lf %lf %lf %lf\n", &map->query.rect.minx,
471
0
                 &map->query.rect.miny, &map->query.rect.maxx,
472
0
                 &map->query.rect.maxy) != 4)
473
0
        goto parse_error;
474
0
      break;
475
0
    case 5:
476
0
      if (sscanf(buffer, "%ld %ld %d\n", &map->query.shapeindex,
477
0
                 &map->query.tileindex, &map->query.clear_resultcache) != 3)
478
0
        goto parse_error;
479
0
      break;
480
0
    case 6:
481
0
      if (strncmp(buffer, "NULL", 4) != 0) {
482
0
        map->query.filteritem = msStrdup(buffer);
483
0
        msStringChop(map->query.filteritem);
484
0
      }
485
0
      break;
486
0
    case 7:
487
0
      if (strncmp(buffer, "NULL", 4) != 0)
488
0
        msLoadExpressionString(&map->query.filter, buffer); /* chop buffer */
489
0
      break;
490
0
    case 8:
491
0
      if (sscanf(buffer, "%d\n", &shapetype) != 1)
492
0
        goto parse_error;
493
494
0
      if (shapetype != MS_SHAPE_NULL) { /* load the rest of the shape */
495
0
        int i, j;
496
0
        lineObj line;
497
498
0
        map->query.shape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
499
0
        msInitShape(map->query.shape);
500
0
        map->query.shape->type = shapetype;
501
502
0
        if (fscanf(stream, "%d\n", &numlines) != 1)
503
0
          goto parse_error;
504
0
        if (numlines > INT_MAX - 1)
505
0
          goto parse_error;
506
0
        for (i = 0; i < numlines; i++) {
507
0
          if (fscanf(stream, "%d\n", &numpoints) != 1 || numpoints < 0 ||
508
0
              numpoints > INT_MAX / (int)sizeof(pointObj))
509
0
            goto parse_error;
510
511
0
          line.numpoints = numpoints;
512
0
          line.point =
513
0
              (pointObj *)msSmallMalloc(line.numpoints * sizeof(pointObj));
514
515
0
          for (j = 0; j < numpoints; j++)
516
0
            if (fscanf(stream, "%lf %lf\n", &line.point[j].x,
517
0
                       &line.point[j].y) != 2)
518
0
              goto parse_error;
519
520
0
          msAddLine(map->query.shape, &line);
521
0
          free(line.point);
522
0
        }
523
0
      }
524
0
      break;
525
0
    default:
526
0
      break;
527
0
    }
528
529
0
    lineno++;
530
0
  }
531
532
  /* TODO: should we throw an error if lineno != 10? */
533
534
  /* force layer and slayer on */
535
0
  if (map->query.layer >= 0 && map->query.layer < map->numlayers)
536
0
    GET_LAYER(map, map->query.layer)->status = MS_ON;
537
0
  if (map->query.slayer >= 0 && map->query.slayer < map->numlayers)
538
0
    GET_LAYER(map, map->query.slayer)->status = MS_ON;
539
540
  /* now execute the query */
541
0
  return msExecuteQuery(map);
542
543
0
parse_error:
544
0
  msSetError(MS_MISCERR, "Parse error line %d.", "loadQueryParameters()",
545
0
             lineno);
546
0
  return MS_FAILURE;
547
0
}
548
549
/*
550
** Save (serialize) a query to disk. There are two methods, one saves the query
551
*parameters and the other saves
552
** all the shape indexes. Note the latter can be very slow against certain data
553
*sources but has a certain usefulness
554
** on occation.
555
*/
556
0
int msSaveQuery(mapObj *map, char *filename, int results) {
557
0
  if (results)
558
0
    return saveQueryResults(map, filename);
559
0
  else
560
0
    return saveQueryParams(map, filename);
561
0
}
562
563
/*
564
** Loads a query file contained either serialized 1) query parameters or 2)
565
*query results (e.g. indexes).
566
*/
567
0
int msLoadQuery(mapObj *map, char *filename) {
568
0
  FILE *stream;
569
0
  char buffer[MS_BUFFER_LENGTH];
570
0
  int retval = MS_FAILURE;
571
572
0
  if (!filename) {
573
0
    msSetError(MS_MISCERR, "No filename provided to load query from.",
574
0
               "msLoadQuery()");
575
0
    return MS_FAILURE;
576
0
  }
577
578
  /*
579
  ** Make sure the file at least has the right extension.
580
  */
581
0
  if (msEvalRegex("\\.qy$", filename) != MS_TRUE) {
582
0
    msSetError(MS_MISCERR, "Queryfile %s has incorrect file extension.",
583
0
               "msLoadQuery()", filename);
584
0
    return MS_FAILURE;
585
0
  }
586
587
  /*
588
  ** Open the file and inspect the first line.
589
  */
590
0
  stream = fopen(filename, "r");
591
0
  if (!stream) {
592
0
    msSetError(MS_IOERR, "(%s)", "msLoadQuery()", filename);
593
0
    return MS_FAILURE;
594
0
  }
595
596
0
  if (fgets(buffer, MS_BUFFER_LENGTH, stream) != NULL) {
597
    /*
598
    ** Call correct reader based on the magic string.
599
    */
600
0
    if (strncasecmp(buffer, MS_QUERY_RESULTS_MAGIC_STRING,
601
0
                    strlen(MS_QUERY_RESULTS_MAGIC_STRING)) == 0) {
602
0
      retval = loadQueryResults(map, stream);
603
0
    } else if (strncasecmp(buffer, MS_QUERY_PARAMS_MAGIC_STRING,
604
0
                           strlen(MS_QUERY_PARAMS_MAGIC_STRING)) == 0) {
605
0
      retval = loadQueryParams(map, stream);
606
0
    } else {
607
0
      msSetError(
608
0
          MS_WEBERR,
609
0
          "Missing magic string, %s doesn't look like a MapServer query file.",
610
0
          "msLoadQuery()", filename);
611
0
      retval = MS_FAILURE;
612
0
    }
613
0
  } else {
614
0
    msSetError(MS_WEBERR, "Empty file or failed read for %s.", "msLoadQuery()",
615
0
               filename);
616
0
    retval = MS_FAILURE;
617
0
  }
618
619
0
  fclose(stream);
620
0
  return retval;
621
0
}
622
623
0
int msQueryByIndex(mapObj *map) {
624
0
  layerObj *lp;
625
0
  int status;
626
627
0
  resultObj record;
628
0
  queryCacheObj queryCache;
629
630
0
  shapeObj shape;
631
0
  double minfeaturesize = -1;
632
633
0
  initQueryCache(&queryCache);
634
635
0
  if (map->query.type != MS_QUERY_BY_INDEX) {
636
0
    msSetError(MS_QUERYERR, "The query is not properly defined.",
637
0
               "msQueryByIndex()");
638
0
    return (MS_FAILURE);
639
0
  }
640
641
0
  if (map->query.layer < 0 || map->query.layer >= map->numlayers) {
642
0
    msSetError(MS_QUERYERR, "No query layer defined.", "msQueryByIndex()");
643
0
    return (MS_FAILURE);
644
0
  }
645
646
0
  lp = (GET_LAYER(map, map->query.layer));
647
648
0
  if (!msIsLayerQueryable(lp)) {
649
0
    msSetError(MS_QUERYERR, "Requested layer has no templates defined.",
650
0
               "msQueryByIndex()");
651
0
    return (MS_FAILURE);
652
0
  }
653
654
0
  if (map->query.clear_resultcache) {
655
0
    if (lp->resultcache) {
656
0
      cleanupResultCache(lp->resultcache);
657
0
      free(lp->resultcache);
658
0
      lp->resultcache = NULL;
659
0
    }
660
0
  }
661
662
0
  msLayerClose(lp); /* reset */
663
0
  status = msLayerOpen(lp);
664
0
  if (status != MS_SUCCESS)
665
0
    return (MS_FAILURE);
666
  /* disable driver paging */
667
0
  msLayerEnablePaging(lp, MS_FALSE);
668
669
  /* build item list, we want *all* items */
670
0
  status = msLayerWhichItems(lp, MS_TRUE, NULL);
671
0
  if (status != MS_SUCCESS)
672
0
    return (MS_FAILURE);
673
674
0
  if (map->query.clear_resultcache || lp->resultcache == NULL) {
675
0
    lp->resultcache = (resultCacheObj *)malloc(
676
0
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
677
0
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
678
0
    initResultCache(lp->resultcache);
679
0
  }
680
681
0
  msInitShape(&shape);
682
683
0
  record.resultindex = -1;
684
0
  record.shapeindex = map->query.shapeindex;
685
0
  record.tileindex = map->query.tileindex;
686
687
0
  status = msLayerGetShape(lp, &shape, &record);
688
0
  if (status != MS_SUCCESS) {
689
0
    msSetError(MS_QUERYERR, "Not valid record request.", "msQueryByIndex()");
690
0
    return (MS_FAILURE);
691
0
  }
692
693
  /*
694
   * The resultindex is used to retrieve a specific item from the result cache.
695
   * Usually, the row number will be used as resultindex. But when working with
696
   * databases and querying a single result, the row number is typically 0 and
697
   * thus useless as the index in the result cache. See #4926 #4076. Only shape
698
   * files and flatgeobuf are considered to have consistent row numbers.
699
   */
700
0
  if (!(lp->connectiontype == MS_SHAPEFILE ||
701
0
        lp->connectiontype == MS_TILED_SHAPEFILE ||
702
0
        lp->connectiontype == MS_FLATGEOBUF)) {
703
0
    shape.resultindex = -1;
704
0
  }
705
706
0
  if (lp->minfeaturesize > 0)
707
0
    minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
708
709
  /* Check if the shape size is ok to be drawn */
710
0
  if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
711
0
      (minfeaturesize > 0)) {
712
713
0
    if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
714
0
      msSetError(MS_QUERYERR,
715
0
                 "Requested shape not valid against layer minfeaturesize.",
716
0
                 "msQueryByIndex()");
717
0
      msFreeShape(&shape);
718
0
      msLayerClose(lp);
719
0
      return (MS_FAILURE);
720
0
    }
721
0
  }
722
723
0
  shape.classindex = msShapeGetClass(lp, map, &shape, NULL, 0);
724
0
  if (!(lp->template) &&
725
0
      ((shape.classindex == -1) ||
726
0
       (lp->class[shape.classindex] -> status ==
727
0
                                           MS_OFF))) { /* not a valid shape */
728
0
    msSetError(MS_QUERYERR,
729
0
               "Requested shape not valid against layer classification scheme.",
730
0
               "msQueryByIndex()");
731
0
    msFreeShape(&shape);
732
0
    msLayerClose(lp);
733
0
    return (MS_FAILURE);
734
0
  }
735
736
0
  if (!(lp->template) &&
737
0
      !(lp->class[shape.classindex] -> template)) { /* no valid template */
738
0
    msSetError(MS_QUERYERR,
739
0
               "Requested shape does not have a valid template, no way to "
740
0
               "present results.",
741
0
               "msQueryByIndex()");
742
0
    msFreeShape(&shape);
743
0
    msLayerClose(lp);
744
0
    return (MS_FAILURE);
745
0
  }
746
747
0
  addResult(map, lp->resultcache, &queryCache, &shape);
748
749
0
  msFreeShape(&shape);
750
  /* msLayerClose(lp); */
751
752
0
  return (MS_SUCCESS);
753
0
}
754
755
0
static char *filterTranslateToLogical(expressionObj *filter, char *filteritem) {
756
0
  char *string = NULL;
757
758
0
  if (filter->type == MS_STRING && filteritem) {
759
0
    string = msStrdup("'[");
760
0
    string = msStringConcatenate(string, filteritem);
761
0
    string = msStringConcatenate(string, "]'");
762
0
    if (filter->flags & MS_EXP_INSENSITIVE)
763
0
      string = msStringConcatenate(string, " =* '");
764
0
    else
765
0
      string = msStringConcatenate(string, " = '");
766
0
    string = msStringConcatenate(string, filter->string);
767
0
    string = msStringConcatenate(string, "'");
768
0
  } else if (filter->type == MS_REGEX && filteritem) {
769
0
    string = msStrdup("'[");
770
0
    string = msStringConcatenate(string, filteritem);
771
0
    string = msStringConcatenate(string, "]'");
772
0
    if (filter->flags & MS_EXP_INSENSITIVE)
773
0
      string = msStringConcatenate(string, " ~* '");
774
0
    else
775
0
      string = msStringConcatenate(string, " ~ '");
776
0
    string = msStringConcatenate(string, filter->string);
777
0
    string = msStringConcatenate(string, "'");
778
0
  } else if (filter->type == MS_EXPRESSION) {
779
0
    string = msStrdup(filter->string);
780
0
  } else {
781
    /* native expression, nothing we can do - sigh */
782
0
  }
783
784
0
  return string;
785
0
}
786
787
static expressionObj mergeFilters(expressionObj *filter1, char *filteritem1,
788
0
                                  expressionObj *filter2, char *filteritem2) {
789
0
  expressionObj filter;
790
791
0
  char *tmpstr1 = NULL;
792
0
  char *tmpstr2 = NULL;
793
794
0
  msInitExpression(&filter);
795
0
  filter.type = MS_EXPRESSION; /* we're building a logical expression */
796
797
0
  tmpstr1 = filterTranslateToLogical(filter1, filteritem1);
798
0
  if (!tmpstr1)
799
0
    return filter; /* should only happen if the filter was a native filter */
800
801
0
  tmpstr2 = filterTranslateToLogical(filter2, filteritem2);
802
0
  if (!tmpstr2) {
803
0
    msFree(tmpstr1);
804
0
    return filter; /* should only happen if the filter was a native filter */
805
0
  }
806
807
0
  filter.string = msStrdup(tmpstr1);
808
0
  filter.string = msStringConcatenate(filter.string, " AND ");
809
0
  filter.string = msStringConcatenate(filter.string, tmpstr2);
810
811
0
  msFree(tmpstr1);
812
0
  msFree(tmpstr2);
813
814
0
  return filter;
815
0
}
816
817
/*
818
** Query using common expression syntax.
819
*/
820
0
int msQueryByFilter(mapObj *map) {
821
0
  int l;
822
0
  int start, stop = 0;
823
824
0
  layerObj *lp;
825
826
0
  char status;
827
828
0
  char *old_filteritem = NULL;
829
0
  expressionObj old_filter;
830
831
0
  rectObj search_rect;
832
0
  const rectObj invalid_rect = MS_INIT_INVALID_RECT;
833
834
0
  shapeObj shape;
835
0
  int paging;
836
837
0
  int nclasses = 0;
838
0
  int *classgroup = NULL;
839
0
  double minfeaturesize = -1;
840
0
  queryCacheObj queryCache;
841
842
0
  initQueryCache(&queryCache);
843
844
0
  if (map->query.type != MS_QUERY_BY_FILTER) {
845
0
    msSetError(MS_QUERYERR, "The query is not properly defined.",
846
0
               "msQueryByFilter()");
847
0
    return (MS_FAILURE);
848
0
  }
849
0
  if (!map->query.filter.string) {
850
0
    msSetError(MS_QUERYERR, "Filter is not set.", "msQueryByFilter()");
851
0
    return (MS_FAILURE);
852
0
  }
853
854
  // fprintf(stderr, "in msQueryByFilter: filter=%s, filteritem=%s\n",
855
  // map->query.filter.string, map->query.filteritem);
856
857
0
  msInitShape(&shape);
858
859
0
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
860
0
    start = map->numlayers - 1;
861
0
  else
862
0
    start = stop = map->query.layer;
863
864
0
  for (l = start; l >= stop; l--) {
865
0
    reprojectionObj *reprojector = NULL;
866
867
0
    lp = (GET_LAYER(map, l));
868
0
    if (map->query.maxfeatures == 0)
869
0
      break; /* nothing else to do */
870
0
    else if (map->query.maxfeatures > 0)
871
0
      lp->maxfeatures = map->query.maxfeatures;
872
873
    /* using mapscript, the map->query.startindex will be unset... */
874
0
    if (lp->startindex > 1 && map->query.startindex < 0)
875
0
      map->query.startindex = lp->startindex;
876
877
    /* conditions may have changed since this layer last drawn, so set
878
       layer->project true to recheck projection needs (Bug #673) */
879
0
    lp->project = MS_TRUE;
880
881
    /* free any previous search results, do it now in case one of the next few
882
     * tests fail */
883
0
    if (lp->resultcache) {
884
0
      if (lp->resultcache->results)
885
0
        free(lp->resultcache->results);
886
0
      free(lp->resultcache);
887
0
      lp->resultcache = NULL;
888
0
    }
889
890
0
    if (!msIsLayerQueryable(lp))
891
0
      continue;
892
0
    if (lp->status == MS_OFF)
893
0
      continue;
894
0
    if (lp->type == MS_LAYER_RASTER)
895
0
      continue; /* ok to skip? */
896
897
0
    if (map->scaledenom > 0) {
898
0
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
899
0
        continue;
900
0
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
901
0
        continue;
902
0
    }
903
904
0
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
905
0
      if ((lp->maxgeowidth > 0) &&
906
0
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
907
0
        continue;
908
0
      if ((lp->mingeowidth > 0) &&
909
0
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
910
0
        continue;
911
0
    }
912
913
0
    paging = msLayerGetPaging(lp);
914
0
    msLayerClose(lp); /* reset */
915
0
    status = msLayerOpen(lp);
916
0
    if (status != MS_SUCCESS)
917
0
      return MS_FAILURE;
918
0
    msLayerEnablePaging(lp, paging);
919
920
    /* disable driver paging */
921
    // msLayerEnablePaging(lp, MS_FALSE);
922
923
0
    old_filteritem = lp->filteritem; /* cache the existing filter/filteritem */
924
0
    msInitExpression(&old_filter);
925
0
    msCopyExpression(&old_filter, &lp->filter);
926
927
    /*
928
    ** Set the lp->filter and lp->filteritem (may need to merge). Remember
929
    *filters are *always* MapServer syntax.
930
    */
931
0
    lp->filteritem = map->query.filteritem; /* re-point lp->filteritem */
932
0
    if (old_filter.string !=
933
0
        NULL) { /* need to merge filters to create one logical expression */
934
0
      msFreeExpression(&lp->filter);
935
0
      lp->filter = mergeFilters(&map->query.filter, map->query.filteritem,
936
0
                                &old_filter, old_filteritem);
937
0
      if (!lp->filter.string) {
938
0
        msSetError(MS_MISCERR, "Filter merge failed, able to process query.",
939
0
                   "msQueryByFilter()");
940
0
        goto restore_old_filter;
941
0
      }
942
0
    } else {
943
0
      msCopyExpression(&lp->filter, &map->query.filter); /* apply new filter */
944
0
    }
945
946
    /* build item list, we want *all* items, note this *also* build tokens for
947
     * the layer filter */
948
0
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
949
0
    if (status != MS_SUCCESS)
950
0
      goto restore_old_filter;
951
952
0
    search_rect = map->query.rect;
953
954
    /* If only result count is needed, we can use msLayerGetShapeCount() */
955
    /* that has optimizations to avoid retrieving individual features */
956
0
    if (map->query.only_cache_result_count &&
957
0
        lp->template != NULL && /* always TRUE for WFS case */
958
0
        lp->minfeaturesize <= 0) {
959
0
      int bUseLayerSRS = MS_FALSE;
960
0
      int numFeatures = -1;
961
962
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
963
0
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
964
      /* Optimization to detect the case where a WFS query uses in fact the */
965
      /* whole layer extent, but expressed in a map SRS different from the layer
966
       * SRS */
967
      /* In the case, we can directly request against the layer extent in its
968
       * native SRS */
969
0
      if (lp->project &&
970
0
          memcmp(&search_rect, &invalid_rect, sizeof(search_rect)) != 0 &&
971
0
          msProjectionsDiffer(&(lp->projection), &(map->projection))) {
972
0
        rectObj layerExtent;
973
0
        if (msOWSGetLayerExtent(map, lp, "FO", &layerExtent) == MS_SUCCESS) {
974
0
          rectObj ext = layerExtent;
975
0
          ext.minx -= 1e-5;
976
0
          ext.miny -= 1e-5;
977
0
          ext.maxx += 1e-5;
978
0
          ext.maxy += 1e-5;
979
0
          msProjectRect(&(lp->projection), &(map->projection), &ext);
980
0
          if (fabs(ext.minx - search_rect.minx) <= 2e-5 &&
981
0
              fabs(ext.miny - search_rect.miny) <= 2e-5 &&
982
0
              fabs(ext.maxx - search_rect.maxx) <= 2e-5 &&
983
0
              fabs(ext.maxy - search_rect.maxy) <= 2e-5) {
984
0
            bUseLayerSRS = MS_TRUE;
985
0
            numFeatures =
986
0
                msLayerGetShapeCount(lp, layerExtent, &(lp->projection));
987
0
          }
988
0
        }
989
0
      }
990
0
#endif
991
992
0
      if (!bUseLayerSRS)
993
0
        numFeatures = msLayerGetShapeCount(lp, search_rect, &(map->projection));
994
0
      if (numFeatures >= 0) {
995
0
        lp->resultcache = (resultCacheObj *)malloc(sizeof(
996
0
            resultCacheObj)); /* allocate and initialize the result cache */
997
0
        MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
998
0
        initResultCache(lp->resultcache);
999
0
        lp->resultcache->numresults = numFeatures;
1000
0
        if (!msLayerGetPaging(lp) && map->query.startindex > 1) {
1001
0
          lp->resultcache->numresults -= (map->query.startindex - 1);
1002
0
        }
1003
1004
0
        lp->filteritem = old_filteritem; /* point back to original value */
1005
0
        msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
1006
0
        msFreeExpression(&old_filter);
1007
1008
0
        continue;
1009
0
      }
1010
      // Fallback in case of error (should not happen normally)
1011
0
    }
1012
1013
0
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
1014
0
    if (lp->project &&
1015
0
        memcmp(&search_rect, &invalid_rect, sizeof(search_rect)) != 0)
1016
0
      msProjectRect(&(map->projection), &(lp->projection),
1017
0
                    &search_rect); /* project the searchrect to source coords */
1018
1019
0
    status = msLayerWhichShapes(lp, search_rect, MS_TRUE);
1020
0
    if (status == MS_DONE) {           /* no overlap */
1021
0
      lp->filteritem = old_filteritem; /* point back to original value */
1022
0
      msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
1023
0
      msFreeExpression(&old_filter);
1024
0
      msLayerClose(lp);
1025
0
      continue;
1026
0
    } else if (status != MS_SUCCESS)
1027
0
      goto restore_old_filter;
1028
1029
0
    lp->resultcache = (resultCacheObj *)malloc(
1030
0
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
1031
0
    initResultCache(lp->resultcache);
1032
1033
0
    nclasses = 0;
1034
0
    classgroup = NULL;
1035
0
    if (lp->classgroup && lp->numclasses > 0)
1036
0
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
1037
1038
0
    if (lp->minfeaturesize > 0)
1039
0
      minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
1040
1041
0
    while ((status = msLayerNextShape(lp, &shape)) ==
1042
0
           MS_SUCCESS) { /* step through the shapes - if necessary the filter is
1043
                            applied in msLayerNextShape(...) */
1044
1045
      /* Check if the shape size is ok to be drawn */
1046
0
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
1047
0
          (minfeaturesize > 0)) {
1048
0
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
1049
0
          if (lp->debug >= MS_DEBUGLEVEL_V)
1050
0
            msDebug("msQueryByFilter(): Skipping shape (%ld) because "
1051
0
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
1052
0
                    shape.index);
1053
0
          msFreeShape(&shape);
1054
0
          continue;
1055
0
        }
1056
0
      }
1057
1058
0
      shape.classindex = msShapeGetClass(lp, map, &shape, classgroup, nclasses);
1059
0
      if (!(lp->template) && ((shape.classindex == -1) ||
1060
0
                              (lp->class[shape.classindex]
1061
0
                               -> status == MS_OFF))) { /* not a valid shape */
1062
0
        msFreeShape(&shape);
1063
0
        continue;
1064
0
      }
1065
1066
0
      if (!(lp->template) &&
1067
0
          !(lp->class[shape.classindex] -> template)) { /* no valid template */
1068
0
        msFreeShape(&shape);
1069
0
        continue;
1070
0
      }
1071
1072
0
      if (lp->project) {
1073
0
        if (reprojector == NULL) {
1074
0
          reprojector =
1075
0
              msProjectCreateReprojector(&(lp->projection), &(map->projection));
1076
0
          if (reprojector == NULL) {
1077
0
            msFreeShape(&shape);
1078
0
            status = MS_FAILURE;
1079
0
            break;
1080
0
          }
1081
0
        }
1082
0
        msProjectShapeEx(reprojector, &shape);
1083
0
      }
1084
1085
      /* Should we skip this feature? */
1086
0
      if (!paging && map->query.startindex > 1) {
1087
0
        --map->query.startindex;
1088
0
        msFreeShape(&shape);
1089
0
        continue;
1090
0
      }
1091
1092
0
      if (map->query.only_cache_result_count)
1093
0
        lp->resultcache->numresults++;
1094
0
      else
1095
0
        addResult(map, lp->resultcache, &queryCache, &shape);
1096
0
      msFreeShape(&shape);
1097
1098
0
      if (map->query.mode ==
1099
0
          MS_QUERY_SINGLE) { /* no need to look any further */
1100
0
        status = MS_DONE;
1101
0
        break;
1102
0
      }
1103
1104
      /* check shape count */
1105
0
      if (lp->maxfeatures > 0 &&
1106
0
          lp->maxfeatures == lp->resultcache->numresults) {
1107
0
        status = MS_DONE;
1108
0
        break;
1109
0
      }
1110
0
    } /* next shape */
1111
1112
0
    if (classgroup)
1113
0
      msFree(classgroup);
1114
1115
0
    lp->filteritem = old_filteritem; /* point back to original value */
1116
0
    msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
1117
0
    msFreeExpression(&old_filter);
1118
1119
0
    msProjectDestroyReprojector(reprojector);
1120
1121
0
    if (status != MS_DONE)
1122
0
      return MS_FAILURE;
1123
0
    if (!map->query.only_cache_result_count && lp->resultcache->numresults == 0)
1124
0
      msLayerClose(lp); /* no need to keep the layer open */
1125
0
  }                     /* next layer */
1126
1127
  /* was anything found? */
1128
0
  for (l = start; l >= stop; l--) {
1129
0
    if (GET_LAYER(map, l)->resultcache &&
1130
0
        GET_LAYER(map, l)->resultcache->numresults > 0)
1131
0
      return MS_SUCCESS;
1132
0
  }
1133
1134
0
  if (map->debug >= MS_DEBUGLEVEL_V) {
1135
0
    msDebug("msQueryByFilter(): No matching record(s) found.\n");
1136
0
  }
1137
0
  return (MS_SUCCESS);
1138
1139
0
restore_old_filter:
1140
0
  lp->filteritem = old_filteritem;
1141
0
  msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
1142
0
  msFreeExpression(&old_filter);
1143
0
  msLayerClose(lp);
1144
0
  return MS_FAILURE;
1145
0
}
1146
1147
0
int msQueryByRect(mapObj *map) {
1148
0
  int l; /* counters */
1149
0
  int start, stop = 0;
1150
1151
0
  layerObj *lp;
1152
1153
0
  char status;
1154
0
  shapeObj shape, searchshape;
1155
0
  rectObj searchrect, searchrectInMapProj;
1156
0
  const rectObj invalid_rect = MS_INIT_INVALID_RECT;
1157
0
  double layer_tolerance = 0, tolerance = 0;
1158
1159
0
  int paging;
1160
0
  int nclasses = 0;
1161
0
  int *classgroup = NULL;
1162
0
  double minfeaturesize = -1;
1163
0
  queryCacheObj queryCache;
1164
1165
0
  initQueryCache(&queryCache);
1166
1167
0
  if (map->query.type != MS_QUERY_BY_RECT) {
1168
0
    msSetError(MS_QUERYERR, "The query is not properly defined.",
1169
0
               "msQueryByRect()");
1170
0
    return (MS_FAILURE);
1171
0
  }
1172
1173
0
  msInitShape(&shape);
1174
0
  msInitShape(&searchshape);
1175
1176
0
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
1177
0
    start = map->numlayers - 1;
1178
0
  else
1179
0
    start = stop = map->query.layer;
1180
1181
0
  for (l = start; l >= stop; l--) {
1182
0
    reprojectionObj *reprojector = NULL;
1183
0
    lp = (GET_LAYER(map, l));
1184
    /* Set the global maxfeatures */
1185
0
    if (map->query.maxfeatures == 0)
1186
0
      break; /* nothing else to do */
1187
0
    else if (map->query.maxfeatures > 0)
1188
0
      lp->maxfeatures = map->query.maxfeatures;
1189
1190
    /* using mapscript, the map->query.startindex will be unset... */
1191
0
    if (lp->startindex > 1 && map->query.startindex < 0)
1192
0
      map->query.startindex = lp->startindex;
1193
1194
    /* conditions may have changed since this layer last drawn, so set
1195
       layer->project true to recheck projection needs (Bug #673) */
1196
0
    lp->project = MS_TRUE;
1197
1198
    /* free any previous search results, do it now in case one of the next few
1199
     * tests fail */
1200
0
    if (lp->resultcache) {
1201
0
      if (lp->resultcache->results)
1202
0
        free(lp->resultcache->results);
1203
0
      free(lp->resultcache);
1204
0
      lp->resultcache = NULL;
1205
0
    }
1206
1207
0
    if (!msIsLayerQueryable(lp))
1208
0
      continue;
1209
0
    if (lp->status == MS_OFF)
1210
0
      continue;
1211
1212
0
    if (map->scaledenom > 0) {
1213
0
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
1214
0
        continue;
1215
0
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
1216
0
        continue;
1217
0
    }
1218
1219
0
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
1220
0
      if ((lp->maxgeowidth > 0) &&
1221
0
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
1222
0
        continue;
1223
0
      if ((lp->mingeowidth > 0) &&
1224
0
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
1225
0
        continue;
1226
0
    }
1227
1228
0
    searchrect = map->query.rect;
1229
0
    if (lp->tolerance > 0) {
1230
0
      layer_tolerance = lp->tolerance;
1231
1232
0
      if (lp->toleranceunits == MS_PIXELS)
1233
0
        tolerance = layer_tolerance *
1234
0
                    msAdjustExtent(&(map->extent), map->width, map->height);
1235
0
      else
1236
0
        tolerance = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
1237
0
                                       msInchesPerUnit(map->units, 0));
1238
1239
0
      searchrect.minx -= tolerance;
1240
0
      searchrect.maxx += tolerance;
1241
0
      searchrect.miny -= tolerance;
1242
0
      searchrect.maxy += tolerance;
1243
0
    }
1244
0
    searchrectInMapProj = searchrect;
1245
1246
0
    msRectToPolygon(searchrect, &searchshape);
1247
1248
    /* Raster layers are handled specially. */
1249
0
    if (lp->type == MS_LAYER_RASTER) {
1250
0
      if (msRasterQueryByRect(map, lp, searchrect) == MS_FAILURE)
1251
0
        return MS_FAILURE;
1252
1253
0
      continue;
1254
0
    }
1255
1256
    /* Paging could have been disabled before */
1257
0
    paging = msLayerGetPaging(lp);
1258
0
    msLayerClose(lp); /* reset */
1259
0
    status = msLayerOpen(lp);
1260
0
    if (status != MS_SUCCESS) {
1261
0
      msFreeShape(&searchshape);
1262
0
      return (MS_FAILURE);
1263
0
    }
1264
0
    msLayerEnablePaging(lp, paging);
1265
1266
    /* build item list, we want *all* items */
1267
0
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
1268
1269
0
    if (status != MS_SUCCESS) {
1270
0
      msFreeShape(&searchshape);
1271
0
      return (MS_FAILURE);
1272
0
    }
1273
1274
    /* If only result count is needed, we can use msLayerGetShapeCount() */
1275
    /* that has optimizations to avoid retrieving individual features */
1276
0
    if (map->query.only_cache_result_count &&
1277
0
        lp->template != NULL && /* always TRUE for WFS case */
1278
0
        lp->minfeaturesize <= 0) {
1279
0
      int bUseLayerSRS = MS_FALSE;
1280
0
      int numFeatures = -1;
1281
1282
0
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1283
0
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
1284
      /* Optimization to detect the case where a WFS query uses in fact the */
1285
      /* whole layer extent, but expressed in a map SRS different from the layer
1286
       * SRS */
1287
      /* In the case, we can directly request against the layer extent in its
1288
       * native SRS */
1289
0
      if (lp->project &&
1290
0
          memcmp(&searchrect, &invalid_rect, sizeof(searchrect)) != 0 &&
1291
0
          msProjectionsDiffer(&(lp->projection), &(map->projection))) {
1292
0
        rectObj layerExtent;
1293
0
        if (msOWSGetLayerExtent(map, lp, "FO", &layerExtent) == MS_SUCCESS) {
1294
0
          rectObj ext = layerExtent;
1295
0
          ext.minx -= 1e-5;
1296
0
          ext.miny -= 1e-5;
1297
0
          ext.maxx += 1e-5;
1298
0
          ext.maxy += 1e-5;
1299
0
          msProjectRect(&(lp->projection), &(map->projection), &ext);
1300
0
          if (fabs(ext.minx - searchrect.minx) <= 2e-5 &&
1301
0
              fabs(ext.miny - searchrect.miny) <= 2e-5 &&
1302
0
              fabs(ext.maxx - searchrect.maxx) <= 2e-5 &&
1303
0
              fabs(ext.maxy - searchrect.maxy) <= 2e-5) {
1304
0
            bUseLayerSRS = MS_TRUE;
1305
0
            numFeatures =
1306
0
                msLayerGetShapeCount(lp, layerExtent, &(lp->projection));
1307
0
          }
1308
0
        }
1309
0
      }
1310
0
#endif
1311
1312
0
      if (!bUseLayerSRS)
1313
0
        numFeatures = msLayerGetShapeCount(lp, searchrect, &(map->projection));
1314
0
      if (numFeatures >= 0) {
1315
0
        lp->resultcache = (resultCacheObj *)malloc(sizeof(
1316
0
            resultCacheObj)); /* allocate and initialize the result cache */
1317
0
        MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
1318
0
        initResultCache(lp->resultcache);
1319
0
        lp->resultcache->numresults = numFeatures;
1320
0
        if (!paging && map->query.startindex > 1) {
1321
0
          lp->resultcache->numresults -= (map->query.startindex - 1);
1322
0
        }
1323
0
        msFreeShape(&searchshape);
1324
0
        continue;
1325
0
      }
1326
      // Fallback in case of error (should not happen normally)
1327
0
    }
1328
1329
0
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
1330
0
    if (lp->project &&
1331
0
        memcmp(&searchrect, &invalid_rect, sizeof(searchrect)) != 0)
1332
0
      msProjectRect(&(map->projection), &(lp->projection),
1333
0
                    &searchrect); /* project the searchrect to source coords */
1334
1335
0
    status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
1336
0
    if (status == MS_DONE) { /* no overlap */
1337
0
      msLayerClose(lp);
1338
0
      continue;
1339
0
    } else if (status != MS_SUCCESS) {
1340
0
      msLayerClose(lp);
1341
0
      msFreeShape(&searchshape);
1342
0
      return (MS_FAILURE);
1343
0
    }
1344
1345
0
    lp->resultcache = (resultCacheObj *)malloc(
1346
0
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
1347
0
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
1348
0
    initResultCache(lp->resultcache);
1349
1350
0
    nclasses = 0;
1351
0
    classgroup = NULL;
1352
0
    if (lp->classgroup && lp->numclasses > 0)
1353
0
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
1354
1355
0
    if (lp->minfeaturesize > 0)
1356
0
      minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
1357
1358
0
    while ((status = msLayerNextShape(lp, &shape)) ==
1359
0
           MS_SUCCESS) { /* step through the shapes */
1360
1361
      /* Check if the shape size is ok to be drawn */
1362
0
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
1363
0
          (minfeaturesize > 0)) {
1364
0
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
1365
0
          if (lp->debug >= MS_DEBUGLEVEL_V)
1366
0
            msDebug("msQueryByRect(): Skipping shape (%ld) because "
1367
0
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
1368
0
                    shape.index);
1369
0
          msFreeShape(&shape);
1370
0
          continue;
1371
0
        }
1372
0
      }
1373
1374
0
      shape.classindex = msShapeGetClass(lp, map, &shape, classgroup, nclasses);
1375
0
      if (!(lp->template) && ((shape.classindex == -1) ||
1376
0
                              (lp->class[shape.classindex]
1377
0
                               -> status == MS_OFF))) { /* not a valid shape */
1378
0
        msFreeShape(&shape);
1379
0
        continue;
1380
0
      }
1381
1382
0
      if (!(lp->template) &&
1383
0
          !(lp->class[shape.classindex] -> template)) { /* no valid template */
1384
0
        msFreeShape(&shape);
1385
0
        continue;
1386
0
      }
1387
1388
0
      if (lp->project) {
1389
0
        if (reprojector == NULL) {
1390
0
          reprojector =
1391
0
              msProjectCreateReprojector(&(lp->projection), &(map->projection));
1392
0
          if (reprojector == NULL) {
1393
0
            msFreeShape(&shape);
1394
0
            status = MS_FAILURE;
1395
0
            break;
1396
0
          }
1397
0
        }
1398
0
        if (msProjectShapeEx(reprojector, &shape) != MS_SUCCESS) {
1399
          // msProjectShapeEx() calls msFreeShape() on error
1400
0
          continue;
1401
0
        }
1402
0
      }
1403
1404
0
      if (msRectContained(&shape.bounds, &searchrectInMapProj) ==
1405
0
          MS_TRUE) { /* if the whole shape is in, don't intersect */
1406
0
        status = MS_TRUE;
1407
0
      } else {
1408
0
        switch (shape.type) { /* make sure shape actually intersects the qrect
1409
                                 (ADD FUNCTIONS SPECIFIC TO RECTOBJ) */
1410
0
        case MS_SHAPE_POINT:
1411
0
          status = msIntersectMultipointPolygon(&shape, &searchshape);
1412
0
          break;
1413
0
        case MS_SHAPE_LINE:
1414
0
          status = msIntersectPolylinePolygon(&shape, &searchshape);
1415
0
          break;
1416
0
        case MS_SHAPE_POLYGON:
1417
0
          status = msIntersectPolygons(&shape, &searchshape);
1418
0
          break;
1419
0
        case MS_SHAPE_NULL:
1420
0
          status = MS_TRUE;
1421
0
          break;
1422
0
        default:
1423
0
          break;
1424
0
        }
1425
0
      }
1426
1427
0
      if (status == MS_TRUE) {
1428
        /* Should we skip this feature? */
1429
0
        if (!paging && map->query.startindex > 1) {
1430
0
          --map->query.startindex;
1431
0
          msFreeShape(&shape);
1432
0
          continue;
1433
0
        }
1434
0
        if (map->query.only_cache_result_count)
1435
0
          lp->resultcache->numresults++;
1436
0
        else
1437
0
          addResult(map, lp->resultcache, &queryCache, &shape);
1438
0
        --map->query.maxfeatures;
1439
0
      }
1440
0
      msFreeShape(&shape);
1441
1442
      /* check shape count */
1443
0
      if (lp->maxfeatures > 0 &&
1444
0
          lp->maxfeatures == lp->resultcache->numresults) {
1445
0
        status = MS_DONE;
1446
0
        break;
1447
0
      }
1448
1449
0
    } /* next shape */
1450
1451
0
    if (classgroup)
1452
0
      msFree(classgroup);
1453
1454
0
    msProjectDestroyReprojector(reprojector);
1455
1456
0
    if (status != MS_DONE) {
1457
0
      msFreeShape(&searchshape);
1458
0
      return (MS_FAILURE);
1459
0
    }
1460
1461
0
    if (!map->query.only_cache_result_count && lp->resultcache->numresults == 0)
1462
0
      msLayerClose(lp); /* no need to keep the layer open */
1463
0
  }                     /* next layer */
1464
1465
0
  msFreeShape(&searchshape);
1466
1467
  /* was anything found? */
1468
0
  for (l = start; l >= stop; l--) {
1469
0
    if (GET_LAYER(map, l)->resultcache &&
1470
0
        GET_LAYER(map, l)->resultcache->numresults > 0)
1471
0
      return (MS_SUCCESS);
1472
0
  }
1473
1474
0
  if (map->debug >= MS_DEBUGLEVEL_V) {
1475
0
    msDebug("msQueryByRect(): No matching record(s) found.\n");
1476
0
  }
1477
0
  return (MS_SUCCESS);
1478
0
}
1479
1480
static int is_duplicate(resultCacheObj *resultcache, int shapeindex,
1481
0
                        int tileindex) {
1482
0
  int i;
1483
1484
0
  for (i = 0; i < resultcache->numresults; i++)
1485
0
    if (resultcache->results[i].shapeindex == shapeindex &&
1486
0
        resultcache->results[i].tileindex == tileindex)
1487
0
      return (MS_TRUE);
1488
1489
0
  return (MS_FALSE);
1490
0
}
1491
1492
0
int msQueryByFeatures(mapObj *map) {
1493
0
  int i, l;
1494
0
  int start, stop = 0;
1495
0
  layerObj *lp, *slp;
1496
0
  char status;
1497
1498
0
  double distance, tolerance, layer_tolerance;
1499
1500
0
  rectObj searchrect;
1501
0
  shapeObj shape, selectshape;
1502
0
  int nclasses = 0;
1503
0
  int *classgroup = NULL;
1504
0
  double minfeaturesize = -1;
1505
1506
0
  queryCacheObj queryCache;
1507
1508
0
  initQueryCache(&queryCache);
1509
1510
  /* is the selection layer valid and has it been queried */
1511
0
  if (map->query.slayer < 0 || map->query.slayer >= map->numlayers) {
1512
0
    msSetError(MS_QUERYERR, "Invalid selection layer index.",
1513
0
               "msQueryByFeatures()");
1514
0
    return (MS_FAILURE);
1515
0
  }
1516
0
  slp = (GET_LAYER(map, map->query.slayer));
1517
0
  if (!slp->resultcache) {
1518
0
    msSetError(MS_QUERYERR, "Selection layer has not been queried.",
1519
0
               "msQueryByFeatures()");
1520
0
    return (MS_FAILURE);
1521
0
  }
1522
1523
  /* conditions may have changed since this layer last drawn, so set
1524
     layer->project true to recheck projection needs (Bug #673) */
1525
0
  slp->project = msProjectionsDiffer(&(slp->projection), &(map->projection));
1526
1527
0
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
1528
0
    start = map->numlayers - 1;
1529
0
  else
1530
0
    start = stop = map->query.layer;
1531
1532
  /* selection layers should already be open */
1533
  /* status = msLayerOpen(slp);
1534
  if(status != MS_SUCCESS) return(MS_FAILURE); */
1535
1536
0
  msInitShape(&shape); /* initialize a few things */
1537
0
  msInitShape(&selectshape);
1538
1539
0
  for (l = start; l >= stop; l--) {
1540
0
    reprojectionObj *reprojector = NULL;
1541
0
    if (l == map->query.slayer)
1542
0
      continue; /* skip the selection layer */
1543
1544
0
    lp = (GET_LAYER(map, l));
1545
0
    if (map->query.maxfeatures == 0)
1546
0
      break; /* nothing else to do */
1547
0
    else if (map->query.maxfeatures > 0)
1548
0
      lp->maxfeatures = map->query.maxfeatures;
1549
1550
    /* using mapscript, the map->query.startindex will be unset... */
1551
0
    if (lp->startindex > 1 && map->query.startindex < 0)
1552
0
      map->query.startindex = lp->startindex;
1553
1554
    /* conditions may have changed since this layer last drawn, so set
1555
       layer->project true to recheck projection needs (Bug #673) */
1556
0
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
1557
1558
    /* free any previous search results, do it now in case one of the next few
1559
     * tests fail */
1560
0
    if (lp->resultcache) {
1561
0
      if (lp->resultcache->results)
1562
0
        free(lp->resultcache->results);
1563
0
      free(lp->resultcache);
1564
0
      lp->resultcache = NULL;
1565
0
    }
1566
1567
0
    if (!msIsLayerQueryable(lp))
1568
0
      continue;
1569
0
    if (lp->status == MS_OFF)
1570
0
      continue;
1571
1572
0
    if (map->scaledenom > 0) {
1573
0
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
1574
0
        continue;
1575
0
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
1576
0
        continue;
1577
0
    }
1578
1579
0
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
1580
0
      if ((lp->maxgeowidth > 0) &&
1581
0
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
1582
0
        continue;
1583
0
      if ((lp->mingeowidth > 0) &&
1584
0
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
1585
0
        continue;
1586
0
    }
1587
1588
    /* Get the layer tolerance default is 3 for point and line layers, 0 for
1589
     * others */
1590
0
    if (lp->tolerance == -1) {
1591
0
      if (lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE)
1592
0
        layer_tolerance = 3;
1593
0
      else
1594
0
        layer_tolerance = 0;
1595
0
    } else
1596
0
      layer_tolerance = lp->tolerance;
1597
1598
0
    if (lp->toleranceunits == MS_PIXELS)
1599
0
      tolerance = layer_tolerance *
1600
0
                  msAdjustExtent(&(map->extent), map->width, map->height);
1601
0
    else
1602
0
      tolerance = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
1603
0
                                     msInchesPerUnit(map->units, 0));
1604
1605
0
    msLayerClose(lp); /* reset */
1606
0
    status = msLayerOpen(lp);
1607
0
    if (status != MS_SUCCESS)
1608
0
      return (MS_FAILURE);
1609
0
    msLayerEnablePaging(lp, MS_FALSE);
1610
1611
    /* build item list, we want *all* items */
1612
0
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
1613
0
    if (status != MS_SUCCESS)
1614
0
      return (MS_FAILURE);
1615
1616
    /* for each selection shape */
1617
0
    for (i = 0; i < slp->resultcache->numresults; i++) {
1618
1619
0
      status =
1620
0
          msLayerGetShape(slp, &selectshape, &(slp->resultcache->results[i]));
1621
0
      if (status != MS_SUCCESS) {
1622
0
        msLayerClose(lp);
1623
0
        msLayerClose(slp);
1624
0
        return (MS_FAILURE);
1625
0
      }
1626
1627
0
      if (selectshape.type != MS_SHAPE_POLYGON &&
1628
0
          selectshape.type != MS_SHAPE_LINE) {
1629
0
        msLayerClose(lp);
1630
0
        msLayerClose(slp);
1631
0
        msSetError(MS_QUERYERR, "Selection features MUST be polygons or lines.",
1632
0
                   "msQueryByFeatures()");
1633
0
        return (MS_FAILURE);
1634
0
      }
1635
1636
0
      if (slp->project)
1637
0
        msProjectShape(&(slp->projection), &(map->projection), &selectshape);
1638
1639
      /* identify target shapes */
1640
0
      searchrect = selectshape.bounds;
1641
1642
0
      searchrect.minx -=
1643
0
          tolerance; /* expand the search box to account for layer tolerances
1644
                        (e.g. buffered searches) */
1645
0
      searchrect.maxx += tolerance;
1646
0
      searchrect.miny -= tolerance;
1647
0
      searchrect.maxy += tolerance;
1648
1649
0
      if (lp->project)
1650
0
        msProjectRect(
1651
0
            &(map->projection), &(lp->projection),
1652
0
            &searchrect); /* project the searchrect to source coords */
1653
1654
0
      status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
1655
0
      if (status == MS_DONE) { /* no overlap */
1656
0
        msLayerClose(lp);
1657
0
        break; /* next layer */
1658
0
      } else if (status != MS_SUCCESS) {
1659
0
        msLayerClose(lp);
1660
0
        msLayerClose(slp);
1661
0
        return (MS_FAILURE);
1662
0
      }
1663
1664
0
      if (i == 0) {
1665
0
        lp->resultcache = (resultCacheObj *)malloc(sizeof(
1666
0
            resultCacheObj)); /* allocate and initialize the result cache */
1667
0
        MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
1668
0
        initResultCache(lp->resultcache);
1669
0
      }
1670
1671
0
      nclasses = 0;
1672
0
      classgroup = NULL;
1673
0
      if (lp->classgroup && lp->numclasses > 0)
1674
0
        classgroup = msAllocateValidClassGroups(lp, &nclasses);
1675
1676
0
      if (lp->minfeaturesize > 0)
1677
0
        minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
1678
1679
0
      while ((status = msLayerNextShape(lp, &shape)) ==
1680
0
             MS_SUCCESS) { /* step through the shapes */
1681
1682
        /* check for dups when there are multiple selection shapes */
1683
0
        if (i > 0 &&
1684
0
            is_duplicate(lp->resultcache, shape.index, shape.tileindex))
1685
0
          continue;
1686
1687
        /* Check if the shape size is ok to be drawn */
1688
0
        if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
1689
0
            (minfeaturesize > 0)) {
1690
0
          if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
1691
0
            if (lp->debug >= MS_DEBUGLEVEL_V)
1692
0
              msDebug("msQueryByFeature(): Skipping shape (%ld) because "
1693
0
                      "LAYER::MINFEATURESIZE is bigger than shape size\n",
1694
0
                      shape.index);
1695
0
            msFreeShape(&shape);
1696
0
            continue;
1697
0
          }
1698
0
        }
1699
1700
0
        shape.classindex =
1701
0
            msShapeGetClass(lp, map, &shape, classgroup, nclasses);
1702
0
        if (!(lp->template) &&
1703
0
            ((shape.classindex == -1) ||
1704
0
             (lp->class[shape.classindex]
1705
0
              -> status == MS_OFF))) { /* not a valid shape */
1706
0
          msFreeShape(&shape);
1707
0
          continue;
1708
0
        }
1709
1710
0
        if (!(lp->template) && !(lp->class[shape.classindex]
1711
0
                                 -> template)) { /* no valid template */
1712
0
          msFreeShape(&shape);
1713
0
          continue;
1714
0
        }
1715
1716
0
        if (lp->project) {
1717
0
          if (reprojector == NULL) {
1718
0
            reprojector = msProjectCreateReprojector(&(lp->projection),
1719
0
                                                     &(map->projection));
1720
0
            if (reprojector == NULL) {
1721
0
              msFreeShape(&shape);
1722
0
              status = MS_FAILURE;
1723
0
              break;
1724
0
            }
1725
0
          }
1726
0
          msProjectShapeEx(reprojector, &shape);
1727
0
        }
1728
1729
0
        switch (selectshape.type) { /* may eventually support types other than
1730
                                       polygon on line */
1731
0
        case MS_SHAPE_POLYGON:
1732
0
          switch (shape.type) { /* make sure shape actually intersects the
1733
                                   selectshape */
1734
0
          case MS_SHAPE_POINT:
1735
0
            if (tolerance == 0) /* just test for intersection */
1736
0
              status = msIntersectMultipointPolygon(&shape, &selectshape);
1737
0
            else { /* check distance, distance=0 means they intersect */
1738
0
              distance = msDistanceShapeToShape(&selectshape, &shape);
1739
0
              if (distance < tolerance)
1740
0
                status = MS_TRUE;
1741
0
            }
1742
0
            break;
1743
0
          case MS_SHAPE_LINE:
1744
0
            if (tolerance == 0) { /* just test for intersection */
1745
0
              status = msIntersectPolylinePolygon(&shape, &selectshape);
1746
0
            } else { /* check distance, distance=0 means they intersect */
1747
0
              distance = msDistanceShapeToShape(&selectshape, &shape);
1748
0
              if (distance < tolerance)
1749
0
                status = MS_TRUE;
1750
0
            }
1751
0
            break;
1752
0
          case MS_SHAPE_POLYGON:
1753
0
            if (tolerance == 0) /* just test for intersection */
1754
0
              status = msIntersectPolygons(&shape, &selectshape);
1755
0
            else { /* check distance, distance=0 means they intersect */
1756
0
              distance = msDistanceShapeToShape(&selectshape, &shape);
1757
0
              if (distance < tolerance)
1758
0
                status = MS_TRUE;
1759
0
            }
1760
0
            break;
1761
0
          default:
1762
0
            status = MS_FALSE;
1763
0
            break;
1764
0
          }
1765
0
          break;
1766
0
        case MS_SHAPE_LINE:
1767
0
          switch (shape.type) { /* make sure shape actually intersects the
1768
                                   selectshape */
1769
0
          case MS_SHAPE_POINT:
1770
0
            if (tolerance == 0) { /* just test for intersection */
1771
0
              distance = msDistanceShapeToShape(&selectshape, &shape);
1772
0
              if (distance == 0)
1773
0
                status = MS_TRUE;
1774
0
            } else {
1775
0
              distance = msDistanceShapeToShape(&selectshape, &shape);
1776
0
              if (distance < tolerance)
1777
0
                status = MS_TRUE;
1778
0
            }
1779
0
            break;
1780
0
          case MS_SHAPE_LINE:
1781
0
            if (tolerance == 0) { /* just test for intersection */
1782
0
              status = msIntersectPolylines(&shape, &selectshape);
1783
0
            } else { /* check distance, distance=0 means they intersect */
1784
0
              distance = msDistanceShapeToShape(&selectshape, &shape);
1785
0
              if (distance < tolerance)
1786
0
                status = MS_TRUE;
1787
0
            }
1788
0
            break;
1789
0
          case MS_SHAPE_POLYGON:
1790
0
            if (tolerance == 0) /* just test for intersection */
1791
0
              status = msIntersectPolylinePolygon(&selectshape, &shape);
1792
0
            else { /* check distance, distance=0 means they intersect */
1793
0
              distance = msDistanceShapeToShape(&selectshape, &shape);
1794
0
              if (distance < tolerance)
1795
0
                status = MS_TRUE;
1796
0
            }
1797
0
            break;
1798
0
          default:
1799
0
            status = MS_FALSE;
1800
0
            break;
1801
0
          }
1802
0
          break;
1803
0
        default:
1804
0
          break; /* should never get here as we test for selection shape type
1805
                    explicitly earlier */
1806
0
        }
1807
1808
0
        if (status == MS_TRUE) {
1809
          /* Should we skip this feature? */
1810
0
          if (!msLayerGetPaging(lp) && map->query.startindex > 1) {
1811
0
            --map->query.startindex;
1812
0
            msFreeShape(&shape);
1813
0
            continue;
1814
0
          }
1815
0
          addResult(map, lp->resultcache, &queryCache, &shape);
1816
0
        }
1817
0
        msFreeShape(&shape);
1818
1819
        /* check shape count */
1820
0
        if (lp->maxfeatures > 0 &&
1821
0
            lp->maxfeatures == lp->resultcache->numresults) {
1822
0
          status = MS_DONE;
1823
0
          break;
1824
0
        }
1825
0
      } /* next shape */
1826
1827
0
      if (classgroup)
1828
0
        msFree(classgroup);
1829
1830
0
      msProjectDestroyReprojector(reprojector);
1831
1832
0
      msFreeShape(&selectshape);
1833
1834
0
      if (status != MS_DONE)
1835
0
        return (MS_FAILURE);
1836
1837
0
    } /* next selection shape */
1838
1839
0
    if (lp->resultcache == NULL || lp->resultcache->numresults == 0)
1840
0
      msLayerClose(lp); /* no need to keep the layer open */
1841
0
  }                     /* next layer */
1842
1843
  /* was anything found? */
1844
0
  for (l = start; l >= stop; l--) {
1845
0
    if (l == map->query.slayer)
1846
0
      continue; /* skip the selection layer */
1847
0
    if (GET_LAYER(map, l)->resultcache &&
1848
0
        GET_LAYER(map, l)->resultcache->numresults > 0)
1849
0
      return (MS_SUCCESS);
1850
0
  }
1851
1852
0
  if (map->debug >= MS_DEBUGLEVEL_V) {
1853
0
    msDebug("msQueryByFeatures(): No matching record(s) found.\n");
1854
0
  }
1855
0
  return (MS_SUCCESS);
1856
0
}
1857
1858
/* msQueryByPoint()
1859
 *
1860
 * With mode=MS_QUERY_SINGLE:
1861
 *   Set maxresults = 0 to have a single result across all layers (the closest
1862
 *     shape from the first layer that finds a match).
1863
 *   Set maxresults = 1 to have up to one result per layer (the closest shape
1864
 *     from each layer).
1865
 *
1866
 * With mode=MS_QUERY_MULTIPLE:
1867
 *   Set maxresults = 0 to have an unlimited number of results.
1868
 *   Set maxresults > 0 to limit the number of results per layer (the shapes
1869
 *     returned are the first ones found in each layer and are not necessarily
1870
 *     the closest ones).
1871
 */
1872
0
int msQueryByPoint(mapObj *map) {
1873
0
  int l;
1874
0
  int start, stop = 0;
1875
1876
0
  double d, t;
1877
0
  double layer_tolerance;
1878
1879
0
  layerObj *lp;
1880
1881
0
  int paging;
1882
0
  char status;
1883
0
  rectObj rect, searchrect;
1884
0
  shapeObj shape;
1885
0
  int nclasses = 0;
1886
0
  int *classgroup = NULL;
1887
0
  double minfeaturesize = -1;
1888
1889
0
  queryCacheObj queryCache;
1890
1891
0
  initQueryCache(&queryCache);
1892
1893
0
  if (map->query.type != MS_QUERY_BY_POINT) {
1894
0
    msSetError(MS_QUERYERR, "The query is not properly defined.",
1895
0
               "msQueryByPoint()");
1896
0
    return (MS_FAILURE);
1897
0
  }
1898
1899
0
  msInitShape(&shape);
1900
1901
0
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
1902
0
    start = map->numlayers - 1;
1903
0
  else
1904
0
    start = stop = map->query.layer;
1905
1906
0
  for (l = start; l >= stop; l--) {
1907
0
    reprojectionObj *reprojector = NULL;
1908
0
    lp = (GET_LAYER(map, l));
1909
0
    if (map->query.maxfeatures == 0)
1910
0
      break; /* nothing else to do */
1911
0
    else if (map->query.maxfeatures > 0)
1912
0
      lp->maxfeatures = map->query.maxfeatures;
1913
1914
    /* using mapscript, the map->query.startindex will be unset... */
1915
0
    if (lp->startindex > 1 && map->query.startindex < 0)
1916
0
      map->query.startindex = lp->startindex;
1917
1918
    /* conditions may have changed since this layer last drawn, so set
1919
       layer->project true to recheck projection needs (Bug #673) */
1920
0
    lp->project = MS_TRUE;
1921
1922
    /* free any previous search results, do it now in case one of the next few
1923
     * tests fail */
1924
0
    if (lp->resultcache) {
1925
0
      if (lp->resultcache->results)
1926
0
        free(lp->resultcache->results);
1927
0
      free(lp->resultcache);
1928
0
      lp->resultcache = NULL;
1929
0
    }
1930
1931
0
    if (!msIsLayerQueryable(lp))
1932
0
      continue;
1933
0
    if (lp->status == MS_OFF)
1934
0
      continue;
1935
1936
0
    if (map->scaledenom > 0) {
1937
0
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
1938
0
        continue;
1939
0
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
1940
0
        continue;
1941
0
    }
1942
1943
0
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
1944
0
      if ((lp->maxgeowidth > 0) &&
1945
0
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
1946
0
        continue;
1947
0
      if ((lp->mingeowidth > 0) &&
1948
0
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
1949
0
        continue;
1950
0
    }
1951
1952
    /* Raster layers are handled specially.  */
1953
0
    if (lp->type == MS_LAYER_RASTER) {
1954
0
      if (msRasterQueryByPoint(map, lp, map->query.mode, map->query.point,
1955
0
                               map->query.buffer,
1956
0
                               map->query.maxresults) == MS_FAILURE)
1957
0
        return MS_FAILURE;
1958
0
      continue;
1959
0
    }
1960
1961
    /* Get the layer tolerance default is 3 for point and line layers, 0 for
1962
     * others */
1963
0
    if (lp->tolerance == -1) {
1964
0
      if (lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE)
1965
0
        layer_tolerance = 3;
1966
0
      else
1967
0
        layer_tolerance = 0;
1968
0
    } else
1969
0
      layer_tolerance = lp->tolerance;
1970
1971
0
    if (map->query.buffer <= 0) { /* use layer tolerance */
1972
0
      if (lp->toleranceunits == MS_PIXELS)
1973
0
        t = layer_tolerance *
1974
0
            MS_MAX(
1975
0
                MS_CELLSIZE(map->extent.minx, map->extent.maxx, map->width),
1976
0
                MS_CELLSIZE(map->extent.miny, map->extent.maxy, map->height));
1977
0
      else
1978
0
        t = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
1979
0
                               msInchesPerUnit(map->units, 0));
1980
0
    } else /* use buffer distance */
1981
0
      t = map->query.buffer;
1982
1983
0
    rect.minx = map->query.point.x - t;
1984
0
    rect.maxx = map->query.point.x + t;
1985
0
    rect.miny = map->query.point.y - t;
1986
0
    rect.maxy = map->query.point.y + t;
1987
1988
    /* Paging could have been disabled before */
1989
0
    paging = msLayerGetPaging(lp);
1990
0
    msLayerClose(lp); /* reset */
1991
0
    status = msLayerOpen(lp);
1992
0
    if (status != MS_SUCCESS)
1993
0
      return (MS_FAILURE);
1994
0
    msLayerEnablePaging(lp, paging);
1995
1996
    /* build item list, we want *all* items */
1997
0
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
1998
0
    if (status != MS_SUCCESS)
1999
0
      return (MS_FAILURE);
2000
2001
    /* identify target shapes */
2002
0
    searchrect = rect;
2003
0
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
2004
0
    if (lp->project)
2005
0
      msProjectRect(&(map->projection), &(lp->projection),
2006
0
                    &searchrect); /* project the searchrect to source coords */
2007
2008
0
    status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
2009
0
    if (status == MS_DONE) { /* no overlap */
2010
0
      msLayerClose(lp);
2011
0
      continue;
2012
0
    } else if (status != MS_SUCCESS) {
2013
0
      msLayerClose(lp);
2014
0
      return (MS_FAILURE);
2015
0
    }
2016
2017
0
    lp->resultcache = (resultCacheObj *)malloc(
2018
0
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
2019
0
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
2020
0
    initResultCache(lp->resultcache);
2021
2022
0
    nclasses = 0;
2023
0
    classgroup = NULL;
2024
0
    if (lp->classgroup && lp->numclasses > 0)
2025
0
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
2026
2027
0
    if (lp->minfeaturesize > 0)
2028
0
      minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
2029
2030
0
    while ((status = msLayerNextShape(lp, &shape)) ==
2031
0
           MS_SUCCESS) { /* step through the shapes */
2032
2033
      /* Check if the shape size is ok to be drawn */
2034
0
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
2035
0
          (minfeaturesize > 0)) {
2036
0
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
2037
0
          if (lp->debug >= MS_DEBUGLEVEL_V)
2038
0
            msDebug("msQueryByPoint(): Skipping shape (%ld) because "
2039
0
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
2040
0
                    shape.index);
2041
0
          msFreeShape(&shape);
2042
0
          continue;
2043
0
        }
2044
0
      }
2045
2046
0
      shape.classindex = msShapeGetClass(lp, map, &shape, classgroup, nclasses);
2047
0
      if (!(lp->template) && ((shape.classindex == -1) ||
2048
0
                              (lp->class[shape.classindex]
2049
0
                               -> status == MS_OFF))) { /* not a valid shape */
2050
0
        msFreeShape(&shape);
2051
0
        continue;
2052
0
      }
2053
2054
0
      if (!(lp->template) &&
2055
0
          !(lp->class[shape.classindex] -> template)) { /* no valid template */
2056
0
        msFreeShape(&shape);
2057
0
        continue;
2058
0
      }
2059
2060
0
      if (lp->project) {
2061
0
        if (reprojector == NULL) {
2062
0
          reprojector =
2063
0
              msProjectCreateReprojector(&(lp->projection), &(map->projection));
2064
0
          if (reprojector == NULL) {
2065
0
            msFreeShape(&shape);
2066
0
            status = MS_FAILURE;
2067
0
            break;
2068
0
          }
2069
0
        }
2070
0
        msProjectShapeEx(reprojector, &shape);
2071
0
      }
2072
2073
0
      d = msDistancePointToShape(&(map->query.point), &shape);
2074
0
      if (d <= t) { /* found one */
2075
2076
        /* Should we skip this feature? */
2077
0
        if (!paging && map->query.startindex > 1) {
2078
0
          --map->query.startindex;
2079
0
          msFreeShape(&shape);
2080
0
          continue;
2081
0
        }
2082
2083
0
        if (map->query.mode == MS_QUERY_SINGLE) {
2084
0
          cleanupResultCache(lp->resultcache);
2085
0
          initQueryCache(&queryCache);
2086
0
          addResult(map, lp->resultcache, &queryCache, &shape);
2087
0
          t = d; /* next one must be closer */
2088
0
        } else {
2089
0
          addResult(map, lp->resultcache, &queryCache, &shape);
2090
0
        }
2091
0
      }
2092
2093
0
      msFreeShape(&shape);
2094
2095
0
      if (map->query.mode == MS_QUERY_MULTIPLE && map->query.maxresults > 0 &&
2096
0
          lp->resultcache->numresults == map->query.maxresults) {
2097
0
        status = MS_DONE; /* got enough results for this layer */
2098
0
        break;
2099
0
      }
2100
2101
      /* check shape count */
2102
0
      if (lp->maxfeatures > 0 &&
2103
0
          lp->maxfeatures == lp->resultcache->numresults) {
2104
0
        status = MS_DONE;
2105
0
        break;
2106
0
      }
2107
0
    } /* next shape */
2108
2109
0
    if (classgroup)
2110
0
      msFree(classgroup);
2111
2112
0
    msProjectDestroyReprojector(reprojector);
2113
2114
0
    if (status != MS_DONE)
2115
0
      return (MS_FAILURE);
2116
2117
0
    if (lp->resultcache->numresults == 0)
2118
0
      msLayerClose(lp); /* no need to keep the layer open */
2119
2120
0
    if ((lp->resultcache->numresults > 0) &&
2121
0
        (map->query.mode == MS_QUERY_SINGLE) && (map->query.maxresults == 0))
2122
0
      break; /* no need to search any further */
2123
0
  }          /* next layer */
2124
2125
  /* was anything found? */
2126
0
  for (l = start; l >= stop; l--) {
2127
0
    if (GET_LAYER(map, l)->resultcache &&
2128
0
        GET_LAYER(map, l)->resultcache->numresults > 0)
2129
0
      return (MS_SUCCESS);
2130
0
  }
2131
2132
0
  if (map->debug >= MS_DEBUGLEVEL_V) {
2133
0
    msDebug("msQueryByPoint(): No matching record(s) found.\n");
2134
0
  }
2135
0
  return (MS_SUCCESS);
2136
0
}
2137
2138
0
int msQueryByShape(mapObj *map) {
2139
0
  int start, stop = 0, l;
2140
0
  shapeObj shape, *qshape = NULL;
2141
0
  layerObj *lp;
2142
0
  char status;
2143
0
  double distance, tolerance, layer_tolerance;
2144
0
  rectObj searchrect;
2145
2146
0
  int nclasses = 0;
2147
0
  int *classgroup = NULL;
2148
0
  double minfeaturesize = -1;
2149
0
  queryCacheObj queryCache;
2150
2151
0
  initQueryCache(&queryCache);
2152
2153
0
  if (map->query.type != MS_QUERY_BY_SHAPE) {
2154
0
    msSetError(MS_QUERYERR, "The query is not properly defined.",
2155
0
               "msQueryByShape()");
2156
0
    return (MS_FAILURE);
2157
0
  }
2158
2159
0
  if (!(map->query.shape)) {
2160
0
    msSetError(MS_QUERYERR, "Query shape is not defined.", "msQueryByShape()");
2161
0
    return (MS_FAILURE);
2162
0
  }
2163
0
  if (map->query.shape->type != MS_SHAPE_POLYGON &&
2164
0
      map->query.shape->type != MS_SHAPE_LINE &&
2165
0
      map->query.shape->type != MS_SHAPE_POINT) {
2166
0
    msSetError(MS_QUERYERR, "Query shape MUST be a polygon, line or point.",
2167
0
               "msQueryByShape()");
2168
0
    return (MS_FAILURE);
2169
0
  }
2170
2171
0
  msInitShape(&shape);
2172
0
  qshape = map->query.shape; /* for brevity */
2173
2174
0
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
2175
0
    start = map->numlayers - 1;
2176
0
  else
2177
0
    start = stop = map->query.layer;
2178
2179
0
  msComputeBounds(qshape); /* make sure an accurate extent exists */
2180
2181
0
  for (l = start; l >= stop; l--) { /* each layer */
2182
0
    reprojectionObj *reprojector = NULL;
2183
0
    lp = (GET_LAYER(map, l));
2184
0
    if (map->query.maxfeatures == 0)
2185
0
      break; /* nothing else to do */
2186
0
    else if (map->query.maxfeatures > 0)
2187
0
      lp->maxfeatures = map->query.maxfeatures;
2188
2189
    /* using mapscript, the map->query.startindex will be unset... */
2190
0
    if (lp->startindex > 1 && map->query.startindex < 0)
2191
0
      map->query.startindex = lp->startindex;
2192
2193
    /* conditions may have changed since this layer last drawn, so set
2194
       layer->project true to recheck projection needs (Bug #673) */
2195
0
    lp->project = MS_TRUE;
2196
2197
    /* free any previous search results, do it now in case one of the next few
2198
     * tests fail */
2199
0
    if (lp->resultcache) {
2200
0
      if (lp->resultcache->results)
2201
0
        free(lp->resultcache->results);
2202
0
      free(lp->resultcache);
2203
0
      lp->resultcache = NULL;
2204
0
    }
2205
2206
0
    if (!msIsLayerQueryable(lp))
2207
0
      continue;
2208
0
    if (lp->status == MS_OFF)
2209
0
      continue;
2210
2211
0
    if (map->scaledenom > 0) {
2212
0
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
2213
0
        continue;
2214
0
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
2215
0
        continue;
2216
0
    }
2217
2218
0
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
2219
0
      if ((lp->maxgeowidth > 0) &&
2220
0
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
2221
0
        continue;
2222
0
      if ((lp->mingeowidth > 0) &&
2223
0
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
2224
0
        continue;
2225
0
    }
2226
2227
    /* Raster layers are handled specially. */
2228
0
    if (lp->type == MS_LAYER_RASTER) {
2229
0
      if (msRasterQueryByShape(map, lp, qshape) == MS_FAILURE)
2230
0
        return MS_FAILURE;
2231
0
      continue;
2232
0
    }
2233
2234
    /* Get the layer tolerance default is 3 for point and line layers, 0 for
2235
     * others */
2236
0
    if (lp->tolerance == -1) {
2237
0
      if (lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE)
2238
0
        layer_tolerance = 3;
2239
0
      else
2240
0
        layer_tolerance = 0;
2241
0
    } else
2242
0
      layer_tolerance = lp->tolerance;
2243
2244
0
    if (lp->toleranceunits == MS_PIXELS)
2245
0
      tolerance = layer_tolerance *
2246
0
                  msAdjustExtent(&(map->extent), map->width, map->height);
2247
0
    else
2248
0
      tolerance = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
2249
0
                                     msInchesPerUnit(map->units, 0));
2250
2251
0
    msLayerClose(lp); /* reset */
2252
0
    status = msLayerOpen(lp);
2253
0
    if (status != MS_SUCCESS)
2254
0
      return (MS_FAILURE);
2255
    /* disable driver paging */
2256
0
    msLayerEnablePaging(lp, MS_FALSE);
2257
2258
    /* build item list, we want *all* items */
2259
0
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
2260
0
    if (status != MS_SUCCESS)
2261
0
      return (MS_FAILURE);
2262
2263
    /* identify target shapes */
2264
0
    searchrect = qshape->bounds;
2265
2266
0
    searchrect.minx -= tolerance; /* expand the search box to account for layer
2267
                                     tolerances (e.g. buffered searches) */
2268
0
    searchrect.maxx += tolerance;
2269
0
    searchrect.miny -= tolerance;
2270
0
    searchrect.maxy += tolerance;
2271
2272
0
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
2273
0
    if (lp->project)
2274
0
      msProjectRect(&(map->projection), &(lp->projection),
2275
0
                    &searchrect); /* project the searchrect to source coords */
2276
2277
0
    status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
2278
0
    if (status == MS_DONE) { /* no overlap */
2279
0
      msLayerClose(lp);
2280
0
      continue;
2281
0
    } else if (status != MS_SUCCESS) {
2282
0
      msLayerClose(lp);
2283
0
      return (MS_FAILURE);
2284
0
    }
2285
2286
0
    lp->resultcache = (resultCacheObj *)malloc(
2287
0
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
2288
0
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
2289
0
    initResultCache(lp->resultcache);
2290
2291
0
    nclasses = 0;
2292
0
    if (lp->classgroup && lp->numclasses > 0)
2293
0
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
2294
2295
0
    if (lp->minfeaturesize > 0)
2296
0
      minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
2297
2298
0
    while ((status = msLayerNextShape(lp, &shape)) ==
2299
0
           MS_SUCCESS) { /* step through the shapes */
2300
2301
      /* Check if the shape size is ok to be drawn */
2302
0
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
2303
0
          (minfeaturesize > 0)) {
2304
0
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
2305
0
          if (lp->debug >= MS_DEBUGLEVEL_V)
2306
0
            msDebug("msQueryByShape(): Skipping shape (%ld) because "
2307
0
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
2308
0
                    shape.index);
2309
0
          msFreeShape(&shape);
2310
0
          continue;
2311
0
        }
2312
0
      }
2313
2314
0
      shape.classindex = msShapeGetClass(lp, map, &shape, classgroup, nclasses);
2315
0
      if (!(lp->template) && ((shape.classindex == -1) ||
2316
0
                              (lp->class[shape.classindex]
2317
0
                               -> status == MS_OFF))) { /* not a valid shape */
2318
0
        msFreeShape(&shape);
2319
0
        continue;
2320
0
      }
2321
2322
0
      if (!(lp->template) &&
2323
0
          !(lp->class[shape.classindex] -> template)) { /* no valid template */
2324
0
        msFreeShape(&shape);
2325
0
        continue;
2326
0
      }
2327
2328
0
      if (lp->project) {
2329
0
        if (reprojector == NULL) {
2330
0
          reprojector =
2331
0
              msProjectCreateReprojector(&(lp->projection), &(map->projection));
2332
0
          if (reprojector == NULL) {
2333
0
            msFreeShape(&shape);
2334
0
            status = MS_FAILURE;
2335
0
            break;
2336
0
          }
2337
0
        }
2338
0
        msProjectShapeEx(reprojector, &shape);
2339
0
      }
2340
2341
0
      switch (qshape->type) { /* may eventually support types other than polygon
2342
                                 or line */
2343
0
      case MS_SHAPE_POLYGON:
2344
0
        switch (
2345
0
            shape.type) { /* make sure shape actually intersects the shape */
2346
0
        case MS_SHAPE_POINT:
2347
0
          if (tolerance == 0) /* just test for intersection */
2348
0
            status = msIntersectMultipointPolygon(&shape, qshape);
2349
0
          else { /* check distance, distance=0 means they intersect */
2350
0
            distance = msDistanceShapeToShape(qshape, &shape);
2351
0
            if (distance < tolerance)
2352
0
              status = MS_TRUE;
2353
0
          }
2354
0
          break;
2355
0
        case MS_SHAPE_LINE:
2356
0
          if (tolerance == 0) { /* just test for intersection */
2357
0
            status = msIntersectPolylinePolygon(&shape, qshape);
2358
0
          } else { /* check distance, distance=0 means they intersect */
2359
0
            distance = msDistanceShapeToShape(qshape, &shape);
2360
0
            if (distance < tolerance)
2361
0
              status = MS_TRUE;
2362
0
          }
2363
0
          break;
2364
0
        case MS_SHAPE_POLYGON:
2365
0
          if (tolerance == 0) /* just test for intersection */
2366
0
            status = msIntersectPolygons(&shape, qshape);
2367
0
          else { /* check distance, distance=0 means they intersect */
2368
0
            distance = msDistanceShapeToShape(qshape, &shape);
2369
0
            if (distance < tolerance)
2370
0
              status = MS_TRUE;
2371
0
          }
2372
0
          break;
2373
0
        default:
2374
0
          break;
2375
0
        }
2376
0
        break;
2377
0
      case MS_SHAPE_LINE:
2378
0
        switch (shape.type) { /* make sure shape actually intersects the
2379
                                 selectshape */
2380
0
        case MS_SHAPE_POINT:
2381
0
          if (tolerance == 0) { /* just test for intersection */
2382
0
            distance = msDistanceShapeToShape(qshape, &shape);
2383
0
            if (distance == 0)
2384
0
              status = MS_TRUE;
2385
0
          } else {
2386
0
            distance = msDistanceShapeToShape(qshape, &shape);
2387
0
            if (distance < tolerance)
2388
0
              status = MS_TRUE;
2389
0
          }
2390
0
          break;
2391
0
        case MS_SHAPE_LINE:
2392
0
          if (tolerance == 0) { /* just test for intersection */
2393
0
            status = msIntersectPolylines(&shape, qshape);
2394
0
          } else { /* check distance, distance=0 means they intersect */
2395
0
            distance = msDistanceShapeToShape(qshape, &shape);
2396
0
            if (distance < tolerance)
2397
0
              status = MS_TRUE;
2398
0
          }
2399
0
          break;
2400
0
        case MS_SHAPE_POLYGON:
2401
0
          if (tolerance == 0) /* just test for intersection */
2402
0
            status = msIntersectPolylinePolygon(qshape, &shape);
2403
0
          else { /* check distance, distance=0 means they intersect */
2404
0
            distance = msDistanceShapeToShape(qshape, &shape);
2405
0
            if (distance < tolerance)
2406
0
              status = MS_TRUE;
2407
0
          }
2408
0
          break;
2409
0
        default:
2410
0
          status = MS_FALSE;
2411
0
          break;
2412
0
        }
2413
0
        break;
2414
0
      case MS_SHAPE_POINT:
2415
0
        distance = msDistanceShapeToShape(qshape, &shape);
2416
0
        status = MS_FALSE;
2417
0
        if (tolerance == 0 && distance == 0)
2418
0
          status = MS_TRUE; /* shapes intersect */
2419
0
        else if (distance < tolerance)
2420
0
          status = MS_TRUE; /* shapes are close enough */
2421
0
        break;
2422
0
      default:
2423
0
        break; /* should never get here as we test for selection shape type
2424
                  explicitly earlier */
2425
0
      }
2426
2427
0
      if (status == MS_TRUE) {
2428
        /* Should we skip this feature? */
2429
0
        if (!msLayerGetPaging(lp) && map->query.startindex > 1) {
2430
0
          --map->query.startindex;
2431
0
          msFreeShape(&shape);
2432
0
          continue;
2433
0
        }
2434
0
        addResult(map, lp->resultcache, &queryCache, &shape);
2435
0
      }
2436
0
      msFreeShape(&shape);
2437
2438
      /* check shape count */
2439
0
      if (lp->maxfeatures > 0 &&
2440
0
          lp->maxfeatures == lp->resultcache->numresults) {
2441
0
        status = MS_DONE;
2442
0
        break;
2443
0
      }
2444
0
    } /* next shape */
2445
2446
0
    free(classgroup);
2447
0
    classgroup = NULL;
2448
2449
0
    msProjectDestroyReprojector(reprojector);
2450
2451
0
    if (status != MS_DONE) {
2452
0
      return (MS_FAILURE);
2453
0
    }
2454
2455
0
    if (lp->resultcache->numresults == 0)
2456
0
      msLayerClose(lp); /* no need to keep the layer open */
2457
0
  }                     /* next layer */
2458
2459
  /* was anything found? */
2460
0
  for (l = start; l >= stop; l--) {
2461
0
    if (GET_LAYER(map, l)->resultcache &&
2462
0
        GET_LAYER(map, l)->resultcache->numresults > 0)
2463
0
      return (MS_SUCCESS);
2464
0
  }
2465
2466
0
  if (map->debug >= MS_DEBUGLEVEL_V) {
2467
0
    msDebug("msQueryByShape(): No matching record(s) found.\n");
2468
0
  }
2469
0
  return (MS_SUCCESS);
2470
0
}
2471
2472
/* msGetQueryResultBounds()
2473
 *
2474
 * Compute the BBOX of all query results, returns the number of layers found
2475
 * that contained query results and were included in the BBOX.
2476
 * i.e. if we return 0 then the value in bounds is invalid.
2477
 */
2478
0
int msGetQueryResultBounds(mapObj *map, rectObj *bounds) {
2479
0
  int i, found = 0;
2480
0
  rectObj tmpBounds;
2481
2482
0
  for (i = 0; i < map->numlayers; i++) {
2483
2484
0
    layerObj *lp;
2485
0
    lp = (GET_LAYER(map, i));
2486
2487
0
    if (!lp->resultcache)
2488
0
      continue;
2489
0
    if (lp->resultcache->numresults <= 0)
2490
0
      continue;
2491
2492
0
    tmpBounds = lp->resultcache->bounds;
2493
2494
0
    if (found == 0) {
2495
0
      *bounds = tmpBounds;
2496
0
    } else {
2497
0
      msMergeRect(bounds, &tmpBounds);
2498
0
    }
2499
2500
0
    found++;
2501
0
  }
2502
2503
0
  return found;
2504
0
}
2505
2506
/* TODO: Rename this msFreeResultSet() or something along those lines... */
2507
/* msQueryFree()
2508
 *
2509
 * Free layer's query results. If qlayer == -1, all layers will be treated.
2510
 */
2511
0
void msQueryFree(mapObj *map, int qlayer) {
2512
0
  int l; /* counters */
2513
0
  int start, stop = 0;
2514
0
  layerObj *lp;
2515
2516
0
  if (qlayer < 0 || qlayer >= map->numlayers)
2517
0
    start = map->numlayers - 1;
2518
0
  else
2519
0
    start = stop = qlayer;
2520
2521
0
  for (l = start; l >= stop; l--) {
2522
0
    lp = (GET_LAYER(map, l));
2523
2524
0
    if (lp->resultcache) {
2525
0
      if (lp->resultcache->results)
2526
0
        free(lp->resultcache->results);
2527
0
      free(lp->resultcache);
2528
0
      lp->resultcache = NULL;
2529
0
    }
2530
0
  }
2531
0
}