/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 | } |