• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MapServer / MapServer / 19469494661

18 Nov 2025 02:23PM UTC coverage: 40.415% (-1.3%) from 41.715%
19469494661

push

github

jmckenna
update for 8.6.0-beta2 release

60832 of 150520 relevant lines covered (40.41%)

24909.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.27
/src/mapquery.cpp
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
#include "mapquery.h"
33
#include "maprendering.h"
34

35
#include "limits.h"
36

37
#include <algorithm>
38
#include <memory>
39
#include <vector>
40

41
/* This object is used by the various mapQueryXXXXX() functions. It stores
42
 * the total amount of shapes and their RAM footprint, when they are cached
43
 * in the resultCacheObj* of layers. This is the total number across all
44
 * queried layers. However this is isn't persistent across several calls to
45
 * mapQueryXXXXX(), if the resultCacheObj* objects weren't cleaned up. This
46
 * is not needed in the context of WFS, for which this is used for now.
47
 */
48
typedef struct {
49
  int cachedShapeCountWarningEmitted;
50
  int cachedShapeCount;
51
  int cachedShapeRAMWarningEmitted;
52
  int cachedShapeRAM;
53
} queryCacheObj;
54

55
int msInitQuery(queryObj *query) {
3,613✔
56
  if (!query)
3,613✔
57
    return MS_FAILURE;
58

59
  msFreeQuery(query); /* clean up anything previously allocated */
3,613✔
60

61
  query->type = MS_QUERY_IS_NULL; /* nothing defined */
3,613✔
62
  query->mode = MS_QUERY_SINGLE;
3,613✔
63

64
  query->layer = query->slayer = -1;
3,613✔
65

66
  query->point.x = query->point.y = -1;
3,613✔
67
  query->buffer = 0.0;
3,613✔
68
  query->maxresults = 0; /* only used by msQueryByPoint() apparently */
3,613✔
69

70
  query->rect.minx = query->rect.miny = query->rect.maxx = query->rect.maxy =
3,613✔
71
      -1;
72
  query->shape = NULL;
3,613✔
73

74
  query->shapeindex = query->tileindex = -1;
3,613✔
75
  query->clear_resultcache =
3,613✔
76
      MS_TRUE; /* index queries allow the old results to persist */
77

78
  query->maxfeatures = -1;
3,613✔
79
  query->startindex = -1;
3,613✔
80
  query->only_cache_result_count = 0;
3,613✔
81

82
  query->filteritem = NULL;
3,613✔
83
  msInitExpression(&query->filter);
3,613✔
84

85
  query->cache_shapes = MS_FALSE;
3,613✔
86
  query->max_cached_shape_count = 0;
3,613✔
87
  query->max_cached_shape_ram_amount = 0;
3,613✔
88

89
  query->getFeatureInfo = new getFeatureInfoObj();
3,613✔
90

91
  return MS_SUCCESS;
3,613✔
92
}
93

94
void msFreeQuery(queryObj *query) {
6,785✔
95
  if (query->shape) {
6,785✔
96
    msFreeShape(query->shape);
13✔
97
    free(query->shape);
13✔
98
  }
99

100
  if (query->filteritem)
6,785✔
101
    free(query->filteritem);
15✔
102
  msFreeExpression(&query->filter);
6,785✔
103

104
  delete query->getFeatureInfo;
10,389✔
105
  query->getFeatureInfo = nullptr;
6,785✔
106
}
6,785✔
107

108
/*
109
** Wrapper for all query functions, just feed is a mapObj with a populated
110
*queryObj...
111
*/
112
int msExecuteQuery(mapObj *map) {
114✔
113
  int tmp = -1, status;
114

115
  /* handle slayer/qlayer management for feature queries */
116
  if (map->query.slayer >= 0) {
114✔
117
    tmp = map->query.layer;
4✔
118
    map->query.layer = map->query.slayer;
4✔
119
  }
120

121
  switch (map->query.type) {
114✔
122
  case MS_QUERY_BY_POINT:
27✔
123
    status = msQueryByPoint(map);
27✔
124
    break;
27✔
125
  case MS_QUERY_BY_RECT:
58✔
126
    status = msQueryByRect(map);
58✔
127
    break;
58✔
128
  case MS_QUERY_BY_FILTER:
17✔
129
    status = msQueryByFilter(map);
17✔
130
    break;
17✔
131
  case MS_QUERY_BY_SHAPE:
8✔
132
    status = msQueryByShape(map);
8✔
133
    break;
8✔
134
  case MS_QUERY_BY_INDEX:
4✔
135
    status = msQueryByIndex(map);
4✔
136
    break;
4✔
137
  default:
×
138
    msSetError(MS_QUERYERR, "Malformed queryObj.", "msExecuteQuery()");
×
139
    return (MS_FAILURE);
×
140
  }
141

142
  if (map->query.slayer >= 0) {
114✔
143
    map->query.layer = tmp; /* restore layer */
4✔
144
    if (status == MS_SUCCESS)
4✔
145
      status = msQueryByFeatures(map);
4✔
146
  }
147

148
  return status;
149
}
150

151
/*
152
** msIsLayerQueryable()  returns MS_TRUE/MS_FALSE
153
*/
154
int msIsLayerQueryable(layerObj *lp) {
1,804✔
155
  int i;
156

157
  if (lp->type == MS_LAYER_TILEINDEX)
1,804✔
158
    return MS_FALSE;
159

160
  if (lp->_template && strlen(lp->_template) > 0)
1,804✔
161
    return MS_TRUE;
162

163
  for (i = 0; i < lp->numclasses; i++) {
608✔
164
    if (lp->_class[i]->_template && strlen(lp->_class[i]->_template) > 0)
275✔
165
      return MS_TRUE;
166
  }
167

168
  return MS_FALSE;
169
}
170

171
static void initQueryCache(queryCacheObj *queryCache) {
172
  queryCache->cachedShapeCountWarningEmitted = MS_FALSE;
933✔
173
  queryCache->cachedShapeCount = 0;
933✔
174
  queryCache->cachedShapeRAMWarningEmitted = MS_FALSE;
933✔
175
  queryCache->cachedShapeRAM = 0;
933✔
176
}
177

178
/** Check whether we should store the shape in resultCacheObj* given the
179
 * limits allowed in map->query.max_cached_shape_count and
180
 * map->query.max_cached_shape_ram_amount.
181
 */
182
static int canCacheShape(mapObj *map, queryCacheObj *queryCache,
4,096✔
183
                         int shape_ram_size) {
184
  if (!map->query.cache_shapes)
4,096✔
185
    return MS_FALSE;
186
  if (queryCache->cachedShapeCountWarningEmitted ||
44✔
187
      (map->query.max_cached_shape_count > 0 &&
16✔
188
       queryCache->cachedShapeCount >= map->query.max_cached_shape_count)) {
16✔
189
    if (!queryCache->cachedShapeCountWarningEmitted) {
32✔
190
      queryCache->cachedShapeCountWarningEmitted = MS_TRUE;
4✔
191
      if (map->debug >= MS_DEBUGLEVEL_V) {
4✔
192
        msDebug("map->query.max_cached_shape_count = %d reached. "
×
193
                "Next features will not be cached.\n",
194
                map->query.max_cached_shape_count);
195
      }
196
    }
197
    return MS_FALSE;
32✔
198
  }
199
  if (queryCache->cachedShapeRAMWarningEmitted ||
12✔
200
      (map->query.max_cached_shape_ram_amount > 0 &&
12✔
201
       queryCache->cachedShapeRAM + shape_ram_size >
12✔
202
           map->query.max_cached_shape_ram_amount)) {
203
    if (!queryCache->cachedShapeRAMWarningEmitted) {
×
204
      queryCache->cachedShapeRAMWarningEmitted = MS_TRUE;
×
205
      if (map->debug >= MS_DEBUGLEVEL_V) {
×
206
        msDebug("map->query.max_cached_shape_ram_amount = %d reached after %d "
×
207
                "cached features. "
208
                "Next features will not be cached.\n",
209
                map->query.max_cached_shape_ram_amount,
210
                queryCache->cachedShapeCount);
211
      }
212
    }
213
    return MS_FALSE;
×
214
  }
215
  return MS_TRUE;
216
}
217

218
static int addResult(mapObj *map, resultCacheObj *cache,
4,096✔
219
                     queryCacheObj *queryCache, shapeObj *shape) {
220
  int i;
221
  int shape_ram_size = (map->query.max_cached_shape_ram_amount > 0)
4,096✔
222
                           ? msGetShapeRAMSize(shape)
4,096✔
223
                           : 0;
224
  int store_shape = canCacheShape(map, queryCache, shape_ram_size);
4,096✔
225

226
  if (cache->numresults == cache->cachesize) { /* just add it to the end */
4,096✔
227
    if (cache->cachesize == 0)
968✔
228
      cache->results =
714✔
229
          (resultObj *)malloc(sizeof(resultObj) * MS_RESULTCACHEINCREMENT);
714✔
230
    else
231
      cache->results = (resultObj *)realloc(
254✔
232
          cache->results,
254✔
233
          sizeof(resultObj) * (cache->cachesize + MS_RESULTCACHEINCREMENT));
254✔
234
    if (!cache->results) {
968✔
235
      msSetError(MS_MEMERR, "Realloc() error.", "addResult()");
×
236
      return (MS_FAILURE);
×
237
    }
238
    cache->cachesize += MS_RESULTCACHEINCREMENT;
968✔
239
  }
240

241
  i = cache->numresults;
4,096✔
242

243
  cache->results[i].classindex = shape->classindex;
4,096✔
244
  cache->results[i].tileindex = shape->tileindex;
4,096✔
245
  cache->results[i].shapeindex = shape->index;
4,096✔
246
  cache->results[i].resultindex = shape->resultindex;
4,096✔
247
  if (store_shape) {
4,096✔
248
    cache->results[i].shape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
12✔
249
    msInitShape(cache->results[i].shape);
12✔
250
    msCopyShape(shape, cache->results[i].shape);
12✔
251
    queryCache->cachedShapeCount++;
12✔
252
    queryCache->cachedShapeRAM += shape_ram_size;
12✔
253
  } else
254
    cache->results[i].shape = NULL;
4,084✔
255
  cache->numresults++;
4,096✔
256

257
  cache->previousBounds = cache->bounds;
4,096✔
258
  if (cache->numresults == 1)
4,096✔
259
    cache->bounds = shape->bounds;
714✔
260
  else
261
    msMergeRect(&(cache->bounds), &(shape->bounds));
3,382✔
262

263
  return (MS_SUCCESS);
264
}
265

266
/*
267
** Serialize a query result set to disk.
268
*/
269
static int saveQueryResults(mapObj *map, char *filename) {
×
270
  FILE *stream;
271
  int i, j, n = 0;
×
272

273
  if (!filename) {
×
274
    msSetError(MS_MISCERR, "No filename provided to save query results to.",
×
275
               "saveQueryResults()");
276
    return MS_FAILURE;
×
277
  }
278

279
  stream = fopen(filename, "w");
×
280
  if (!stream) {
×
281
    msSetError(MS_IOERR, "(%s)", "saveQueryResults()", filename);
×
282
    return MS_FAILURE;
×
283
  }
284

285
  fprintf(stream, "%s - Generated by msSaveQuery()\n",
286
          MS_QUERY_RESULTS_MAGIC_STRING);
287

288
  /* count the number of layers with results */
289
  for (i = 0; i < map->numlayers; i++)
×
290
    if (GET_LAYER(map, i)->resultcache)
×
291
      n++;
×
292
  fwrite(&n, sizeof(int), 1, stream);
×
293

294
  /* now write the result set for each layer */
295
  for (i = 0; i < map->numlayers; i++) {
×
296
    if (GET_LAYER(map, i)->resultcache) {
×
297
      fwrite(&i, sizeof(int), 1, stream); /* layer index */
×
298
      fwrite(&(GET_LAYER(map, i)->resultcache->numresults), sizeof(int), 1,
×
299
             stream); /* number of results */
300
      fwrite(&(GET_LAYER(map, i)->resultcache->bounds), sizeof(rectObj), 1,
×
301
             stream); /* bounding box */
302
      for (j = 0; j < GET_LAYER(map, i)->resultcache->numresults; j++)
×
303
        fwrite(&(GET_LAYER(map, i)->resultcache->results[j]), sizeof(resultObj),
×
304
               1, stream); /* each result */
305
    }
306
  }
307

308
  fclose(stream);
×
309
  return MS_SUCCESS;
310
}
311

312
static int loadQueryResults(mapObj *map, FILE *stream) {
×
313
  int i, j, k, n = 0;
×
314

315
  if (1 != fread(&n, sizeof(int), 1, stream) || n > INT_MAX - 1) {
×
316
    msSetError(MS_MISCERR, "failed to read query count from query file stream",
×
317
               "loadQueryResults()");
318
    return MS_FAILURE;
×
319
  }
320

321
  /* now load the result set for each layer found in the query file */
322
  for (i = 0; i < n; i++) {
×
323
    if (1 != fread(&j, sizeof(int), 1, stream)) { /* layer index */
×
324
      msSetError(MS_MISCERR,
×
325
                 "failed to read layer index from query file stream",
326
                 "loadQueryResults()");
327
      return MS_FAILURE;
×
328
    }
329

330
    if (j < 0 || j > map->numlayers) {
×
331
      msSetError(MS_MISCERR, "Invalid layer index loaded from query file.",
×
332
                 "loadQueryResults()");
333
      return MS_FAILURE;
×
334
    }
335

336
    /* inialize the results for this layer */
337
    GET_LAYER(map, j)->resultcache = (resultCacheObj *)malloc(
×
338
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
339
    MS_CHECK_ALLOC(GET_LAYER(map, j)->resultcache, sizeof(resultCacheObj),
×
340
                   MS_FAILURE);
341

342
    if (1 != fread(&(GET_LAYER(map, j)->resultcache->numresults), sizeof(int),
×
343
                   1, stream) ||
×
344
        (GET_LAYER(map, j)->resultcache->numresults <
×
345
         0)) { /* number of results */
346
      msSetError(MS_MISCERR,
×
347
                 "failed to read number of results from query file stream",
348
                 "loadQueryResults()");
349
      free(GET_LAYER(map, j)->resultcache);
×
350
      GET_LAYER(map, j)->resultcache = NULL;
×
351
      return MS_FAILURE;
×
352
    }
353
    GET_LAYER(map, j)->resultcache->cachesize =
×
354
        GET_LAYER(map, j)->resultcache->numresults;
355

356
    if (1 != fread(&(GET_LAYER(map, j)->resultcache->bounds), sizeof(rectObj),
×
357
                   1, stream)) { /* bounding box */
358
      msSetError(MS_MISCERR, "failed to read bounds from query file stream",
×
359
                 "loadQueryResults()");
360
      free(GET_LAYER(map, j)->resultcache);
×
361
      GET_LAYER(map, j)->resultcache = NULL;
×
362
      return MS_FAILURE;
×
363
    }
364

365
    GET_LAYER(map, j)->resultcache->results = (resultObj *)malloc(
×
366
        sizeof(resultObj) * GET_LAYER(map, j)->resultcache->numresults);
×
367
    if (GET_LAYER(map, j)->resultcache->results == NULL) {
×
368
      msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
×
369
                 "loadQueryResults()", __FILE__, __LINE__,
370
                 (unsigned int)(sizeof(resultObj) *
371
                                GET_LAYER(map, j)->resultcache->numresults));
372
      free(GET_LAYER(map, j)->resultcache);
×
373
      GET_LAYER(map, j)->resultcache = NULL;
×
374
      return MS_FAILURE;
×
375
    }
376

377
    for (k = 0; k < GET_LAYER(map, j)->resultcache->numresults; k++) {
×
378
      if (1 != fread(&(GET_LAYER(map, j)->resultcache->results[k]),
×
379
                     sizeof(resultObj), 1, stream)) { /* each result */
380
        msSetError(MS_MISCERR,
×
381
                   "failed to read result %d from query file stream",
382
                   "loadQueryResults()", k);
383
        free(GET_LAYER(map, j)->resultcache->results);
×
384
        free(GET_LAYER(map, j)->resultcache);
×
385
        GET_LAYER(map, j)->resultcache = NULL;
×
386
        return MS_FAILURE;
×
387
      }
388
      if (!GET_LAYER(map, j)->tileindex)
×
389
        GET_LAYER(map, j)->resultcache->results[k].tileindex =
×
390
            -1; /* reset the tile index for non-tiled layers */
391
      GET_LAYER(map, j)->resultcache->results[k].resultindex =
×
392
          -1; /* all results loaded this way have a -1 result (set) index */
393
    }
394
  }
395

396
  return MS_SUCCESS;
397
}
398

