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