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