399
/*
400
** Serialize the parameters necessary to duplicate a query to disk. (TODO: add
401
*filter query...)
402
*/
403
static int saveQueryParams(mapObj *map, char *filename) {
1✔
404
  FILE *stream;
405

406
  if (!filename) {
1✔
407
    msSetError(MS_MISCERR, "No filename provided to save query to.",
×
408
               "saveQueryParams()");
409
    return MS_FAILURE;
×
410
  }
411

412
  stream = fopen(filename, "w");
1✔
413
  if (!stream) {
1✔
414
    msSetError(MS_IOERR, "(%s)", "saveQueryParams()", filename);
×
415
    return MS_FAILURE;
×
416
  }
417

418
  fprintf(stream, "%s - Generated by msSaveQuery()\n",
419
          MS_QUERY_PARAMS_MAGIC_STRING);
420

421
  fprintf(stream, "%d %d %d %d\n", map->query.mode, map->query.type,
1✔
422
          map->query.layer, map->query.slayer); /* all queries */
423
  fprintf(stream, "%.15g %.15g %g %d\n", map->query.point.x, map->query.point.y,
1✔
424
          map->query.buffer, map->query.maxresults); /* by point */
425
  fprintf(stream, "%.15g %.15g %.15g %.15g\n", map->query.rect.minx,
1✔
426
          map->query.rect.miny, map->query.rect.maxx,
427
          map->query.rect.maxy); /* by rect */
428
  fprintf(stream, "%ld %ld %d\n", map->query.shapeindex, map->query.tileindex,
1✔
429
          map->query.clear_resultcache); /* by index */
430

431
  fprintf(stream, "%s\n",
1✔
432
          (map->query.filteritem) ? map->query.filteritem
1✔
433
                                  : "NULL"); /* by filter */
434
  fprintf(stream, "%s\n",
1✔
435
          (map->query.filter.string) ? map->query.filter.string : "NULL");
1✔
436

437
  if (map->query.shape) { /* by shape */
1✔
438
    int i, j;
439
    shapeObj *s = map->query.shape;
440

441
    fprintf(stream, "%d\n", s->type);
×
442
    fprintf(stream, "%d\n", s->numlines);
×
443
    for (i = 0; i < s->numlines; i++) {
×
444
      fprintf(stream, "%d\n", s->line[i].numpoints);
×
445
      for (j = 0; j < s->line[i].numpoints; j++)
×
446
        fprintf(stream, "%.15g %.15g\n", s->line[i].point[j].x,
×
447
                s->line[i].point[j].y);
×
448
    }
449
  } else {
450
    fprintf(stream, "%d\n", MS_SHAPE_NULL); /* NULL shape */
451
  }
452

453
  fclose(stream);
1✔
454
  return MS_SUCCESS;
1✔
455
}
456

457
static int loadQueryParams(mapObj *map, FILE *stream) {
1✔
458
  char buffer[MS_BUFFER_LENGTH];
459
  int lineno;
460

461
  int shapetype, numlines, numpoints;
462

463
  msInitQuery(&(map->query)); /* this will free any previous query as well */
1✔
464

465
  lineno = 2; /* line 1 is the magic string */
466
  while (fgets(buffer, MS_BUFFER_LENGTH, stream) != NULL) {
8✔
467
    switch (lineno) {
7✔
468
    case 2:
1✔
469
      if (sscanf(buffer, "%d %d %d %d\n", &(map->query.mode),
1✔
470
                 &(map->query.type), &(map->query.layer),
471
                 &(map->query.slayer)) != 4)
472
        goto parse_error;
×
473
      break;
474
    case 3:
1✔
475
      if (sscanf(buffer, "%lf %lf %lf %d\n", &map->query.point.x,
1✔
476
                 &map->query.point.y, &map->query.buffer,
477
                 &map->query.maxresults) != 4)
478
        goto parse_error;
×
479
      break;
480
    case 4:
1✔
481
      if (sscanf(buffer, "%lf %lf %lf %lf\n", &map->query.rect.minx,
1✔
482
                 &map->query.rect.miny, &map->query.rect.maxx,
483
                 &map->query.rect.maxy) != 4)
484
        goto parse_error;
×
485
      break;
486
    case 5:
1✔
487
      if (sscanf(buffer, "%ld %ld %d\n", &map->query.shapeindex,
1✔
488
                 &map->query.tileindex, &map->query.clear_resultcache) != 3)
489
        goto parse_error;
×
490
      break;
491
    case 6:
1✔
492
      if (strncmp(buffer, "NULL", 4) != 0) {
1✔
493
        map->query.filteritem = msStrdup(buffer);
×
494
        msStringChop(map->query.filteritem);
×
495
      }
496
      break;
497
    case 7:
1✔
498
      if (strncmp(buffer, "NULL", 4) != 0)
1✔
499
        msLoadExpressionString(&map->query.filter, buffer); /* chop buffer */
×
500
      break;
501
    case 8:
1✔
502
      if (sscanf(buffer, "%d\n", &shapetype) != 1)
1✔
503
        goto parse_error;
×
504

505
      if (shapetype != MS_SHAPE_NULL) { /* load the rest of the shape */
1✔
506
        int i, j;
507
        lineObj line;
508

509
        map->query.shape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
×
510
        msInitShape(map->query.shape);
×
511
        map->query.shape->type = shapetype;
×
512

513
        if (fscanf(stream, "%d\n", &numlines) != 1)
×
514
          goto parse_error;
×
515
        if (numlines > INT_MAX - 1)
×
516
          goto parse_error;
×
517
        for (i = 0; i < numlines; i++) {
×
518
          if (fscanf(stream, "%d\n", &numpoints) != 1 || numpoints < 0 ||
×
519
              numpoints > INT_MAX / (int)sizeof(pointObj))
520
            goto parse_error;
×
521

522
          line.numpoints = numpoints;
×
523
          line.point =
×
524
              (pointObj *)msSmallMalloc(line.numpoints * sizeof(pointObj));
×
525

526
          for (j = 0; j < numpoints; j++)
×
527
            if (fscanf(stream, "%lf %lf\n", &line.point[j].x,
×
528
                       &line.point[j].y) != 2)
×
529
              goto parse_error;
×
530

531
          msAddLine(map->query.shape, &line);
×
532
          free(line.point);
×
533
        }
534
      }
535
      break;
536
    default:
537
      break;
538
    }
539

540
    lineno++;
7✔
541
  }
542

543
  /* TODO: should we throw an error if lineno != 10? */
544

545
  /* force layer and slayer on */
546
  if (map->query.layer >= 0 && map->query.layer < map->numlayers)
1✔
547
    GET_LAYER(map, map->query.layer)->status = MS_ON;
1✔
548
  if (map->query.slayer >= 0 && map->query.slayer < map->numlayers)
1✔
549
    GET_LAYER(map, map->query.slayer)->status = MS_ON;
×
550

551
  /* now execute the query */
552
  return msExecuteQuery(map);
1✔
553

554
parse_error:
×
555
  msSetError(MS_MISCERR, "Parse error line %d.", "loadQueryParameters()",
×
556
             lineno);
557
  return MS_FAILURE;
558
}
559

560
/*
561
** Save (serialize) a query to disk. There are two methods, one saves the query
562
*parameters and the other saves
563
** all the shape indexes. Note the latter can be very slow against certain data
564
*sources but has a certain usefulness
565
** on occation.
566
*/
567
int msSaveQuery(mapObj *map, char *filename, int results) {
1✔
568
  if (results)
1✔
569
    return saveQueryResults(map, filename);
×
570
  else
571
    return saveQueryParams(map, filename);
1✔
572
}
573

574
/*
575
** Loads a query file contained either serialized 1) query parameters or 2)
576
*query results (e.g. indexes).
577
*/
578
int msLoadQuery(mapObj *map, char *filename) {
1✔
579
  FILE *stream;
580
  char buffer[MS_BUFFER_LENGTH];
581
  int retval = MS_FAILURE;
582

583
  if (!filename) {
1✔
584
    msSetError(MS_MISCERR, "No filename provided to load query from.",
×
585
               "msLoadQuery()");
586
    return MS_FAILURE;
×
587
  }
588

589
  /*
590
  ** Make sure the file at least has the right extension.
591
  */
592
  if (msEvalRegex("\\.qy$", filename) != MS_TRUE) {
1✔
593
    msSetError(MS_MISCERR, "Queryfile %s has incorrect file extension.",
×
594
               "msLoadQuery()", filename);
595
    return MS_FAILURE;
×
596
  }
597

598
  /*
599
  ** Open the file and inspect the first line.
600
  */
601
  stream = fopen(filename, "r");
1✔
602
  if (!stream) {
1✔
603
    msSetError(MS_IOERR, "(%s)", "msLoadQuery()", filename);
×
604
    return MS_FAILURE;
×
605
  }
606

607
  if (fgets(buffer, MS_BUFFER_LENGTH, stream) != NULL) {
1✔
608
    /*
609
    ** Call correct reader based on the magic string.
610
    */
611
    if (strncasecmp(buffer, MS_QUERY_RESULTS_MAGIC_STRING,
1✔
612
                    strlen(MS_QUERY_RESULTS_MAGIC_STRING)) == 0) {
613
      retval = loadQueryResults(map, stream);
×
614
    } else if (strncasecmp(buffer, MS_QUERY_PARAMS_MAGIC_STRING,
1✔
615
                           strlen(MS_QUERY_PARAMS_MAGIC_STRING)) == 0) {
616
      retval = loadQueryParams(map, stream);
1✔
617
    } else {
618
      msSetError(
×
619
          MS_WEBERR,
620
          "Missing magic string, %s doesn't look like a MapServer query file.",
621
          "msLoadQuery()", filename);
622
      retval = MS_FAILURE;
623
    }
624
  } else {
625
    msSetError(MS_WEBERR, "Empty file or failed read for %s.", "msLoadQuery()",
×
626
               filename);
627
    retval = MS_FAILURE;
628
  }
629

630
  fclose(stream);
1✔
631
  return retval;
632
}
633

634
int msQueryByIndex(mapObj *map) {
11✔
635
  layerObj *lp;
636
  int status;
637

638
  resultObj record;
639
  queryCacheObj queryCache;
640

641
  shapeObj shape;
11✔
642
  double minfeaturesize = -1;
643

644
  initQueryCache(&queryCache);
645

646
  if (map->query.type != MS_QUERY_BY_INDEX) {
11✔
647
    msSetError(MS_QUERYERR, "The query is not properly defined.",
×
648
               "msQueryByIndex()");
649
    return (MS_FAILURE);
650
  }
651

652
  if (map->query.layer < 0 || map->query.layer >= map->numlayers) {
11✔
653
    msSetError(MS_QUERYERR, "No query layer defined.", "msQueryByIndex()");
×
654
    return (MS_FAILURE);
655
  }
656

657
  lp = (GET_LAYER(map, map->query.layer));
11✔
658

659
  if (!msIsLayerQueryable(lp)) {
11✔
660
    msSetError(MS_QUERYERR, "Requested layer has no _templates defined.",
×
661
               "msQueryByIndex()");
662
    return (MS_FAILURE);
663
  }
664

665
  if (map->query.clear_resultcache) {
11✔
666
    if (lp->resultcache) {
8✔
667
      cleanupResultCache(lp->resultcache);
×
668
      free(lp->resultcache);
×
669
      lp->resultcache = NULL;
×
670
    }
671
  }
672

673
  msLayerClose(lp); /* reset */
11✔
674
  status = msLayerOpen(lp);
11✔
675
  if (status != MS_SUCCESS)
11✔
676
    return (MS_FAILURE);
677
  /* disable driver paging */
678
  msLayerEnablePaging(lp, MS_FALSE);
11✔
679

680
  /* build item list, we want *all* items */
681
  status = msLayerWhichItems(lp, MS_TRUE, NULL);
11✔
682
  if (status != MS_SUCCESS)
11✔
683
    return (MS_FAILURE);
684

685
  if (map->query.clear_resultcache || lp->resultcache == NULL) {
11✔
686
    lp->resultcache = (resultCacheObj *)malloc(
10✔
687
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
688
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
10✔
689
    initResultCache(lp->resultcache);
10✔
690
  }
691

692
  msInitShape(&shape);
11✔
693

694
  record.resultindex = -1;
11✔
695
  record.shapeindex = map->query.shapeindex;
11✔
696
  record.tileindex = map->query.tileindex;
11✔
697

698
  status = msLayerGetShape(lp, &shape, &record);
11✔
699
  if (status != MS_SUCCESS) {
11✔
700
    msSetError(MS_QUERYERR, "Not valid record request.", "msQueryByIndex()");
×
701
    return (MS_FAILURE);
702
  }
703

704
  /*
705
   * The resultindex is used to retrieve a specific item from the result cache.
706
   * Usually, the row number will be used as resultindex. But when working with
707
   * databases and querying a single result, the row number is typically 0 and
708
   * thus useless as the index in the result cache. See #4926 #4076. Only shape
709
   * files and flatgeobuf are considered to have consistent row numbers.
710
   */
711
  if (!(lp->connectiontype == MS_SHAPEFILE ||
11✔
712
        lp->connectiontype == MS_TILED_SHAPEFILE ||
713
        lp->connectiontype == MS_FLATGEOBUF)) {
714
    shape.resultindex = -1;
3✔
715
  }
716

717
  if (lp->minfeaturesize > 0)
11✔
718
    minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
×
719

720
  /* Check if the shape size is ok to be drawn */
721
  if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
11✔
722
      (minfeaturesize > 0)) {
723

724
    if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
×
725
      msSetError(MS_QUERYERR,
×
726
                 "Requested shape not valid against layer minfeaturesize.",
727
                 "msQueryByIndex()");
728
      msFreeShape(&shape);
×
729
      msLayerClose(lp);
×
730
      return (MS_FAILURE);
731
    }
732
  }
