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