733

734
  shape.classindex = msShapeGetClass(lp, map, &shape, NULL, 0);
11✔
735
  if (!(lp->_template) &&
11✔
736
      ((shape.classindex == -1) || (lp->_class[shape.classindex]->status ==
×
737
                                    MS_OFF))) { /* not a valid shape */
738
    msSetError(
×
739
        MS_QUERYERR,
740
        "Requested shape not valid against layer _classification scheme.",
741
        "msQueryByIndex()");
742
    msFreeShape(&shape);
×
743
    msLayerClose(lp);
×
744
    return (MS_FAILURE);
745
  }
746

747
  if (!(lp->_template) &&
11✔
748
      !(lp->_class[shape.classindex]->_template)) { /* no valid _template */
×
749
    msSetError(MS_QUERYERR,
×
750
               "Requested shape does not have a valid _template, no way to "
751
               "present results.",
752
               "msQueryByIndex()");
753
    msFreeShape(&shape);
×
754
    msLayerClose(lp);
×
755
    return (MS_FAILURE);
756
  }
757

758
  addResult(map, lp->resultcache, &queryCache, &shape);
11✔
759

760
  msFreeShape(&shape);
11✔
761
  /* msLayerClose(lp); */
762

763
  return (MS_SUCCESS);
764
}
11✔
765

766
static char *filterTranslateToLogical(expressionObj *filter, char *filteritem) {
6✔
767
  char *string = NULL;
768

769
  if (filter->type == MS_STRING && filteritem) {
6✔
770
    string = msStrdup("'[");
1✔
771
    string = msStringConcatenate(string, filteritem);
1✔
772
    string = msStringConcatenate(string, "]'");
1✔
773
    if (filter->flags & MS_EXP_INSENSITIVE)
1✔
774
      string = msStringConcatenate(string, " =* '");
×
775
    else
776
      string = msStringConcatenate(string, " = '");
1✔
777
    string = msStringConcatenate(string, filter->string);
1✔
778
    string = msStringConcatenate(string, "'");
1✔
779
  } else if (filter->type == MS_REGEX && filteritem) {
5✔
780
    string = msStrdup("'[");
×
781
    string = msStringConcatenate(string, filteritem);
×
782
    string = msStringConcatenate(string, "]'");
×
783
    if (filter->flags & MS_EXP_INSENSITIVE)
×
784
      string = msStringConcatenate(string, " ~* '");
×
785
    else
786
      string = msStringConcatenate(string, " ~ '");
×
787
    string = msStringConcatenate(string, filter->string);
×
788
    string = msStringConcatenate(string, "'");
×
789
  } else if (filter->type == MS_EXPRESSION) {
5✔
790
    string = msStrdup(filter->string);
5✔
791
  } else {
792
    /* native expression, nothing we can do - sigh */
793
  }
794

795
  return string;
6✔
796
}
797

798
static expressionObj mergeFilters(expressionObj *filter1, char *filteritem1,
3✔
799
                                  expressionObj *filter2, char *filteritem2) {
800
  expressionObj filter;
801

802
  char *tmpstr1 = NULL;
803
  char *tmpstr2 = NULL;
804

805
  msInitExpression(&filter);
3✔
806
  filter.type = MS_EXPRESSION; /* we're building a logical expression */
3✔
807

808
  tmpstr1 = filterTranslateToLogical(filter1, filteritem1);
3✔
809
  if (!tmpstr1)
3✔
810
    return filter; /* should only happen if the filter was a native filter */
811

812
  tmpstr2 = filterTranslateToLogical(filter2, filteritem2);
3✔
813
  if (!tmpstr2) {
3✔
814
    msFree(tmpstr1);
×
815
    return filter; /* should only happen if the filter was a native filter */
×
816
  }
817

818
  filter.string = msStrdup(tmpstr1);
3✔
819
  filter.string = msStringConcatenate(filter.string, " AND ");
3✔
820
  filter.string = msStringConcatenate(filter.string, tmpstr2);
3✔
821

822
  msFree(tmpstr1);
3✔
823
  msFree(tmpstr2);
3✔
824

825
  return filter;
3✔
826
}
827

828
/*
829
** Query using common expression syntax.
830
*/
831
int msQueryByFilter(mapObj *map) {
387✔
832
  int l;
833
  int start, stop = 0;
834

835
  layerObj *lp;
836

837
  char status;
838

839
  char *old_filteritem = NULL;
840
  expressionObj old_filter;
841

842
  rectObj search_rect;
843
  const rectObj invalid_rect = MS_INIT_INVALID_RECT;
387✔
844

845
  shapeObj shape;
387✔
846
  int paging;
847

848
  int nclasses = 0;
387✔
849
  int *classgroup = NULL;
850
  double minfeaturesize = -1;
851
  queryCacheObj queryCache;
852

853
  initQueryCache(&queryCache);
854

855
  if (map->query.type != MS_QUERY_BY_FILTER) {
387✔
856
    msSetError(MS_QUERYERR, "The query is not properly defined.",
×
857
               "msQueryByFilter()");
858
    return (MS_FAILURE);
859
  }
860
  if (!map->query.filter.string) {
387✔
861
    msSetError(MS_QUERYERR, "Filter is not set.", "msQueryByFilter()");
×
862
    return (MS_FAILURE);
863
  }
864

865
  // fprintf(stderr, "in msQueryByFilter: filter=%s, filteritem=%s\n",
866
  // map->query.filter.string, map->query.filteritem);
867

868
  msInitShape(&shape);
387✔
869

870
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
387✔
871
    start = map->numlayers - 1;
×
872
  else
873
    start = stop = map->query.layer;
874

875
  for (l = start; l >= stop; l--) {
772✔
876
    reprojectionObj *reprojector = NULL;
877

878
    lp = (GET_LAYER(map, l));
387✔
879
    if (map->query.maxfeatures == 0)
387✔
880
      break; /* nothing else to do */
881
    else if (map->query.maxfeatures > 0)
387✔
882
      lp->maxfeatures = map->query.maxfeatures;
43✔
883

884
    /* using mapscript, the map->query.startindex will be unset... */
885
    if (lp->startindex > 1 && map->query.startindex < 0)
387✔
886
      map->query.startindex = lp->startindex;
×
887

888
    /* conditions may have changed since this layer last drawn, so set
889
       layer->project true to recheck projection needs (Bug #673) */
890
    lp->project = MS_TRUE;
387✔
891

892
    /* free any previous search results, do it now in case one of the next few
893
     * tests fail */
894
    if (lp->resultcache) {
387✔
895
      if (lp->resultcache->results)
×
896
        free(lp->resultcache->results);
×
897
      free(lp->resultcache);
×
898
      lp->resultcache = NULL;
×
899
    }
900

901
    if (!msIsLayerQueryable(lp))
387✔
902
      continue;
×
903
    if (lp->status == MS_OFF)
387✔
904
      continue;
×
905
    if (lp->type == MS_LAYER_RASTER)
387✔
906
      continue; /* ok to skip? */
×
907

908
    if (map->scaledenom > 0) {
387✔
909
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
×
910
        continue;
×
911
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
×
912
        continue;
×
913
    }
914

915
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
387✔
916
      if ((lp->maxgeowidth > 0) &&
387✔
917
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
×
918
        continue;
×
919
      if ((lp->mingeowidth > 0) &&
387✔
920
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
×
921
        continue;
×
922
    }
923

924
    paging = msLayerGetPaging(lp);
387✔
925
    msLayerClose(lp); /* reset */
387✔
926
    status = msLayerOpen(lp);
387✔
927
    if (status != MS_SUCCESS)
387✔
928
      return MS_FAILURE;
929
    msLayerEnablePaging(lp, paging);
387✔
930

931
    /* disable driver paging */
932
    // msLayerEnablePaging(lp, MS_FALSE);
933

934
    old_filteritem = lp->filteritem; /* cache the existing filter/filteritem */
387✔
935
    msInitExpression(&old_filter);
387✔
936
    msCopyExpression(&old_filter, &lp->filter);
387✔
937

938
    /*
939
    ** Set the lp->filter and lp->filteritem (may need to merge). Remember
940
    *filters are *always* MapServer syntax.
941
    */
942
    lp->filteritem = map->query.filteritem; /* re-point lp->filteritem */
387✔
943
    if (old_filter.string !=
387✔
944
        NULL) { /* need to merge filters to create one logical expression */
945
      msFreeExpression(&lp->filter);
3✔
946
      lp->filter = mergeFilters(&map->query.filter, map->query.filteritem,
3✔
947
                                &old_filter, old_filteritem);
948
      if (!lp->filter.string) {
3✔
949
        msSetError(MS_MISCERR, "Filter merge failed, able to process query.",
×
950
                   "msQueryByFilter()");
951
        goto restore_old_filter;
×
952
      }
953
    } else {
954
      msCopyExpression(&lp->filter, &map->query.filter); /* apply new filter */
384✔
955
    }
956

957
    /* build item list, we want *all* items, note this *also* build tokens for
958
     * the layer filter */
959
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
387✔
960
    if (status != MS_SUCCESS)
387✔
961
      goto restore_old_filter;
1✔
962

963
    search_rect = map->query.rect;
386✔
964

965
    /* If only result count is needed, we can use msLayerGetShapeCount() */
966
    /* that has optimizations to avoid retrieving individual features */
967
    if (map->query.only_cache_result_count &&
386✔
968
        lp->_template != NULL && /* always TRUE for WFS case */
36✔
969
        lp->minfeaturesize <= 0) {
36✔
970
      int bUseLayerSRS = MS_FALSE;
971
      int numFeatures = -1;
972

973
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
974
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
975
      /* Optimization to detect the case where a WFS query uses in fact the */
976
      /* whole layer extent, but expressed in a map SRS different from the layer
977
       * SRS */
978
      /* In the case, we can directly request against the layer extent in its
979
       * native SRS */
980
      if (lp->project &&
72✔
981
          memcmp(&search_rect, &invalid_rect, sizeof(search_rect)) != 0 &&
72✔
982
          msProjectionsDiffer(&(lp->projection), &(map->projection))) {
36✔
983
        rectObj layerExtent;
984
        if (msOWSGetLayerExtent(map, lp, "FO", &layerExtent) == MS_SUCCESS) {
36✔
985
          rectObj ext = layerExtent;
36✔
986
          ext.minx -= 1e-5;
36✔
987
          ext.miny -= 1e-5;
36✔
988
          ext.maxx += 1e-5;
36✔
989
          ext.maxy += 1e-5;
36✔
990
          msProjectRect(&(lp->projection), &(map->projection), &ext);
36✔
991
          if (fabs(ext.minx - search_rect.minx) <= 2e-5 &&
36✔
992
              fabs(ext.miny - search_rect.miny) <= 2e-5 &&
6✔
993
              fabs(ext.maxx - search_rect.maxx) <= 2e-5 &&
6✔
994
              fabs(ext.maxy - search_rect.maxy) <= 2e-5) {
3✔
995
            bUseLayerSRS = MS_TRUE;
996
            numFeatures =
997
                msLayerGetShapeCount(lp, layerExtent, &(lp->projection));
3✔
998
          }
999
        }
1000
      }
1001
#endif
1002

1003
      if (!bUseLayerSRS)
1004
        numFeatures = msLayerGetShapeCount(lp, search_rect, &(map->projection));
33✔
1005
      if (numFeatures >= 0) {
36✔
1006
        lp->resultcache = (resultCacheObj *)malloc(sizeof(
36✔
1007
            resultCacheObj)); /* allocate and initialize the result cache */
1008
        MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
36✔
1009
        initResultCache(lp->resultcache);
36✔
1010
        lp->resultcache->numresults = numFeatures;
36✔
1011
        if (!msLayerGetPaging(lp) && map->query.startindex > 1) {
36✔
1012
          lp->resultcache->numresults -= (map->query.startindex - 1);
×
1013
        }
1014

1015
        lp->filteritem = old_filteritem; /* point back to original value */
36✔
1016
        msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
36✔
1017
        msFreeExpression(&old_filter);
36✔
1018

1019
        continue;
36✔
1020
      }
1021
      // Fallback in case of error (should not happen normally)
1022
    }
1023

1024
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
350✔
1025
    if (lp->project &&
350✔
1026
        memcmp(&search_rect, &invalid_rect, sizeof(search_rect)) != 0)
227✔
1027
      msProjectRect(&(map->projection), &(lp->projection),
227✔
1028
                    &search_rect); /* project the searchrect to source coords */
1029

1030
    status = msLayerWhichShapes(lp, search_rect, MS_TRUE);
350✔
1031
    if (status == MS_DONE) {           /* no overlap */
350✔
1032
      lp->filteritem = old_filteritem; /* point back to original value */
×
1033
      msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
×
1034
      msFreeExpression(&old_filter);
×
1035
      msLayerClose(lp);
×
1036
      continue;
×
1037
    } else if (status != MS_SUCCESS)
350✔
1038
      goto restore_old_filter;
1✔
1039

1040
    lp->resultcache = (resultCacheObj *)malloc(
349✔
1041
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
1042
    initResultCache(lp->resultcache);
349✔
1043

1044
    nclasses = 0;
349✔
1045
    classgroup = NULL;
1046
    if (lp->classgroup && lp->numclasses > 0)
349✔
1047
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
6✔
1048

1049
    if (lp->minfeaturesize > 0)
349✔
1050
      minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
×
1051

1052
    while ((status = msLayerNextShape(lp, &shape)) ==
1,866✔
1053
           MS_SUCCESS) { /* step through the shapes - if necessary the filter is
1054
                            applied in msLayerNextShape(...) */
1055

1056
      /* Check if the shape size is ok to be drawn */
1057
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
1,526✔
1058
          (minfeaturesize > 0)) {
1059
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
×
1060
          if (lp->debug >= MS_DEBUGLEVEL_V)
×
1061
            msDebug("msQueryByFilter(): Skipping shape (%ld) because "
×
1062
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
1063
                    shape.index);
1064
          msFreeShape(&shape);
×
1065
          continue;
×
1066
        }
1067
      }
1068

1069
      shape.classindex = msShapeGetClass(lp, map, &shape, classgroup, nclasses);
1,526✔
1070
      if (!(lp->_template) &&
1,526✔
1071
          ((shape.classindex == -1) || (lp->_class[shape.classindex]->status ==
×
1072
                                        MS_OFF))) { /* not a valid shape */
1073
        msFreeShape(&shape);
×
1074
        continue;
×
1075
      }
1076

1077
      if (!(lp->_template) &&
1,526✔
1078
          !(lp->_class[shape.classindex]->_template)) { /* no valid _template */
×
1079
        msFreeShape(&shape);
×
1080
        continue;
×
1081
      }
1082

1083
      if (lp->project) {
1,526✔
1084
        if (reprojector == NULL) {
848✔
1085
          reprojector =
1086
              msProjectCreateReprojector(&(lp->projection), &(map->projection));
188✔
1087
          if (reprojector == NULL) {
188✔
1088
            msFreeShape(&shape);
×
1089
            status = MS_FAILURE;
1090
            break;
1091
          }
1092
        }
1093
        msProjectShapeEx(reprojector, &shape);
848✔
1094
      }
1095

1096
      /* Should we skip this feature? */
1097
      if (!paging && map->query.startindex > 1) {
1,526✔
1098
        --map->query.startindex;
2✔
1099
        msFreeShape(&shape);
2✔
1100
        continue;
2✔
1101
      }
1102

1103
      if (map->query.only_cache_result_count)
1,524✔
1104
        lp->resultcache->numresults++;
×
1105
      else
1106
        addResult(map, lp->resultcache, &queryCache, &shape);
1,524✔
1107
      msFreeShape(&shape);
1,524✔
1108

1109
      if (map->query.mode ==
1,524✔
1110
          MS_QUERY_SINGLE) { /* no need to look any further */
1111
        status = MS_DONE;
1112
        break;
1113
      }
1114

1115
      /* check shape count */
1116
      if (lp->maxfeatures > 0 &&
1,520✔
1117
          lp->maxfeatures == lp->resultcache->numresults) {
43✔
1118
        status = MS_DONE;
1119
        break;
1120
      }
1121
    } /* next shape */
1122

1123
    if (classgroup)
349✔
1124
      msFree(classgroup);
6✔
1125

1126
    lp->filteritem = old_filteritem; /* point back to original value */
349✔
1127
    msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
349✔
1128
    msFreeExpression(&old_filter);
349✔
1129

1130
    msProjectDestroyReprojector(reprojector);
349✔
1131

1132
    if (status != MS_DONE)
349✔
1133
      return MS_FAILURE;
1134
    if (!map->query.only_cache_result_count && lp->resultcache->numresults == 0)
349✔
1135
      msLayerClose(lp); /* no need to keep the layer open */
52✔
1136
  }                     /* next layer */
1137

1138
  /* was anything found? */
1139
  for (l = start; l >= stop; l--) {
442✔
1140
    if (GET_LAYER(map, l)->resultcache &&
385✔
1141
        GET_LAYER(map, l)->resultcache->numresults > 0)
385✔
1142
      return MS_SUCCESS;
1143
  }
1144

1145
  if (map->debug >= MS_DEBUGLEVEL_V) {
57✔
1146
    msDebug("msQueryByFilter(): No matching record(s) found.\n");
×
1147
  }
1148
  return (MS_SUCCESS);
1149

1150
restore_old_filter:
2✔
1151
  lp->filteritem = old_filteritem;
2✔
1152
  msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
2✔
1153
  msFreeExpression(&old_filter);
2✔
1154
  msLayerClose(lp);
2✔
1155
  return MS_FAILURE;
1156
}
387✔
1157

1158
int msQueryByRect(mapObj *map) {
366✔
1159
  int l; /* counters */
1160
  int start, stop = 0;
1161

1162
  layerObj *lp;
1163

1164
  char status;
1165
  shapeObj shape, searchshape;
366✔
1166
  rectObj searchrect, searchrectInMapProj;
1167
  const rectObj invalid_rect = MS_INIT_INVALID_RECT;
366✔
1168
  double layer_tolerance = 0, tolerance = 0;
1169

1170
  int paging;
1171
  int nclasses = 0;
366✔
1172
  int *classgroup = NULL;
1173
  int numresultstotal;
1174
  double minfeaturesize = -1;
1175
  queryCacheObj queryCache;
1176

1177
  initQueryCache(&queryCache);
1178

1179
  if (map->query.type != MS_QUERY_BY_RECT) {
366✔
1180
    msSetError(MS_QUERYERR, "The query is not properly defined.",
×
1181
               "msQueryByRect()");
1182
    return (MS_FAILURE);
1183
  }
1184

1185
  msInitShape(&shape);
366✔
1186
  msInitShape(&searchshape);
366✔
1187

1188
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
366✔
1189
    start = map->numlayers - 1;
32✔
1190
  else
1191
    start = stop = map->query.layer;
1192

1193
  for (l = start; l >= stop; l--) {
762✔
1194
    reprojectionObj *reprojector = NULL;
1195
    lp = (GET_LAYER(map, l));
407✔
1196
    /* Set the global maxfeatures */
1197
    if (map->query.maxfeatures == 0)
407✔
1198
      break; /* nothing else to do */
1199
    else if (map->query.maxfeatures > 0)
396✔
1200
      lp->maxfeatures = map->query.maxfeatures;
203✔
1201

1202
    /* using mapscript, the map->query.startindex will be unset... */
1203
    if (lp->startindex > 1 && map->query.startindex < 0)
396✔
1204
      map->query.startindex = lp->startindex;
×
1205

1206
    /* conditions may have changed since this layer last drawn, so set
1207
       layer->project true to recheck projection needs (Bug #673) */
1208
    lp->project = MS_TRUE;
396✔
1209

1210
    /* free any previous search results, do it now in case one of the next few
1211
     * tests fail */
1212
    if (lp->resultcache) {
396✔
1213
      if (lp->resultcache->results)
16✔
1214
        free(lp->resultcache->results);
×
1215
      free(lp->resultcache);
16✔
1216
      lp->resultcache = NULL;
16✔
1217
    }
1218

1219
    if (!msIsLayerQueryable(lp))
396✔
1220
      continue;
12✔
1221
    if (lp->status == MS_OFF)
384✔
1222
      continue;
2✔
1223

1224
    if (map->scaledenom > 0) {
382✔
1225
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
×
1226
        continue;
×
1227
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
×
1228
        continue;
×
1229
    }
1230

1231
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
382✔
1232
      if ((lp->maxgeowidth > 0) &&
382✔
1233
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
×
1234
        continue;
×
1235
      if ((lp->mingeowidth > 0) &&
382✔
1236
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
×
1237
        continue;
×
1238
    }
1239

1240
    searchrect = map->query.rect;
382✔
1241
    if (lp->tolerance > 0) {
382✔
1242
      layer_tolerance = lp->tolerance;
1243

1244
      if (lp->toleranceunits == MS_PIXELS)
2✔
1245
        tolerance = layer_tolerance *
2✔
1246
                    msAdjustExtent(&(map->extent), map->width, map->height);
2✔
1247
      else
1248
        tolerance = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
×
1249
                                       msInchesPerUnit(map->units, 0));
×
1250

1251
      searchrect.minx -= tolerance;
2✔
1252
      searchrect.maxx += tolerance;
2✔
1253
      searchrect.miny -= tolerance;
2✔
1254
      searchrect.maxy += tolerance;
2✔
1255
    }
1256
    searchrectInMapProj = searchrect;
382✔
1257

1258
    msRectToPolygon(searchrect, &searchshape);
382✔
1259

1260
    /* Raster layers are handled specially. */
1261
    if (lp->type == MS_LAYER_RASTER) {
382✔
1262
      if (msRasterQueryByRect(map, lp, searchrect) == MS_FAILURE)
1✔
1263
        return MS_FAILURE;
1264

1265
      continue;
1✔
1266
    }
1267

1268
    /* Paging could have been disabled before */
1269
    paging = msLayerGetPaging(lp);
381✔
1270
    msLayerClose(lp); /* reset */
381✔
1271
    status = msLayerOpen(lp);
381✔
1272
    if (status != MS_SUCCESS) {
381✔
1273
      msFreeShape(&searchshape);
×
1274
      return (MS_FAILURE);
1275
    }
1276
    msLayerEnablePaging(lp, paging);
381✔
1277

1278
    /* build item list, we want *all* items */
1279
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
381✔
1280

1281
    if (status != MS_SUCCESS) {
381✔
1282
      msFreeShape(&searchshape);
×
1283
      return (MS_FAILURE);
1284
    }
1285

1286
    /* If only result count is needed, we can use msLayerGetShapeCount() */
1287
    /* that has optimizations to avoid retrieving individual features */
1288
    if (map->query.only_cache_result_count &&
381✔
1289
        lp->_template != NULL && /* always TRUE for WFS case */
92✔
1290
        lp->minfeaturesize <= 0) {
92✔
1291
      int bUseLayerSRS = MS_FALSE;
1292
      int numFeatures = -1;
1293

1294
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1295
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
1296
      /* Optimization to detect the case where a WFS query uses in fact the */
1297
      /* whole layer extent, but expressed in a map SRS different from the layer
1298
       * SRS */
1299
      /* In the case, we can directly request against the layer extent in its
1300
       * native SRS */
1301
      if (lp->project &&
184✔
1302
          memcmp(&searchrect, &invalid_rect, sizeof(searchrect)) != 0 &&
183✔
1303
          msProjectionsDiffer(&(lp->projection), &(map->projection))) {
91✔
1304
        rectObj layerExtent;
1305
        if (msOWSGetLayerExtent(map, lp, "FO", &layerExtent) == MS_SUCCESS) {
81✔
1306
          rectObj ext = layerExtent;
81✔
1307
          ext.minx -= 1e-5;
81✔
1308
          ext.miny -= 1e-5;
81✔
1309
          ext.maxx += 1e-5;
81✔
1310
          ext.maxy += 1e-5;
81✔
1311
          msProjectRect(&(lp->projection), &(map->projection), &ext);
81✔
1312
          if (fabs(ext.minx - searchrect.minx) <= 2e-5 &&
81✔
1313
              fabs(ext.miny - searchrect.miny) <= 2e-5 &&
58✔
1314
              fabs(ext.maxx - searchrect.maxx) <= 2e-5 &&
58✔
1315
              fabs(ext.maxy - searchrect.maxy) <= 2e-5) {
57✔
1316
            bUseLayerSRS = MS_TRUE;
1317
            numFeatures =
1318
                msLayerGetShapeCount(lp, layerExtent, &(lp->projection));
57✔
1319
          }
1320
        }
1321
      }
1322
#endif
1323

1324
      if (!bUseLayerSRS)
1325
        numFeatures = msLayerGetShapeCount(lp, searchrect, &(map->projection));
35✔
1326
      if (numFeatures >= 0) {
92✔
1327
        lp->resultcache = (resultCacheObj *)malloc(sizeof(
92✔
1328
            resultCacheObj)); /* allocate and initialize the result cache */
1329
        MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
92✔
1330
        initResultCache(lp->resultcache);
92✔
1331
        lp->resultcache->numresults = numFeatures;
92✔
1332
        if (!paging && map->query.startindex > 1) {
92✔
1333
          lp->resultcache->numresults -= (map->query.startindex - 1);
1✔
1334
        }
1335
        msFreeShape(&searchshape);
92✔
1336
        continue;
92✔
1337
      }
1338
      // Fallback in case of error (should not happen normally)
1339
    }
1340

1341
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
289✔
1342
    if (lp->project &&
289✔
1343
        memcmp(&searchrect, &invalid_rect, sizeof(searchrect)) != 0)
176✔
1344
      msProjectRect(&(map->projection), &(lp->projection),
175✔
1345
                    &searchrect); /* project the searchrect to source coords */
1346

1347
    status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
289✔
1348
    if (status == MS_DONE) { /* no overlap */
289✔
1349
      msLayerClose(lp);
8✔
1350
      continue;
8✔
1351
    } else if (status != MS_SUCCESS) {
281✔
1352
      msLayerClose(lp);
×
1353
      msFreeShape(&searchshape);
×
1354
      return (MS_FAILURE);
1355
    }
1356

1357
    lp->resultcache = (resultCacheObj *)malloc(
281✔
1358
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
1359
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
281✔
1360
    initResultCache(lp->resultcache);
281✔
1361

1362
    nclasses = 0;
281✔
1363
    classgroup = NULL;
1364
    if (lp->classgroup && lp->numclasses > 0)
281✔
1365
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
×
1366

1367
    if (lp->minfeaturesize > 0)
281✔
1368
      minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
×
1369

1370
    numresultstotal = 0;
1371
    while ((status = msLayerNextShape(lp, &shape)) ==
2,642✔
1372
           MS_SUCCESS) { /* step through the shapes */
1373
      numresultstotal++;
2,427✔
1374
      /* Check if the shape size is ok to be drawn */
1375
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
2,427✔
1376
          (minfeaturesize > 0)) {
1377
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
×
1378
          if (lp->debug >= MS_DEBUGLEVEL_V)
×
1379
            msDebug("msQueryByRect(): Skipping shape (%ld) because "
×
1380
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
1381
                    shape.index);
1382
          msFreeShape(&shape);
×
1383
          continue;
×
1384
        }
1385
      }
1386

1387
      shape.classindex = msShapeGetClass(lp, map, &shape, classgroup, nclasses);
2,427✔
1388
      if (!(lp->_template) &&
2,427✔
1389
          ((shape.classindex == -1) || (lp->_class[shape.classindex]->status ==
×
1390
                                        MS_OFF))) { /* not a valid shape */
1391
        msFreeShape(&shape);
×
1392
        continue;
×
1393
      }
1394

1395
      if (!(lp->_template) &&
2,427✔
1396
          !(lp->_class[shape.classindex]->_template)) { /* no valid _template */
×
1397
        msFreeShape(&shape);
×
1398
        continue;
×
1399
      }
1400

1401
      if (lp->project) {
2,427✔
1402
        if (reprojector == NULL) {
1,701✔
1403
          reprojector =
1404
              msProjectCreateReprojector(&(lp->projection), &(map->projection));
169✔
1405
          if (reprojector == NULL) {
169✔
1406
            msFreeShape(&shape);
×
1407
            status = MS_FAILURE;
1408
            break;
1409
          }
1410
        }
1411
        if (msProjectShapeEx(reprojector, &shape) != MS_SUCCESS) {
1,701✔
1412
          // msProjectShapeEx() calls msFreeShape() on error
1413
          continue;
×
1414
        }
1415
      }
1416

1417
      if (msRectContained(&shape.bounds, &searchrectInMapProj) ==
2,427✔
1418
          MS_TRUE) { /* if the whole shape is in, don't intersect */
1419
        status = MS_TRUE;
1420
      } else {
1421
        switch (shape.type) { /* make sure shape actually intersects the qrect
125✔
1422
                                 (ADD FUNCTIONS SPECIFIC TO RECTOBJ) */
1423
        case MS_SHAPE_POINT:
7✔
1424
          status = msIntersectMultipointPolygon(&shape, &searchshape);
7✔
1425
          break;
7✔
1426
        case MS_SHAPE_LINE:
2✔
1427
          status = msIntersectPolylinePolygon(&shape, &searchshape);
2✔
1428
          break;
2✔
1429
        case MS_SHAPE_POLYGON:
116✔
1430
          status = msIntersectPolygons(&shape, &searchshape);
116✔
1431
          break;
116✔
1432
        case MS_SHAPE_NULL:
1433
          status = MS_TRUE;
1434
          break;
1435
        default:
1436
          break;
1437
        }
1438
      }
1439

1440
      if (status == MS_TRUE) {
125✔
1441
        /* Should we skip this feature? */
1442
        if (!paging && map->query.startindex > 1) {
2,411✔
1443
          --map->query.startindex;
102✔
1444
          msFreeShape(&shape);
102✔
1445
          continue;
102✔
1446
        }
1447
        if (map->query.only_cache_result_count)
2,309✔
1448
          lp->resultcache->numresults++;
×
1449
        else
1450
          addResult(map, lp->resultcache, &queryCache, &shape);
2,309✔
1451
        --map->query.maxfeatures;
2,309✔
1452
      }
1453
      msFreeShape(&shape);
2,325✔
1454

1455
      /* check shape count */
1456
      if (lp->maxfeatures > 0 &&
2,325✔
1457
          lp->maxfeatures == lp->resultcache->numresults) {
1,377✔
1458
        status = MS_DONE;
1459
        break;
1460
      }
1461

1462
    } /* next shape */
1463

1464
    if (paging && lp->maxfeatures > 0 && numresultstotal == lp->maxfeatures &&
281✔
1465
        numresultstotal > lp->resultcache->numresults)
18✔
1466
      lp->resultcache->hasnext = MS_TRUE;
1✔
1467

1468
    if (classgroup)
281✔
1469
      msFree(classgroup);
×
1470

1471
    msProjectDestroyReprojector(reprojector);
281✔
1472

1473
    if (status != MS_DONE) {
281✔
1474
      msFreeShape(&searchshape);
×
1475
      return (MS_FAILURE);
1476
    }
1477

1478
    if (!map->query.only_cache_result_count && lp->resultcache->numresults == 0)
281✔
1479
      msLayerClose(lp); /* no need to keep the layer open */
2✔
1480
  }                     /* next layer */
1481

1482
  msFreeShape(&searchshape);
366✔
1483

1484
  /* was anything found? */
1485
  for (l = start; l >= stop; l--) {
406✔
1486
    if (GET_LAYER(map, l)->resultcache &&
388✔
1487
        GET_LAYER(map, l)->resultcache->numresults > 0)
359✔
1488
      return (MS_SUCCESS);
1489
  }
1490

1491
  if (map->debug >= MS_DEBUGLEVEL_V) {
18✔
1492
    msDebug("msQueryByRect(): No matching record(s) found.\n");
×
1493
  }
1494
  return (MS_SUCCESS);
1495
}
366✔
1496

1497
static int is_duplicate(resultCacheObj *resultcache, int shapeindex,
1498
                        int tileindex) {
1499
  int i;
1500

1501
  for (i = 0; i < resultcache->numresults; i++)
×
1502
    if (resultcache->results[i].shapeindex == shapeindex &&
×
1503
        resultcache->results[i].tileindex == tileindex)
×
1504
      return (MS_TRUE);
1505

1506
  return (MS_FALSE);
1507
}
1508

1509
int msQueryByFeatures(mapObj *map) {
4✔
1510
  int i, l;
1511
  int start, stop = 0;
1512
  layerObj *lp, *slp;
1513
  char status;
1514

1515
  double distance, tolerance, layer_tolerance;
1516

1517
  rectObj searchrect;
1518
  shapeObj shape, selectshape;
4✔
1519
  int nclasses = 0;
4✔
1520
  int *classgroup = NULL;
1521
  double minfeaturesize = -1;
1522

1523
  queryCacheObj queryCache;
1524

1525
  initQueryCache(&queryCache);
1526

1527
  /* is the selection layer valid and has it been queried */
1528
  if (map->query.slayer < 0 || map->query.slayer >= map->numlayers) {
4✔
1529
    msSetError(MS_QUERYERR, "Invalid selection layer index.",
×
1530
               "msQueryByFeatures()");
1531
    return (MS_FAILURE);
1532
  }
1533
  slp = (GET_LAYER(map, map->query.slayer));
4✔
1534
  if (!slp->resultcache) {
4✔
1535
    msSetError(MS_QUERYERR, "Selection layer has not been queried.",
×
1536
               "msQueryByFeatures()");
1537
    return (MS_FAILURE);
1538
  }
1539

1540
  /* conditions may have changed since this layer last drawn, so set
1541
     layer->project true to recheck projection needs (Bug #673) */
1542
  slp->project = msProjectionsDiffer(&(slp->projection), &(map->projection));
4✔
1543

1544
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
4✔
1545
    start = map->numlayers - 1;
×
1546
  else
1547
    start = stop = map->query.layer;
1548

1549
  /* selection layers should already be open */
1550
  /* status = msLayerOpen(slp);
1551
  if(status != MS_SUCCESS) return(MS_FAILURE); */
1552

1553
  msInitShape(&shape); /* initialize a few things */
4✔
1554
  msInitShape(&selectshape);
4✔
1555

1556
  for (l = start; l >= stop; l--) {
8✔
1557
    reprojectionObj *reprojector = NULL;
1558
    if (l == map->query.slayer)
4✔
1559
      continue; /* skip the selection layer */
×
1560

1561
    lp = (GET_LAYER(map, l));
4✔
1562
    if (map->query.maxfeatures == 0)
4✔
1563
      break; /* nothing else to do */
1564
    else if (map->query.maxfeatures > 0)
4✔
1565
      lp->maxfeatures = map->query.maxfeatures;
×
1566

1567
    /* using mapscript, the map->query.startindex will be unset... */
1568
    if (lp->startindex > 1 && map->query.startindex < 0)
4✔
1569
      map->query.startindex = lp->startindex;
×
1570

1571
    /* conditions may have changed since this layer last drawn, so set
1572
       layer->project true to recheck projection needs (Bug #673) */
1573
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
4✔
1574

1575
    /* free any previous search results, do it now in case one of the next few
1576
     * tests fail */
1577
    if (lp->resultcache) {
4✔
1578
      if (lp->resultcache->results)
×
1579
        free(lp->resultcache->results);
×
1580
      free(lp->resultcache);
×
1581
      lp->resultcache = NULL;
×
1582
    }
1583

1584
    if (!msIsLayerQueryable(lp))
4✔
1585
      continue;
×
1586
    if (lp->status == MS_OFF)
4✔
1587
      continue;
×
1588

1589
    if (map->scaledenom > 0) {
4✔
1590
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
×
1591
        continue;
×
1592
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
×
1593
        continue;
×
1594
    }
1595

1596
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
4✔
1597
      if ((lp->maxgeowidth > 0) &&
4✔
1598
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
×
1599
        continue;
×
1600
      if ((lp->mingeowidth > 0) &&
4✔
1601
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
×
1602
        continue;
×
1603
    }
1604

1605
    /* Get the layer tolerance default is 3 for point and line layers, 0 for
1606
     * others */
1607
    if (lp->tolerance == -1) {
4✔
1608
      if (lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE)
4✔
1609
        layer_tolerance = 3;
1610
      else
1611
        layer_tolerance = 0;
1612
    } else
1613
      layer_tolerance = lp->tolerance;
1614

1615
    if (lp->toleranceunits == MS_PIXELS)
4✔
1616
      tolerance = layer_tolerance *
4✔
1617
                  msAdjustExtent(&(map->extent), map->width, map->height);
4✔
1618
    else
1619
      tolerance = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
×
1620
                                     msInchesPerUnit(map->units, 0));
×
1621

1622
    msLayerClose(lp); /* reset */
4✔
1623
    status = msLayerOpen(lp);
4✔
1624
    if (status != MS_SUCCESS)
4✔
1625
      return (MS_FAILURE);
1626
    msLayerEnablePaging(lp, MS_FALSE);
4✔
1627

1628
    /* build item list, we want *all* items */
1629
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
4✔
1630
    if (status != MS_SUCCESS)
4✔
1631
      return (MS_FAILURE);
1632

1633
    /* for each selection shape */
1634
    for (i = 0; i < slp->resultcache->numresults; i++) {
8✔
1635

1636
      status =
4✔
1637
          msLayerGetShape(slp, &selectshape, &(slp->resultcache->results[i]));
4✔
1638
      if (status != MS_SUCCESS) {
4✔
1639
        msLayerClose(lp);
×
1640
        msLayerClose(slp);
×
1641
        return (MS_FAILURE);
1642
      }
1643

1644
      if (selectshape.type != MS_SHAPE_POLYGON &&
4✔
1645
          selectshape.type != MS_SHAPE_LINE) {
1646
        msLayerClose(lp);
×
1647
        msLayerClose(slp);
×
1648
        msSetError(MS_QUERYERR, "Selection features MUST be polygons or lines.",
×
1649
                   "msQueryByFeatures()");
1650
        return (MS_FAILURE);
1651
      }
1652

1653
      if (slp->project)
4✔
1654
        msProjectShape(&(slp->projection), &(map->projection), &selectshape);
×
1655

1656
      /* identify target shapes */
1657
      searchrect = selectshape.bounds;
4✔
1658

1659
      searchrect.minx -=
4✔
1660
          tolerance; /* expand the search box to account for layer tolerances
1661
                        (e.g. buffered searches) */
1662
      searchrect.maxx += tolerance;
4✔
1663
      searchrect.miny -= tolerance;
4✔
1664
      searchrect.maxy += tolerance;
4✔
1665

1666
      if (lp->project)
4✔
1667
        msProjectRect(
×
1668
            &(map->projection), &(lp->projection),
1669
            &searchrect); /* project the searchrect to source coords */
1670

1671
      status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
4✔
1672
      if (status == MS_DONE) { /* no overlap */
4✔
1673
        msLayerClose(lp);
×
1674
        break; /* next layer */
1675
      } else if (status != MS_SUCCESS) {
4✔
1676
        msLayerClose(lp);
×
1677
        msLayerClose(slp);
×
1678
        return (MS_FAILURE);
1679
      }
1680

1681
      if (i == 0) {
4✔
1682
        lp->resultcache = (resultCacheObj *)malloc(sizeof(
4✔
1683
            resultCacheObj)); /* allocate and initialize the result cache */
1684
        MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
4✔
1685
        initResultCache(lp->resultcache);
4✔
1686
      }
1687

1688
      nclasses = 0;
4✔
1689
      classgroup = NULL;
1690
      if (lp->classgroup && lp->numclasses > 0)
4✔
1691
        classgroup = msAllocateValidClassGroups(lp, &nclasses);
×
1692

1693
      if (lp->minfeaturesize > 0)
4✔
1694
        minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
×
1695

1696
      while ((status = msLayerNextShape(lp, &shape)) ==
20✔
1697
             MS_SUCCESS) { /* step through the shapes */
1698

1699
        /* check for dups when there are multiple selection shapes */
1700
        if (i > 0 &&
16✔
1701
            is_duplicate(lp->resultcache, shape.index, shape.tileindex))
×
1702
          continue;
×
1703

1704
        /* Check if the shape size is ok to be drawn */
1705
        if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
16✔
1706
            (minfeaturesize > 0)) {
1707
          if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
×
1708
            if (lp->debug >= MS_DEBUGLEVEL_V)
×
1709
              msDebug("msQueryByFeature(): Skipping shape (%ld) because "
×
1710
                      "LAYER::MINFEATURESIZE is bigger than shape size\n",
1711
                      shape.index);
1712
            msFreeShape(&shape);
×
1713
            continue;
×
1714
          }
1715
        }
1716

1717
        shape.classindex =
16✔
1718
            msShapeGetClass(lp, map, &shape, classgroup, nclasses);
16✔
1719
        if (!(lp->_template) && ((shape.classindex == -1) ||
16✔
1720
                                 (lp->_class[shape.classindex]->status ==
×
1721
                                  MS_OFF))) { /* not a valid shape */
1722
          msFreeShape(&shape);
×
1723
          continue;
×
1724
        }
1725

1726
        if (!(lp->_template) && !(lp->_class[shape.classindex]
16✔
1727
                                      ->_template)) { /* no valid _template */
×
1728
          msFreeShape(&shape);
×
1729
          continue;
×
1730
        }
1731

1732
        if (lp->project) {
16✔
1733
          if (reprojector == NULL) {
×
1734
            reprojector = msProjectCreateReprojector(&(lp->projection),
×
1735
                                                     &(map->projection));
1736
            if (reprojector == NULL) {
×
1737
              msFreeShape(&shape);
×
1738
              status = MS_FAILURE;
1739
              break;
1740
            }
1741
          }
1742
          msProjectShapeEx(reprojector, &shape);
×
1743
        }
1744

1745
        switch (selectshape.type) { /* may eventually support types other than
16✔
1746
                                       polygon on line */
1747
        case MS_SHAPE_POLYGON:
16✔
1748
          switch (shape.type) { /* make sure shape actually intersects the
16✔
1749
                                   selectshape */
1750
          case MS_SHAPE_POINT:
×
1751
            if (tolerance == 0) /* just test for intersection */
×
1752
              status = msIntersectMultipointPolygon(&shape, &selectshape);
×
1753
            else { /* check distance, distance=0 means they intersect */
1754
              distance = msDistanceShapeToShape(&selectshape, &shape);
×
1755
              if (distance < tolerance)
×
1756
                status = MS_TRUE;
1757
            }
1758
            break;
1759
          case MS_SHAPE_LINE:
×
1760
            if (tolerance == 0) { /* just test for intersection */
×
1761
              status = msIntersectPolylinePolygon(&shape, &selectshape);
×
1762
            } else { /* check distance, distance=0 means they intersect */
1763
              distance = msDistanceShapeToShape(&selectshape, &shape);
×
1764
              if (distance < tolerance)
×
1765
                status = MS_TRUE;
1766
            }
1767
            break;
1768
          case MS_SHAPE_POLYGON:
16✔
1769
            if (tolerance == 0) /* just test for intersection */
16✔
1770
              status = msIntersectPolygons(&shape, &selectshape);
16✔
1771
            else { /* check distance, distance=0 means they intersect */
1772
              distance = msDistanceShapeToShape(&selectshape, &shape);
×
1773
              if (distance < tolerance)
×
1774
                status = MS_TRUE;
1775
            }
1776
            break;
1777
          default:
1778
            status = MS_FALSE;
1779
            break;
1780
          }
1781
          break;
1782
        case MS_SHAPE_LINE:
×
1783
          switch (shape.type) { /* make sure shape actually intersects the
×
1784
                                   selectshape */
1785
          case MS_SHAPE_POINT:
×
1786
            if (tolerance == 0) { /* just test for intersection */
×
1787
              distance = msDistanceShapeToShape(&selectshape, &shape);
×
1788
              if (distance == 0)
×
1789
                status = MS_TRUE;
1790
            } else {
1791
              distance = msDistanceShapeToShape(&selectshape, &shape);
×
1792
              if (distance < tolerance)
×
1793
                status = MS_TRUE;
1794
            }
1795
            break;
1796
          case MS_SHAPE_LINE:
×
1797
            if (tolerance == 0) { /* just test for intersection */
×
1798
              status = msIntersectPolylines(&shape, &selectshape);
×
1799
            } else { /* check distance, distance=0 means they intersect */
1800
              distance = msDistanceShapeToShape(&selectshape, &shape);
×
1801
              if (distance < tolerance)
×
1802
                status = MS_TRUE;
1803
            }
1804
            break;
1805
          case MS_SHAPE_POLYGON:
×
1806
            if (tolerance == 0) /* just test for intersection */
×
1807
              status = msIntersectPolylinePolygon(&selectshape, &shape);
×
1808
            else { /* check distance, distance=0 means they intersect */
1809
              distance = msDistanceShapeToShape(&selectshape, &shape);
×
1810
              if (distance < tolerance)
×
1811
                status = MS_TRUE;
1812
            }
1813
            break;
1814
          default:
1815
            status = MS_FALSE;
1816
            break;
1817
          }
1818
          break;
1819
        default:
1820
          break; /* should never get here as we test for selection shape type
1821
                    explicitly earlier */
1822
        }
1823

1824
        if (status == MS_TRUE) {
16✔
1825
          /* Should we skip this feature? */
1826
          if (!msLayerGetPaging(lp) && map->query.startindex > 1) {
16✔
1827
            --map->query.startindex;
×
1828
            msFreeShape(&shape);
×
1829
            continue;
×
1830
          }
1831
          addResult(map, lp->resultcache, &queryCache, &shape);
16✔
1832
        }
1833
        msFreeShape(&shape);
16✔
1834

1835
        /* check shape count */
1836
        if (lp->maxfeatures > 0 &&
16✔
1837
            lp->maxfeatures == lp->resultcache->numresults) {
×
1838
          status = MS_DONE;
1839
          break;
1840
        }
1841
      } /* next shape */
1842

1843
      if (classgroup)
4✔
1844
        msFree(classgroup);
×
1845

1846
      msProjectDestroyReprojector(reprojector);
4✔
1847

1848
      msFreeShape(&selectshape);
4✔
1849

1850
      if (status != MS_DONE)
4✔
1851
        return (MS_FAILURE);
1852

1853
    } /* next selection shape */
1854

1855
    if (lp->resultcache == NULL || lp->resultcache->numresults == 0)
4✔
1856
      msLayerClose(lp); /* no need to keep the layer open */
×
1857
  }                     /* next layer */
1858

1859
  /* was anything found? */
1860
  for (l = start; l >= stop; l--) {
4✔
1861
    if (l == map->query.slayer)
4✔
1862
      continue; /* skip the selection layer */
×
1863
    if (GET_LAYER(map, l)->resultcache &&
4✔
1864
        GET_LAYER(map, l)->resultcache->numresults > 0)
4✔
1865
      return (MS_SUCCESS);
1866
  }
1867

1868
  if (map->debug >= MS_DEBUGLEVEL_V) {
×
1869
    msDebug("msQueryByFeatures(): No matching record(s) found.\n");
×
1870
  }
1871
  return (MS_SUCCESS);
1872
}
4✔
1873

1874
/* msQueryByPoint()
1875
 *
1876
 * With mode=MS_QUERY_SINGLE:
1877
 *   Set maxresults = 0 to have a single result across all layers (the closest
1878
 *     shape from the first layer that finds a match).
1879
 *   Set maxresults = 1 to have up to one result per layer (the closest shape
1880
 *     from each layer).
1881
 *
1882
 * With mode=MS_QUERY_MULTIPLE:
1883
 *   Set maxresults = 0 to have an unlimited number of results.
1884
 *   Set maxresults > 0 to limit the number of results per layer (the shapes
1885
 *     returned are the first ones found in each layer and are not necessarily
1886
 *     the closest ones).
1887
 */
1888
int msQueryByPoint(mapObj *map) {
93✔
1889

1890
  if (map->query.type != MS_QUERY_BY_POINT) {
93✔
1891
    msSetError(MS_QUERYERR, "The query is not properly defined.",
×
1892
               "msQueryByPoint()");
1893
    return (MS_FAILURE);
×
1894
  }
1895

1896
  queryCacheObj queryCache;
1897
  initQueryCache(&queryCache);
1898

1899
  int start, stop = 0;
1900
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
93✔
1901
    start = map->numlayers - 1;
58✔
1902
  else
1903
    start = stop = map->query.layer;
1904

1905
  const double cellx =
93✔
1906
      MS_CELLSIZE(map->extent.minx, map->extent.maxx, map->width);
93✔
1907
  const double celly =
93✔
1908
      MS_CELLSIZE(map->extent.miny, map->extent.maxy, map->height);
93✔
1909

1910
  const double resolutionfactor = map->resolution / map->defresolution;
93✔
1911

1912
  for (int l = start; l >= stop; l--) {
365✔
1913
    std::unique_ptr<reprojectionObj, decltype(&msProjectDestroyReprojector)>
1914
        reprojector{nullptr, msProjectDestroyReprojector};
1915
    layerObj *lp = (GET_LAYER(map, l));
282✔
1916
    if (map->query.maxfeatures == 0)
282✔
1917
      break; /* nothing else to do */
1918
    else if (map->query.maxfeatures > 0)
282✔
1919
      lp->maxfeatures = map->query.maxfeatures;
×
1920

1921
    /* using mapscript, the map->query.startindex will be unset... */
1922
    if (lp->startindex > 1 && map->query.startindex < 0)
282✔
1923
      map->query.startindex = lp->startindex;
×
1924

1925
    /* conditions may have changed since this layer last drawn, so set
1926
       layer->project true to recheck projection needs (Bug #673) */
1927
    lp->project = MS_TRUE;
282✔
1928

1929
    /* free any previous search results, do it now in case one of the next few
1930
     * tests fail */
1931
    if (lp->resultcache) {
282✔
1932
      if (lp->resultcache->results)
×
1933
        free(lp->resultcache->results);
×
1934
      free(lp->resultcache);
×
1935
      lp->resultcache = NULL;
×
1936
    }
1937

1938
    if (!msIsLayerQueryable(lp))
282✔
1939
      continue;
56✔
1940
    if (lp->status == MS_OFF)
226✔
1941
      continue;
83✔
1942

1943
    if (map->scaledenom > 0) {
143✔
1944
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
104✔
1945
        continue;
×
1946
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
104✔
1947
        continue;
×
1948
    }
1949

1950
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
143✔
1951
      if ((lp->maxgeowidth > 0) &&
137✔
1952
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
×
1953
        continue;
×
1954
      if ((lp->mingeowidth > 0) &&
137✔
1955
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
×
1956
        continue;
×
1957
    }
1958

1959
    /* Raster layers are handled specially.  */
1960
    if (lp->type == MS_LAYER_RASTER) {
143✔
1961
      if (msRasterQueryByPoint(map, lp, map->query.mode, map->query.point,
19✔
1962
                               map->query.buffer,
1963
                               map->query.maxresults) == MS_FAILURE)
1964
        return MS_FAILURE;
1965
      continue;
19✔
1966
    }
1967

1968
    /* Get the layer tolerance default is 3 for point and line layers, 0 for
1969
     * others */
1970
    double layer_tolerance = 0;
1971
    if (lp->tolerance == -1) {
124✔
1972
      if (lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE)
115✔
1973
        layer_tolerance = 3;
1974
    } else {
1975
      layer_tolerance = lp->tolerance;
1976
    }
1977

1978
    struct SearchSymbol {
1979
      mapObj *m_map = nullptr;
1980
      shapeObj shape{};
1981
      int classindex = -1;
1982
      styleObj *style = nullptr;
1983
      imageObj *cachedImage = nullptr;
1984

1985
      SearchSymbol(mapObj *map) : m_map(map) {}
22✔
1986
      ~SearchSymbol() {
22✔
1987
        if (cachedImage) {
22✔
1988
          /*
1989
           * hack to work around bug #3834: if we have use an alternate
1990
           * renderer, the symbolset may contain symbols that reference it. We
1991
           * want to remove those references before the altFormat is destroyed
1992
           * to avoid a segfault and/or a leak, and so the the main renderer
1993
           * doesn't pick the cache up thinking it's for him.
1994
           */
1995
          for (int i = 0; i < m_map->symbolset.numsymbols; i++) {
170✔
1996
            if (m_map->symbolset.symbol[i] != NULL) {
160✔
1997
              symbolObj *s = m_map->symbolset.symbol[i];
1998
              if (s->renderer == MS_IMAGE_RENDERER(cachedImage)) {
160✔
1999
                MS_IMAGE_RENDERER(cachedImage)->freeSymbol(s);
10✔
2000
                s->renderer = NULL;
10✔
2001
              }
2002
            }
2003
          }
2004

2005
          msFreeImage(cachedImage);
10✔
2006
        }
2007
      }
22✔
2008

2009
      SearchSymbol(const SearchSymbol &) = delete;
2010
      SearchSymbol &operator=(const SearchSymbol &) = delete;
2011
      SearchSymbol(SearchSymbol &&other) {
11✔
2012
        m_map = other.m_map;
11✔
2013
        shape = std::move(other.shape);
11✔
2014
        classindex = other.classindex;
11✔
2015
        std::swap(style, other.style);
2016
        std::swap(cachedImage, other.cachedImage);
2017
      }
11✔
2018
      SearchSymbol &operator=(SearchSymbol &&other) {
2019
        m_map = other.m_map;
2020
        shape = std::move(other.shape);
2021
        classindex = other.classindex;
2022
        std::swap(style, other.style);
2023
        std::swap(cachedImage, other.cachedImage);
2024
        return *this;
2025
      }
2026
    };
2027
    std::vector<SearchSymbol> searchSymbols;
2028

2029
    rectObj rect = {HUGE_VAL, HUGE_VAL, -HUGE_VAL, -HUGE_VAL};
124✔
2030
    double t = 0;
2031
    if (map->query.buffer <= 0) { /* use layer tolerance */
124✔
2032

2033
      if (lp->toleranceunits == MS_PIXELS)
122✔
2034
        t = layer_tolerance * MS_MAX(cellx, celly);
217✔
2035
      else
2036
        t = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
×
2037
                               msInchesPerUnit(map->units, 0));
×
2038

2039
      const auto takeIntoAccountClass = [map, lp, t, cellx, celly,
13✔
2040
                                         resolutionfactor, &rect,
2041
                                         &searchSymbols](int classindex) {
2042
        classObj *klass = lp->_class[classindex];
13✔
2043
        if (!msScaleInBounds(map->scaledenom, klass->minscaledenom,
13✔
2044
                             klass->maxscaledenom)) {
2045
          return;
2046
        }
2047

2048
        for (int s = 0; s < klass->numstyles; s++) {
24✔
2049
          auto *style = klass->styles[s];
12✔
2050
          if (msScaleInBounds(map->scaledenom, style->minscaledenom,
12✔
2051
                              style->maxscaledenom)) {
2052
            if (style->symbol >= map->symbolset.numsymbols ||
11✔
2053
                style->symbol <= 0)
2054
              continue;
×
2055
            symbolObj *symbol = map->symbolset.symbol[style->symbol];
11✔
2056

2057
            double symbol_width = 0;
11✔
2058
            double symbol_height = 0;
11✔
2059
            if (msGetMarkerSize(map, style, &symbol_width, &symbol_height,
11✔
2060
                                style->scalefactor) != MS_SUCCESS) {
2061
              continue;
×
2062
            }
2063

2064
            symbolStyleObj s;
2065

2066
            computeSymbolStyle(&s, style, symbol, style->scalefactor,
11✔
2067
                               resolutionfactor);
2068

2069
            double center_x =
2070
                MS_MAP2IMAGE_X(map->query.point.x, map->extent.minx, cellx);
11✔
2071
            double center_y =
2072
                MS_MAP2IMAGE_Y(map->query.point.y, map->extent.maxy, celly);
11✔
2073

2074
            if (msAdjustMarkerPos(map, style, symbol, &center_x, &center_y,
11✔
2075
                                  style->scalefactor,
2076
                                  s.rotation) != MS_SUCCESS) {
2077
              continue;
×
2078
            }
2079

2080
            center_x = MS_IMAGE2MAP_X(center_x, map->extent.minx, cellx);
11✔
2081
            center_y = MS_IMAGE2MAP_Y(center_y, map->extent.maxy, celly);
11✔
2082

2083
            const auto applyRotationAndScaling = [cellx, celly, &s](double &x,
44✔
2084
                                                                    double &y) {
2085
              double sina = sin(s.rotation), cosa = cos(s.rotation);
44✔
2086
              double out_x = x * cosa - y * sina;
44✔
2087
              double out_y = x * sina + y * cosa;
44✔
2088
              x = out_x * cellx;
44✔
2089
              y = out_y * celly;
44✔
2090
            };
55✔
2091

2092
            double P1_X = -symbol_width / 2;
11✔
2093
            double P1_Y = -symbol_height / 2;
11✔
2094
            applyRotationAndScaling(P1_X, P1_Y);
11✔
2095
            P1_X -= t;
11✔
2096
            P1_Y -= t;
11✔
2097

2098
            double P2_X = symbol_width / 2;
11✔
2099
            double P2_Y = -symbol_height / 2;
11✔
2100
            applyRotationAndScaling(P2_X, P2_Y);
11✔
2101
            P2_X += t;
11✔
2102
            P2_Y -= t;
11✔
2103

2104
            double P3_X = symbol_width / 2;
11✔
2105
            double P3_Y = symbol_height / 2;
11✔
2106
            applyRotationAndScaling(P3_X, P3_Y);
11✔
2107
            P3_X += t;
11✔
2108
            P3_Y += t;
11✔
2109

2110
            double P4_X = -symbol_width / 2;
11✔
2111
            double P4_Y = symbol_height / 2;
11✔
2112
            applyRotationAndScaling(P4_X, P4_Y);
11✔
2113
            P4_X -= t;
11✔
2114
            P4_Y += t;
11✔
2115

2116
            SearchSymbol searchSymbol(map);
2117
            searchSymbol.style = style;
11✔
2118
            searchSymbol.classindex = classindex;
11✔
2119

2120
            lineObj line = {0, NULL};
11✔
2121
            line.numpoints = 5;
11✔
2122
            line.point =
11✔
2123
                (pointObj *)msSmallMalloc(sizeof(pointObj) * line.numpoints);
11✔
2124
            line.point[0].x = center_x + P1_X;
11✔
2125
            line.point[0].y = center_y + P1_Y;
11✔
2126
            line.point[1].x = center_x + P2_X;
11✔
2127
            line.point[1].y = center_y + P2_Y;
11✔
2128
            line.point[2].x = center_x + P3_X;
11✔
2129
            line.point[2].y = center_y + P3_Y;
11✔
2130
            line.point[3].x = center_x + P4_X;
11✔
2131
            line.point[3].y = center_y + P4_Y;
11✔
2132
            line.point[4].x = center_x + P1_X;
11✔
2133
            line.point[4].y = center_y + P1_Y;
11✔
2134
            msAddLineDirectly(&(searchSymbol.shape), &line);
11✔
2135

2136
            searchSymbols.push_back(std::move(searchSymbol));
11✔
2137

2138
            rect.minx = MIN(rect.minx,
33✔
2139
                            center_x + MIN(MIN(P1_X, P2_X), MIN(P3_X, P4_X)));
2140
            rect.miny = MIN(rect.miny,
22✔
2141
                            center_y + MIN(MIN(P1_Y, P2_Y), MIN(P3_Y, P4_Y)));
2142
            rect.maxx = MAX(rect.maxx,
22✔
2143
                            center_x + MAX(MAX(P1_X, P2_X), MAX(P3_X, P4_X)));
2144
            rect.maxy = MAX(rect.maxy,
33✔
2145
                            center_y + MAX(MAX(P1_Y, P2_Y), MAX(P3_Y, P4_Y)));
2146
          }
11✔
2147
        }
2148
      };
122✔
2149

2150
      if (lp->type == MS_LAYER_POINT && lp->identificationclassgroup) {
122✔
2151
        for (int i = 0; i < lp->numclasses; i++) {
3✔
2152
          if (lp->_class[i]->group &&
3✔
2153
              strcasecmp(lp->_class[i]->group, lp->identificationclassgroup) ==
3✔
2154
                  0) {
2155
            takeIntoAccountClass(i);
3✔
2156
            break;
2157
          }
2158
        }
2159
      } else if (lp->type == MS_LAYER_POINT && lp->identificationclassauto) {
119✔
2160

2161
        for (int i = 0; i < lp->numclasses; i++) {
20✔
2162
          auto it =
2163
              map->query.getFeatureInfo->mapLayerIndexToStyleNames.find(i);
10✔
2164
          if (it ==
2165
                  map->query.getFeatureInfo->mapLayerIndexToStyleNames.end() ||
10✔
2166
              it->second.empty()) {
2167
            takeIntoAccountClass(i);
10✔
2168
          } else if (lp->_class[i]->group &&
×
2169
                     std::find(it->second.begin(), it->second.end(),
×
2170
                               lp->_class[i]->group) != it->second.end()) {
×
2171
            takeIntoAccountClass(i);
×
2172
          }
2173
        }
2174
      }
2175
    } else { /* use buffer distance */
2176
      t = map->query.buffer;
2177
    }
2178

2179
    if (rect.minx == HUGE_VAL) {
124✔
2180
      rect.minx = map->query.point.x - t;
113✔
2181
      rect.maxx = map->query.point.x + t;
113✔
2182
      rect.miny = map->query.point.y - t;
113✔
2183
      rect.maxy = map->query.point.y + t;
113✔
2184
    }
2185

2186
    /* Paging could have been disabled before */
2187
    int paging = msLayerGetPaging(lp);
124✔
2188
    msLayerClose(lp); /* reset */
124✔
2189
    int status = msLayerOpen(lp);
124✔
2190
    if (status != MS_SUCCESS)
124✔
2191
      return (MS_FAILURE);
2192
    msLayerEnablePaging(lp, paging);
124✔
2193

2194
    /* build item list, we want *all* items */
2195
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
124✔
2196
    if (status != MS_SUCCESS)
124✔
2197
      return (MS_FAILURE);
2198

2199
    /* identify target shapes */
2200
    rectObj searchrect = rect;
124✔
2201
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
124✔
2202
    if (lp->project)
124✔
2203
      msProjectRect(&(map->projection), &(lp->projection),
59✔
2204
                    &searchrect); /* project the searchrect to source coords */
2205

2206
    status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
124✔
2207
    if (status == MS_DONE) { /* no overlap */
124✔
2208
      msLayerClose(lp);
7✔
2209
      continue;
2210
    } else if (status != MS_SUCCESS) {
117✔
2211
      msLayerClose(lp);
×
2212
      return (MS_FAILURE);
2213
    }
2214

2215
    lp->resultcache = (resultCacheObj *)malloc(
117✔
2216
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
2217
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
117✔
2218
    initResultCache(lp->resultcache);
117✔
2219

2220
    int nclasses = 0;
117✔
2221
    int *classgroup = NULL;
2222
    if (lp->type == MS_LAYER_POINT && lp->identificationclassgroup &&
117✔
2223
        lp->numclasses > 0) {
3✔
2224
      char *lp_classgroup_backup = lp->classgroup;
3✔
2225
      lp->classgroup = lp->identificationclassgroup;
3✔
2226
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
3✔
2227
      lp->classgroup = lp_classgroup_backup;
3✔
2228
    } else if (lp->classgroup && lp->numclasses > 0) {
117✔
2229
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
42✔
2230
    }
2231

2232
    const double minfeaturesize =
2233
        (lp->minfeaturesize > 0) ? Pix2LayerGeoref(map, lp, lp->minfeaturesize)
117✔
2234
                                 : -1;
2235

2236
    while (true) { /* step through the shapes */
2237

2238
      shapeObj shape;
842✔
2239
      if ((status = msLayerNextShape(lp, &shape)) != MS_SUCCESS)
842✔
2240
        break;
2241

2242
      /* Check if the shape size is ok to be drawn */
2243
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
733✔
2244
          (minfeaturesize > 0)) {
2245
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
×
2246
          if (lp->debug >= MS_DEBUGLEVEL_V)
×
2247
            msDebug("msQueryByPoint(): Skipping shape (%ld) because "
×
2248
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
2249
                    shape.index);
2250
          continue;
×
2251
        }
2252
      }
2253

2254
      bool reprojectionDone = false;
2255
      bool matchFound = false;
2256

2257
      if (!(lp->type == MS_LAYER_POINT &&
733✔
2258
            (lp->identificationclassgroup || lp->identificationclassauto))) {
501✔
2259
        shape.classindex =
716✔
2260
            msShapeGetClass(lp, map, &shape, classgroup, nclasses);
716✔
2261
        if (!(lp->_template) && ((shape.classindex == -1) ||
716✔
2262
                                 (lp->_class[shape.classindex]->status ==
×
2263
                                  MS_OFF))) { /* not a valid shape */
2264
          continue;
×
2265
        }
2266

2267
        if (!(lp->_template) && !(lp->_class[shape.classindex]
716✔
2268
                                      ->_template)) { /* no valid _template */
×
2269
          continue;
×
2270
        }
2271
      } else {
2272
        int classindex = -1;
2273
        while ((classindex = msShapeGetNextClass(classindex, lp, map, &shape,
17✔
2274
                                                 classgroup, nclasses)) != -1) {
17✔
2275
          if (lp->_class[classindex]->status == MS_OFF) {
16✔
2276
            continue;
×
2277
          }
2278
          if (searchSymbols.empty() || shape.type != MS_SHAPE_POINT) {
16✔
2279
            break;
2280
          }
2281

2282
          if (!reprojectionDone) {
2283
            reprojectionDone = true;
2284
            if (lp->project) {
16✔
2285
              if (reprojector == NULL) {
×
2286
                reprojector.reset(msProjectCreateReprojector(
×
2287
                    &(lp->projection), &(map->projection)));
2288
                if (reprojector == NULL) {
×
2289
                  status = MS_FAILURE;
2290
                  break;
2291
                }
2292
              }
2293
              msProjectShapeEx(reprojector.get(), &shape);
×
2294
            }
2295
          }
2296

2297
          for (auto &searchSymbol : searchSymbols) {
17✔
2298
            if (searchSymbol.classindex == classindex &&
32✔
2299
                msPointInPolygon(&(shape.line[0].point[0]),
16✔
2300
                                 &(searchSymbol.shape.line[0]))) {
16✔
2301
              if (!searchSymbol.cachedImage) {
15✔
2302
                outputFormatObj *altFormat = msSelectOutputFormat(map, "png");
10✔
2303
                msInitializeRendererVTable(altFormat);
10✔
2304

2305
                double symbol_width = 0;
10✔
2306
                double symbol_height = 0;
10✔
2307
                if (msGetMarkerSize(
10✔
2308
                        map, searchSymbol.style, &symbol_width, &symbol_height,
2309
                        searchSymbol.style->scalefactor) != MS_SUCCESS) {
10✔
2310
                  assert(false);
2311
                }
2312

2313
                // Takes into account potential rotation of up to 45 deg: 1.5 >
2314
                // sqrt(2)
2315
                symbol_width *= 1.5;
10✔
2316
                symbol_height *= 1.5;
10✔
2317

2318
                searchSymbol.cachedImage = msImageCreate(
10✔
2319
                    symbol_width, symbol_height, altFormat, nullptr, nullptr,
2320
                    map->resolution, map->defresolution, nullptr);
2321
                if (!searchSymbol.cachedImage) {
10✔
2322
                  msSetError(MS_MISCERR, "Unable to initialize symbol image.",
×
2323
                             "msQueryByPoint()");
2324
                  return (MS_FAILURE);
×
2325
                }
2326

2327
                pointObj imCenter;
2328
                imCenter.x = searchSymbol.cachedImage->width / 2;
10✔
2329
                imCenter.y = searchSymbol.cachedImage->height / 2;
10✔
2330
                if (msDrawMarkerSymbol(map, searchSymbol.cachedImage, &imCenter,
10✔
2331
                                       searchSymbol.style,
2332
                                       searchSymbol.style->scalefactor) !=
10✔
2333
                    MS_SUCCESS) {
2334
                  msSetError(MS_MISCERR, "Unable to draw symbol image.",
×
2335
                             "msQueryByPoint()");
2336
                  return (MS_FAILURE);
2337
                }
2338
              }
2339

2340
              rasterBufferObj rb;
2341
              memset(&rb, 0, sizeof(rasterBufferObj));
2342

2343
              if (MS_IMAGE_RENDERER(searchSymbol.cachedImage)
15✔
2344
                          ->getRasterBufferHandle(searchSymbol.cachedImage,
15✔
2345
                                                  &rb) == MS_SUCCESS &&
15✔
2346
                  rb.type == MS_BUFFER_BYTE_RGBA) {
15✔
2347

2348
                const int test_x = static_cast<int>(std::round(
15✔
2349
                    searchSymbol.cachedImage->width / 2 +
15✔
2350
                    (map->query.point.x - shape.line[0].point[0].x) / cellx));
15✔
2351
                const int test_y = static_cast<int>(std::round(
15✔
2352
                    searchSymbol.cachedImage->height / 2 -
15✔
2353
                    (map->query.point.y - shape.line[0].point[0].y) / celly));
15✔
2354

2355
                // Check that the queried pixel hits a non-transparent pixel of
2356
                // the symbol
2357
                const int tolerancePixel =
15✔
2358
                    static_cast<int>(std::ceil(t / MS_MAX(cellx, celly)));
15✔
2359
                for (int y = -tolerancePixel;
42✔
2360
                     !matchFound && y <= tolerancePixel; ++y) {
42✔
2361
                  for (int x = -tolerancePixel; x <= tolerancePixel; ++x) {
159✔
2362
                    if (test_y + y >= 0 &&
141✔
2363
                        test_y + y < searchSymbol.cachedImage->height &&
141✔
2364
                        test_x + x >= 0 &&
141✔
2365
                        test_x + x < searchSymbol.cachedImage->width) {
2366
                      if (rb.data.rgba
141✔
2367
                              .a[(test_y + y) * rb.data.rgba.row_step +
141✔
2368
                                 (test_x + x) * rb.data.rgba.pixel_step]) {
141✔
2369
                        matchFound = true;
2370
                        break;
2371
                      }
2372
                    }
2373
                  }
2374
                }
2375
              }
2376

2377
              break;
15✔
2378
            }
2379
          }
2380

2381
          break;
2382
        }
2383
        if (status == MS_FAILURE) {
17✔
2384
          break;
2385
        }
2386
        if (classindex == -1)
17✔
2387
          continue;
1✔
2388
      }
2389

2390
      if (!reprojectionDone && lp->project) {
732✔
2391
        if (reprojector == NULL) {
619✔
2392
          reprojector.reset(msProjectCreateReprojector(&(lp->projection),
54✔
2393
                                                       &(map->projection)));
2394
          if (reprojector == NULL) {
54✔
2395
            status = MS_FAILURE;
2396
            break;
2397
          }
2398
        }
2399
        msProjectShapeEx(reprojector.get(), &shape);
619✔
2400
      }
2401

2402
      double d = 0;
2403

2404
      if (searchSymbols.empty() || shape.type != MS_SHAPE_POINT) {
732✔
2405
        d = msDistancePointToShape(&(map->query.point), &shape);
716✔
2406
        matchFound = d <= t;
716✔
2407
      }
2408

2409
      if (matchFound) { /* found one */
732✔
2410

2411
        /* Should we skip this feature? */
2412
        if (!paging && map->query.startindex > 1) {
137✔
2413
          --map->query.startindex;
×
2414
          continue;
×
2415
        }
2416

2417
        if (map->query.mode == MS_QUERY_SINGLE) {
137✔
2418
          cleanupResultCache(lp->resultcache);
59✔
2419
          initQueryCache(&queryCache);
2420
          addResult(map, lp->resultcache, &queryCache, &shape);
59✔
2421
          t = d; /* next one must be closer */
2422
        } else {
2423
          addResult(map, lp->resultcache, &queryCache, &shape);
78✔
2424
        }
2425
      }
2426

2427
      if (map->query.mode == MS_QUERY_MULTIPLE && map->query.maxresults > 0 &&
732✔
2428
          lp->resultcache->numresults == map->query.maxresults) {
549✔
2429
        status = MS_DONE; /* got enough results for this layer */
2430
        break;
2431
      }
2432

2433
      /* check shape count */
2434
      if (lp->maxfeatures > 0 &&
724✔
2435
          lp->maxfeatures == lp->resultcache->numresults) {
×
2436
        status = MS_DONE;
2437
        break;
2438
      }
2439

2440
    } /* next shape */
842✔
2441

2442
    if (classgroup)
117✔
2443
      msFree(classgroup);
45✔
2444

2445
    if (status != MS_DONE)
117✔
2446
      return (MS_FAILURE);
×
2447

2448
    if (lp->resultcache->numresults == 0)
117✔
2449
      msLayerClose(lp); /* no need to keep the layer open */
27✔
2450

2451
    if ((lp->resultcache->numresults > 0) &&
117✔
2452
        (map->query.mode == MS_QUERY_SINGLE) && (map->query.maxresults == 0))
90✔
2453
      break; /* no need to search any further */
2454
  }          /* next layer */
124✔
2455

2456
  /* was anything found? */
2457
  for (int l = start; l >= stop; l--) {
214✔
2458
    if (GET_LAYER(map, l)->resultcache &&
202✔
2459
        GET_LAYER(map, l)->resultcache->numresults > 0)
100✔
2460
      return (MS_SUCCESS);
2461
  }
2462

2463
  if (map->debug >= MS_DEBUGLEVEL_V) {
12✔
2464
    msDebug("msQueryByPoint(): No matching record(s) found.\n");
×
2465
  }
2466
  return (MS_SUCCESS);
2467
}
2468

2469
int msQueryByShape(mapObj *map) {
13✔
2470
  int start, stop = 0, l;
2471
  shapeObj shape, *qshape = NULL;
13✔
2472
  layerObj *lp;
2473
  char status;
2474
  double distance, tolerance, layer_tolerance;
2475
  rectObj searchrect;
2476

2477
  int nclasses = 0;
13✔
2478
  int *classgroup = NULL;
2479
  double minfeaturesize = -1;
2480
  queryCacheObj queryCache;
2481

2482
  initQueryCache(&queryCache);
2483

2484
  if (map->query.type != MS_QUERY_BY_SHAPE) {
13✔
2485
    msSetError(MS_QUERYERR, "The query is not properly defined.",
×
2486
               "msQueryByShape()");
2487
    return (MS_FAILURE);
2488
  }
2489

2490
  if (!(map->query.shape)) {
13✔
2491
    msSetError(MS_QUERYERR, "Query shape is not defined.", "msQueryByShape()");
×
2492
    return (MS_FAILURE);
2493
  }
2494
  if (map->query.shape->type != MS_SHAPE_POLYGON &&
13✔
2495
      map->query.shape->type != MS_SHAPE_LINE &&
13✔
2496
      map->query.shape->type != MS_SHAPE_POINT) {
2497
    msSetError(MS_QUERYERR, "Query shape MUST be a polygon, line or point.",
×
2498
               "msQueryByShape()");
2499
    return (MS_FAILURE);
2500
  }
2501

2502
  msInitShape(&shape);
13✔
2503
  qshape = map->query.shape; /* for brevity */
13✔
2504

2505
  if (map->query.layer < 0 || map->query.layer >= map->numlayers)
13✔
2506
    start = map->numlayers - 1;
8✔
2507
  else
2508
    start = stop = map->query.layer;
2509

2510
  msComputeBounds(qshape); /* make sure an accurate extent exists */
13✔
2511

2512
  for (l = start; l >= stop; l--) { /* each layer */
36✔
2513
    reprojectionObj *reprojector = NULL;
2514
    lp = (GET_LAYER(map, l));
23✔
2515
    if (map->query.maxfeatures == 0)
23✔
2516
      break; /* nothing else to do */
2517
    else if (map->query.maxfeatures > 0)
23✔
2518
      lp->maxfeatures = map->query.maxfeatures;
×
2519

2520
    /* using mapscript, the map->query.startindex will be unset... */
2521
    if (lp->startindex > 1 && map->query.startindex < 0)
23✔
2522
      map->query.startindex = lp->startindex;
×
2523

2524
    /* conditions may have changed since this layer last drawn, so set
2525
       layer->project true to recheck projection needs (Bug #673) */
2526
    lp->project = MS_TRUE;
23✔
2527

2528
    /* free any previous search results, do it now in case one of the next few
2529
     * tests fail */
2530
    if (lp->resultcache) {
23✔
2531
      if (lp->resultcache->results)
×
2532
        free(lp->resultcache->results);
×
2533
      free(lp->resultcache);
×
2534
      lp->resultcache = NULL;
×
2535
    }
2536

2537
    if (!msIsLayerQueryable(lp))
23✔
2538
      continue;
×
2539
    if (lp->status == MS_OFF)
23✔
2540
      continue;
×
2541

2542
    if (map->scaledenom > 0) {
23✔
2543
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
×
2544
        continue;
×
2545
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
×
2546
        continue;
×
2547
    }
2548

2549
    if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) {
23✔
2550
      if ((lp->maxgeowidth > 0) &&
23✔
2551
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
×
2552
        continue;
×
2553
      if ((lp->mingeowidth > 0) &&
23✔
2554
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
×
2555
        continue;
×
2556
    }
2557

2558
    /* Raster layers are handled specially. */
2559
    if (lp->type == MS_LAYER_RASTER) {
23✔
2560
      if (msRasterQueryByShape(map, lp, qshape) == MS_FAILURE)
2✔
2561
        return MS_FAILURE;
2562
      continue;
2✔
2563
    }
2564

2565
    /* Get the layer tolerance default is 3 for point and line layers, 0 for
2566
     * others */
2567
    if (lp->tolerance == -1) {
21✔
2568
      if (lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE)
21✔
2569
        layer_tolerance = 3;
2570
      else
2571
        layer_tolerance = 0;
2572
    } else
2573
      layer_tolerance = lp->tolerance;
2574

2575
    if (lp->toleranceunits == MS_PIXELS)
21✔
2576
      tolerance = layer_tolerance *
21✔
2577
                  msAdjustExtent(&(map->extent), map->width, map->height);
21✔
2578
    else
2579
      tolerance = layer_tolerance * (msInchesPerUnit(lp->toleranceunits, 0) /
×
2580
                                     msInchesPerUnit(map->units, 0));
×
2581

2582
    msLayerClose(lp); /* reset */
21✔
2583
    status = msLayerOpen(lp);
21✔
2584
    if (status != MS_SUCCESS)
21✔
2585
      return (MS_FAILURE);
2586
    /* disable driver paging */
2587
    msLayerEnablePaging(lp, MS_FALSE);
21✔
2588

2589
    /* build item list, we want *all* items */
2590
    status = msLayerWhichItems(lp, MS_TRUE, NULL);
21✔
2591
    if (status != MS_SUCCESS)
21✔
2592
      return (MS_FAILURE);
2593

2594
    /* identify target shapes */
2595
    searchrect = qshape->bounds;
21✔
2596

2597
    searchrect.minx -= tolerance; /* expand the search box to account for layer
21✔
2598
                                     tolerances (e.g. buffered searches) */
2599
    searchrect.maxx += tolerance;
21✔
2600
    searchrect.miny -= tolerance;
21✔
2601
    searchrect.maxy += tolerance;
21✔
2602

2603
    lp->project = msProjectionsDiffer(&(lp->projection), &(map->projection));
21✔
2604
    if (lp->project)
21✔
2605
      msProjectRect(&(map->projection), &(lp->projection),
×
2606
                    &searchrect); /* project the searchrect to source coords */
2607

2608
    status = msLayerWhichShapes(lp, searchrect, MS_TRUE);
21✔
2609
    if (status == MS_DONE) { /* no overlap */
21✔
2610
      msLayerClose(lp);
1✔
2611
      continue;
1✔
2612
    } else if (status != MS_SUCCESS) {
20✔
2613
      msLayerClose(lp);
×
2614
      return (MS_FAILURE);
2615
    }
2616

2617
    lp->resultcache = (resultCacheObj *)malloc(
20✔
2618
        sizeof(resultCacheObj)); /* allocate and initialize the result cache */
2619
    MS_CHECK_ALLOC(lp->resultcache, sizeof(resultCacheObj), MS_FAILURE);
20✔
2620
    initResultCache(lp->resultcache);
20✔
2621

2622
    nclasses = 0;
20✔
2623
    if (lp->classgroup && lp->numclasses > 0)
20✔
2624
      classgroup = msAllocateValidClassGroups(lp, &nclasses);
×
2625

2626
    if (lp->minfeaturesize > 0)
20✔
2627
      minfeaturesize = Pix2LayerGeoref(map, lp, lp->minfeaturesize);
×
2628

2629
    while ((status = msLayerNextShape(lp, &shape)) ==
129✔
2630
           MS_SUCCESS) { /* step through the shapes */
2631

2632
      /* Check if the shape size is ok to be drawn */
2633
      if ((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) &&
109✔
2634
          (minfeaturesize > 0)) {
2635
        if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
×
2636
          if (lp->debug >= MS_DEBUGLEVEL_V)
×
2637
            msDebug("msQueryByShape(): Skipping shape (%ld) because "
×
2638
                    "LAYER::MINFEATURESIZE is bigger than shape size\n",
2639
                    shape.index);
2640
          msFreeShape(&shape);
×
2641
          continue;
×
2642
        }
2643
      }
2644

2645
      shape.classindex = msShapeGetClass(lp, map, &shape, classgroup, nclasses);
109✔
2646
      if (!(lp->_template) &&
109✔
2647
          ((shape.classindex == -1) || (lp->_class[shape.classindex]->status ==
×
2648
                                        MS_OFF))) { /* not a valid shape */
2649
        msFreeShape(&shape);
×
2650
        continue;
×
2651
      }
2652

2653
      if (!(lp->_template) &&
109✔
2654
          !(lp->_class[shape.classindex]->_template)) { /* no valid _template */
×
2655
        msFreeShape(&shape);
×
2656
        continue;
×
2657
      }
2658

2659
      if (lp->project) {
109✔
2660
        if (reprojector == NULL) {
×
2661
          reprojector =
2662
              msProjectCreateReprojector(&(lp->projection), &(map->projection));
×
2663
          if (reprojector == NULL) {
×
2664
            msFreeShape(&shape);
×
2665
            status = MS_FAILURE;
2666
            break;
2667
          }
2668
        }
2669
        msProjectShapeEx(reprojector, &shape);
×
2670
      }
2671

2672
      switch (qshape->type) { /* may eventually support types other than polygon
109✔
2673
                                 or line */
2674
      case MS_SHAPE_POLYGON:
109✔
2675
        switch (
109✔
2676
            shape.type) { /* make sure shape actually intersects the shape */
109✔
2677
        case MS_SHAPE_POINT:
1✔
2678
          if (tolerance == 0) /* just test for intersection */
1✔
2679
            status = msIntersectMultipointPolygon(&shape, qshape);
×
2680
          else { /* check distance, distance=0 means they intersect */
2681
            distance = msDistanceShapeToShape(qshape, &shape);
1✔
2682
            if (distance < tolerance)
1✔
2683
              status = MS_TRUE;
2684
          }
2685
          break;
2686
        case MS_SHAPE_LINE:
×
2687
          if (tolerance == 0) { /* just test for intersection */
×
2688
            status = msIntersectPolylinePolygon(&shape, qshape);
×
2689
          } else { /* check distance, distance=0 means they intersect */
2690
            distance = msDistanceShapeToShape(qshape, &shape);
×
2691
            if (distance < tolerance)
×
2692
              status = MS_TRUE;
2693
          }
2694
          break;
2695
        case MS_SHAPE_POLYGON:
108✔
2696
          if (tolerance == 0) /* just test for intersection */
108✔
2697
            status = msIntersectPolygons(&shape, qshape);
108✔
2698
          else { /* check distance, distance=0 means they intersect */
2699
            distance = msDistanceShapeToShape(qshape, &shape);
×
2700
            if (distance < tolerance)
×
2701
              status = MS_TRUE;
2702
          }
2703
          break;
2704
        default:
2705
          break;
2706
        }
2707
        break;
2708
      case MS_SHAPE_LINE:
×
2709
        switch (shape.type) { /* make sure shape actually intersects the
×
2710
                                 selectshape */
2711
        case MS_SHAPE_POINT:
×
2712
          if (tolerance == 0) { /* just test for intersection */
×
2713
            distance = msDistanceShapeToShape(qshape, &shape);
×
2714
            if (distance == 0)
×
2715
              status = MS_TRUE;
2716
          } else {
2717
            distance = msDistanceShapeToShape(qshape, &shape);
×
2718
            if (distance < tolerance)
×
2719
              status = MS_TRUE;
2720
          }
2721
          break;
2722
        case MS_SHAPE_LINE:
×
2723
          if (tolerance == 0) { /* just test for intersection */
×
2724
            status = msIntersectPolylines(&shape, qshape);
×
2725
          } else { /* check distance, distance=0 means they intersect */
2726
            distance = msDistanceShapeToShape(qshape, &shape);
×
2727
            if (distance < tolerance)
×
2728
              status = MS_TRUE;
2729
          }
2730
          break;
2731
        case MS_SHAPE_POLYGON:
×
2732
          if (tolerance == 0) /* just test for intersection */
×
2733
            status = msIntersectPolylinePolygon(qshape, &shape);
×
2734
          else { /* check distance, distance=0 means they intersect */
2735
            distance = msDistanceShapeToShape(qshape, &shape);
×
2736
            if (distance < tolerance)
×
2737
              status = MS_TRUE;
2738
          }
2739
          break;
2740
        default:
2741
          status = MS_FALSE;
2742
          break;
2743
        }
2744
        break;
2745
      case MS_SHAPE_POINT:
×
2746
        distance = msDistanceShapeToShape(qshape, &shape);
×
2747
        status = MS_FALSE;
2748
        if (tolerance == 0 && distance == 0)
×
2749
          status = MS_TRUE; /* shapes intersect */
2750
        else if (distance < tolerance)
×
2751
          status = MS_TRUE; /* shapes are close enough */
2752
        break;
2753
      default:
2754
        break; /* should never get here as we test for selection shape type
2755
                  explicitly earlier */
2756
      }
2757

2758
      if (status == MS_TRUE) {
108✔
2759
        /* Should we skip this feature? */
2760
        if (!msLayerGetPaging(lp) && map->query.startindex > 1) {
99✔
2761
          --map->query.startindex;
×
2762
          msFreeShape(&shape);
×
2763
          continue;
×
2764
        }
2765
        addResult(map, lp->resultcache, &queryCache, &shape);
99✔
2766
      }
2767
      msFreeShape(&shape);
109✔
2768

2769
      /* check shape count */
2770
      if (lp->maxfeatures > 0 &&
109✔
2771
          lp->maxfeatures == lp->resultcache->numresults) {
×
2772
        status = MS_DONE;
2773
        break;
2774
      }
2775
    } /* next shape */
2776

2777
    free(classgroup);
20✔
2778
    classgroup = NULL;
2779

2780
    msProjectDestroyReprojector(reprojector);
20✔
2781

2782
    if (status != MS_DONE) {
20✔
2783
      return (MS_FAILURE);
2784
    }
2785

2786
    if (lp->resultcache->numresults == 0)
20✔
2787
      msLayerClose(lp); /* no need to keep the layer open */
×
2788
  }                     /* next layer */
2789

2790
  /* was anything found? */
2791
  for (l = start; l >= stop; l--) {
14✔
2792
    if (GET_LAYER(map, l)->resultcache &&
13✔
2793
        GET_LAYER(map, l)->resultcache->numresults > 0)
12✔
2794
      return (MS_SUCCESS);
2795
  }
2796

2797
  if (map->debug >= MS_DEBUGLEVEL_V) {
1✔
2798
    msDebug("msQueryByShape(): No matching record(s) found.\n");
×
2799
  }
2800
  return (MS_SUCCESS);
2801
}
13✔
2802

2803
/* msGetQueryResultBounds()
2804
 *
2805
 * Compute the BBOX of all query results, returns the number of layers found
2806
 * that contained query results and were included in the BBOX.
2807
 * i.e. if we return 0 then the value in bounds is invalid.
2808
 */
2809
int msGetQueryResultBounds(mapObj *map, rectObj *bounds) {
478✔
2810
  int i, found = 0;
2811
  rectObj tmpBounds;
2812

2813
  for (i = 0; i < map->numlayers; i++) {
2,524✔
2814

2815
    layerObj *lp;
2816
    lp = (GET_LAYER(map, i));
2,046✔
2817

2818
    if (!lp->resultcache)
2,046✔
2819
      continue;
1,512✔
2820
    if (lp->resultcache->numresults <= 0)
534✔
2821
      continue;
54✔
2822

2823
    tmpBounds = lp->resultcache->bounds;
480✔
2824

2825
    if (found == 0) {
480✔
2826
      *bounds = tmpBounds;
426✔
2827
    } else {
2828
      msMergeRect(bounds, &tmpBounds);
54✔
2829
    }
2830

2831
    found++;
480✔
2832
  }
2833

2834
  return found;
478✔
2835
}
2836

2837
/* TODO: Rename this msFreeResultSet() or something along those lines... */
2838
/* msQueryFree()
2839
 *
2840
 * Free layer's query results. If qlayer == -1, all layers will be treated.
2841
 */
2842
void msQueryFree(mapObj *map, int qlayer) {
1✔
2843
  int l; /* counters */
2844
  int start, stop = 0;
2845
  layerObj *lp;
2846

2847
  if (qlayer < 0 || qlayer >= map->numlayers)
1✔
2848
    start = map->numlayers - 1;
1✔
2849
  else
2850
    start = stop = qlayer;
2851

2852
  for (l = start; l >= stop; l--) {
8✔
2853
    lp = (GET_LAYER(map, l));
7✔
2854

2855
    if (lp->resultcache) {
7✔
2856
      if (lp->resultcache->results)
1✔
2857
        free(lp->resultcache->results);
1✔
2858
      free(lp->resultcache);
1✔
2859
      lp->resultcache = NULL;
1✔
2860
    }
2861
  }
2862
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc