/src/MapServer/src/mapraster.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $id: mapraster.c 10772 2010-11-29 18:27:02Z aboudreault $ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: msDrawRasterLayer(): generic raster layer drawing. |
6 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
7 | | * Pete Olson (LMIC) |
8 | | * Steve Lime |
9 | | * |
10 | | ****************************************************************************** |
11 | | * Copyright (c) 1996-2010 Regents of the University of Minnesota. |
12 | | * |
13 | | * Permission is hereby granted, free of charge, to any person obtaining a |
14 | | * copy of this software and associated documentation files (the "Software"), |
15 | | * to deal in the Software without restriction, including without limitation |
16 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
17 | | * and/or sell copies of the Software, and to permit persons to whom the |
18 | | * Software is furnished to do so, subject to the following conditions: |
19 | | * |
20 | | * The above copyright notice and this permission notice shall be included in |
21 | | * all copies of this Software or works derived from this Software. |
22 | | * |
23 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
24 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
25 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
26 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
27 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
28 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
29 | | * DEALINGS IN THE SOFTWARE. |
30 | | ****************************************************************************/ |
31 | | |
32 | | #include <assert.h> |
33 | | #include "mapserver.h" |
34 | | #include "mapfile.h" |
35 | | #include "mapresample.h" |
36 | | #include "mapthread.h" |
37 | | |
38 | | extern int msyylex_destroy(void); |
39 | | extern int yyparse(parseObj *); |
40 | | |
41 | | extern parseResultObj yypresult; /* result of parsing, true/false */ |
42 | | |
43 | | #include "gdal.h" |
44 | | #include "cpl_string.h" |
45 | | #include "mapraster.h" |
46 | | |
47 | | #define MAXCOLORS 256 |
48 | | #define BUFLEN 1024 |
49 | | #define HDRLEN 8 |
50 | | |
51 | | #define CVT(x) ((x) >> 8) /* converts to 8-bit color value */ |
52 | | |
53 | | #define NUMGRAYS 16 |
54 | | |
55 | | /************************************************************************/ |
56 | | /* msGetClass_String() */ |
57 | | /************************************************************************/ |
58 | | |
59 | | static int msGetClass_String(layerObj *layer, colorObj *color, |
60 | | const char *pixel_value, int firstClassToTry) |
61 | | |
62 | 0 | { |
63 | 0 | int i; |
64 | 0 | const char *tmpstr1 = NULL; |
65 | 0 | int numitems; |
66 | 0 | char *item_names[4] = {"pixel", "red", "green", "blue"}; |
67 | 0 | char *item_values[4]; |
68 | 0 | char red_value[8], green_value[8], blue_value[8]; |
69 | | |
70 | | /* -------------------------------------------------------------------- */ |
71 | | /* No need to do a lookup in the case of one default class. */ |
72 | | /* -------------------------------------------------------------------- */ |
73 | 0 | if ((layer->numclasses == 1) && |
74 | 0 | !(layer->class[0] -> expression.string)) /* no need to do lookup */ |
75 | 0 | return (0); |
76 | | |
77 | | /* -------------------------------------------------------------------- */ |
78 | | /* Setup values list for expressions. */ |
79 | | /* -------------------------------------------------------------------- */ |
80 | 0 | numitems = 4; |
81 | 0 | if (color->red == -1 && color->green == -1 && color->blue == -1) { |
82 | 0 | strcpy(red_value, "-1"); |
83 | 0 | strcpy(green_value, "-1"); |
84 | 0 | strcpy(blue_value, "-1"); |
85 | 0 | } else { |
86 | 0 | sprintf(red_value, "%d", color->red); |
87 | 0 | sprintf(green_value, "%d", color->green); |
88 | 0 | sprintf(blue_value, "%d", color->blue); |
89 | 0 | } |
90 | |
|
91 | 0 | item_values[0] = (char *)pixel_value; |
92 | 0 | item_values[1] = red_value; |
93 | 0 | item_values[2] = green_value; |
94 | 0 | item_values[3] = blue_value; |
95 | | |
96 | | /* -------------------------------------------------------------------- */ |
97 | | /* Loop over classes till we find a match. */ |
98 | | /* -------------------------------------------------------------------- */ |
99 | 0 | if (firstClassToTry >= layer->numclasses) |
100 | 0 | firstClassToTry = -1; |
101 | 0 | for (i = 0; i < layer->numclasses; i++) { |
102 | |
|
103 | 0 | const int idx = firstClassToTry < 0 ? i |
104 | 0 | : i == 0 ? firstClassToTry |
105 | 0 | : i <= firstClassToTry ? i - 1 |
106 | 0 | : i; |
107 | | |
108 | | /* check for correct classgroup, if set */ |
109 | 0 | if (layer->class[idx] -> group && layer -> classgroup &&strcasecmp( |
110 | 0 | layer->class[idx] -> group, |
111 | 0 | layer -> classgroup) != 0) |
112 | 0 | continue; |
113 | | |
114 | | /* Empty expression - always matches */ |
115 | 0 | if (layer->class[idx] -> expression.string == NULL) |
116 | 0 | return (i); |
117 | | |
118 | 0 | switch (layer->class[idx] -> expression.type) { |
119 | | |
120 | | /* -------------------------------------------------------------------- */ |
121 | | /* Simple string match */ |
122 | | /* -------------------------------------------------------------------- */ |
123 | 0 | case (MS_STRING): |
124 | | /* trim junk white space */ |
125 | 0 | tmpstr1 = pixel_value; |
126 | 0 | while (*tmpstr1 == ' ') |
127 | 0 | tmpstr1++; |
128 | |
|
129 | 0 | if (strcmp(layer->class[idx] -> expression.string, tmpstr1) == 0) |
130 | 0 | return (idx); /* matched */ |
131 | 0 | break; |
132 | | |
133 | | /* -------------------------------------------------------------------- */ |
134 | | /* Regular expression. Rarely used for raster. */ |
135 | | /* -------------------------------------------------------------------- */ |
136 | 0 | case (MS_REGEX): |
137 | 0 | if (!layer->class[idx] -> expression.compiled) { |
138 | 0 | if (ms_regcomp(&(layer->class[idx] -> expression.regex), |
139 | 0 | layer->class[idx] -> expression.string, |
140 | 0 | MS_REG_EXTENDED | MS_REG_NOSUB) != |
141 | 0 | 0) { /* compile the expression */ |
142 | 0 | msSetError(MS_REGEXERR, "Invalid regular expression.", |
143 | 0 | "msGetClass()"); |
144 | 0 | return (-1); |
145 | 0 | } |
146 | 0 | layer->class[idx]->expression.compiled = MS_TRUE; |
147 | 0 | } |
148 | | |
149 | 0 | if (ms_regexec(&(layer->class[idx] -> expression.regex), pixel_value, 0, |
150 | 0 | NULL, 0) == 0) |
151 | 0 | return (idx); /* got a match */ |
152 | 0 | break; |
153 | | |
154 | | /* -------------------------------------------------------------------- */ |
155 | | /* Parsed expression. */ |
156 | | /* -------------------------------------------------------------------- */ |
157 | 0 | case (MS_EXPRESSION): { |
158 | 0 | int status; |
159 | 0 | parseObj p; |
160 | 0 | shapeObj dummy_shape; |
161 | 0 | expressionObj *expression = &(layer->class[idx] -> expression); |
162 | |
|
163 | 0 | dummy_shape.numvalues = numitems; |
164 | 0 | dummy_shape.values = item_values; |
165 | |
|
166 | 0 | if (expression->tokens == NULL) |
167 | 0 | msTokenizeExpression(expression, item_names, &numitems); |
168 | |
|
169 | 0 | p.shape = &dummy_shape; |
170 | 0 | p.expr = expression; |
171 | 0 | p.expr->curtoken = p.expr->tokens; /* reset */ |
172 | 0 | p.type = MS_PARSE_TYPE_BOOLEAN; |
173 | |
|
174 | 0 | status = yyparse(&p); |
175 | |
|
176 | 0 | if (status != 0) { |
177 | 0 | msSetError(MS_PARSEERR, "Failed to parse expression: %s", |
178 | 0 | "msGetClass_FloatRGB", expression->string); |
179 | 0 | return -1; |
180 | 0 | } |
181 | | |
182 | 0 | if (p.result.intval) |
183 | 0 | return idx; |
184 | 0 | break; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | 0 | return (-1); /* not found */ |
190 | 0 | } |
191 | | |
192 | | /************************************************************************/ |
193 | | /* msGetClass() */ |
194 | | /************************************************************************/ |
195 | | |
196 | 0 | int msGetClass(layerObj *layer, colorObj *color, int colormap_index) { |
197 | 0 | char pixel_value[12]; |
198 | |
|
199 | 0 | snprintf(pixel_value, sizeof(pixel_value), "%d", colormap_index); |
200 | |
|
201 | 0 | return msGetClass_String(layer, color, pixel_value, -1); |
202 | 0 | } |
203 | | |
204 | | /************************************************************************/ |
205 | | /* msGetClass_FloatRGB() */ |
206 | | /* */ |
207 | | /* Returns the class based on classification of a floating */ |
208 | | /* pixel value. */ |
209 | | /************************************************************************/ |
210 | | |
211 | | int msGetClass_FloatRGB(layerObj *layer, float fValue, int red, int green, |
212 | 0 | int blue) { |
213 | 0 | return msGetClass_FloatRGB_WithFirstClassToTry(layer, fValue, red, green, |
214 | 0 | blue, -1); |
215 | 0 | } |
216 | | |
217 | | int msGetClass_FloatRGB_WithFirstClassToTry(layerObj *layer, float fValue, |
218 | | int red, int green, int blue, |
219 | 0 | int firstClassToTry) { |
220 | 0 | char pixel_value[100]; |
221 | 0 | colorObj color; |
222 | |
|
223 | 0 | color.red = red; |
224 | 0 | color.green = green; |
225 | 0 | color.blue = blue; |
226 | |
|
227 | 0 | snprintf(pixel_value, sizeof(pixel_value), "%18g", fValue); |
228 | |
|
229 | 0 | return msGetClass_String(layer, &color, pixel_value, firstClassToTry); |
230 | 0 | } |
231 | | |
232 | | /************************************************************************/ |
233 | | /* msRasterSetupTileLayer() */ |
234 | | /* */ |
235 | | /* Setup the tile layer. */ |
236 | | /************************************************************************/ |
237 | | |
238 | | int msDrawRasterSetupTileLayer(mapObj *map, layerObj *layer, |
239 | | rectObj *psearchrect, int is_query, |
240 | | int *ptilelayerindex, /* output */ |
241 | | int *ptileitemindex, /* output */ |
242 | | int *ptilesrsindex, /* output */ |
243 | 0 | layerObj **ptlp /* output */) { |
244 | 0 | int i; |
245 | 0 | char *requested_fields; |
246 | 0 | int status; |
247 | 0 | layerObj *tlp = NULL; |
248 | |
|
249 | 0 | *ptilelayerindex = msGetLayerIndex(layer->map, layer->tileindex); |
250 | 0 | if (*ptilelayerindex == |
251 | 0 | -1) { /* the tileindex references a file, not a layer */ |
252 | | |
253 | | /* so we create a temporary layer */ |
254 | 0 | tlp = (layerObj *)malloc(sizeof(layerObj)); |
255 | 0 | MS_CHECK_ALLOC(tlp, sizeof(layerObj), MS_FAILURE); |
256 | |
|
257 | 0 | initLayer(tlp, map); |
258 | 0 | *ptlp = tlp; |
259 | | |
260 | | /* set a few parameters for a very basic shapefile-based layer */ |
261 | 0 | tlp->name = msStrdup("TILE"); |
262 | 0 | tlp->type = MS_LAYER_TILEINDEX; |
263 | 0 | tlp->data = msStrdup(layer->tileindex); |
264 | |
|
265 | 0 | if (is_query) { |
266 | 0 | tlp->map = map; /*needed when scaletokens are applied, to extract current |
267 | | map scale */ |
268 | 0 | for (i = 0; i < layer->numscaletokens; i++) { |
269 | 0 | if (msGrowLayerScaletokens(tlp) == NULL) { |
270 | 0 | return MS_FAILURE; |
271 | 0 | } |
272 | 0 | initScaleToken(&tlp->scaletokens[i]); |
273 | 0 | msCopyScaleToken(&layer->scaletokens[i], &tlp->scaletokens[i]); |
274 | 0 | tlp->numscaletokens++; |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | 0 | if (layer->projection.numargs > 0 && |
279 | 0 | EQUAL(layer->projection.args[0], "auto")) { |
280 | 0 | tlp->projection.numargs = 1; |
281 | 0 | tlp->projection.args[0] = msStrdup("auto"); |
282 | 0 | } |
283 | |
|
284 | 0 | if (layer->filteritem) |
285 | 0 | tlp->filteritem = msStrdup(layer->filteritem); |
286 | 0 | if (layer->filter.string) { |
287 | 0 | if (layer->filter.type == MS_EXPRESSION) { |
288 | 0 | char *pszTmp = (char *)msSmallMalloc( |
289 | 0 | sizeof(char) * (strlen(layer->filter.string) + 3)); |
290 | 0 | sprintf(pszTmp, "(%s)", layer->filter.string); |
291 | 0 | msLoadExpressionString(&tlp->filter, pszTmp); |
292 | 0 | free(pszTmp); |
293 | 0 | } else if (layer->filter.type == MS_REGEX || |
294 | 0 | layer->filter.type == MS_IREGEX) { |
295 | 0 | char *pszTmp = (char *)msSmallMalloc( |
296 | 0 | sizeof(char) * (strlen(layer->filter.string) + 3)); |
297 | 0 | sprintf(pszTmp, "/%s/", layer->filter.string); |
298 | 0 | msLoadExpressionString(&tlp->filter, pszTmp); |
299 | 0 | free(pszTmp); |
300 | 0 | } else |
301 | 0 | msLoadExpressionString(&tlp->filter, layer->filter.string); |
302 | |
|
303 | 0 | tlp->filter.type = layer->filter.type; |
304 | 0 | } |
305 | |
|
306 | 0 | } else { |
307 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
308 | 0 | return MS_FAILURE; |
309 | 0 | tlp = (GET_LAYER(layer->map, *ptilelayerindex)); |
310 | 0 | *ptlp = tlp; |
311 | 0 | } |
312 | 0 | status = msLayerOpen(tlp); |
313 | 0 | if (status != MS_SUCCESS) { |
314 | 0 | return status; |
315 | 0 | } |
316 | | |
317 | | /* fetch tileitem and tilesrs fields */ |
318 | 0 | requested_fields = (char *)msSmallMalloc( |
319 | 0 | sizeof(char) * (strlen(layer->tileitem) + 1 + |
320 | 0 | (layer->tilesrs ? strlen(layer->tilesrs) : 0) + 1)); |
321 | 0 | if (layer->tilesrs != NULL) |
322 | 0 | sprintf(requested_fields, "%s,%s", layer->tileitem, layer->tilesrs); |
323 | 0 | else |
324 | 0 | strcpy(requested_fields, layer->tileitem); |
325 | 0 | status = msLayerWhichItems(tlp, MS_FALSE, requested_fields); |
326 | 0 | msFree(requested_fields); |
327 | 0 | if (status != MS_SUCCESS) { |
328 | 0 | return status; |
329 | 0 | } |
330 | | |
331 | | /* get the tileitem and tilesrs index */ |
332 | 0 | for (i = 0; i < tlp->numitems; i++) { |
333 | 0 | if (strcasecmp(tlp->items[i], layer->tileitem) == 0) { |
334 | 0 | *ptileitemindex = i; |
335 | 0 | } |
336 | 0 | if (layer->tilesrs != NULL && |
337 | 0 | strcasecmp(tlp->items[i], layer->tilesrs) == 0) { |
338 | 0 | *ptilesrsindex = i; |
339 | 0 | } |
340 | 0 | } |
341 | 0 | if (*ptileitemindex < 0) { /* didn't find it */ |
342 | 0 | msSetError(MS_MEMERR, "Could not find attribute %s in tileindex.", |
343 | 0 | "msDrawRasterLayerLow()", layer->tileitem); |
344 | 0 | return MS_FAILURE; |
345 | 0 | } |
346 | 0 | if (layer->tilesrs != NULL && *ptilesrsindex < 0) { /* didn't find it */ |
347 | 0 | msSetError(MS_MEMERR, "Could not find attribute %s in tileindex.", |
348 | 0 | "msDrawRasterLayerLow()", layer->tilesrs); |
349 | 0 | return MS_FAILURE; |
350 | 0 | } |
351 | | |
352 | | /* if necessary, project the searchrect to source coords */ |
353 | 0 | if ((map->projection.numargs > 0) && (layer->projection.numargs > 0) && |
354 | 0 | !EQUAL(layer->projection.args[0], "auto")) { |
355 | 0 | if (msProjectRect(&map->projection, &layer->projection, psearchrect) != |
356 | 0 | MS_SUCCESS) { |
357 | 0 | msDebug("msDrawRasterLayerLow(%s): unable to reproject map request " |
358 | 0 | "rectangle into layer projection, canceling.\n", |
359 | 0 | layer->name); |
360 | 0 | return MS_FAILURE; |
361 | 0 | } |
362 | 0 | } else if ((map->projection.numargs > 0) && (tlp->projection.numargs > 0) && |
363 | 0 | !EQUAL(tlp->projection.args[0], "auto")) { |
364 | 0 | if (msProjectRect(&map->projection, &tlp->projection, psearchrect) != |
365 | 0 | MS_SUCCESS) { |
366 | 0 | msDebug("msDrawRasterLayerLow(%s): unable to reproject map request " |
367 | 0 | "rectangle into layer projection, canceling.\n", |
368 | 0 | layer->name); |
369 | 0 | return MS_FAILURE; |
370 | 0 | } |
371 | 0 | } |
372 | 0 | return msLayerWhichShapes(tlp, *psearchrect, MS_FALSE); |
373 | 0 | } |
374 | | |
375 | | /************************************************************************/ |
376 | | /* msDrawRasterCleanupTileLayer() */ |
377 | | /* */ |
378 | | /* Cleanup the tile layer. */ |
379 | | /************************************************************************/ |
380 | | |
381 | 0 | void msDrawRasterCleanupTileLayer(layerObj *tlp, int tilelayerindex) { |
382 | 0 | msLayerClose(tlp); |
383 | 0 | if (tilelayerindex == -1) { |
384 | 0 | freeLayer(tlp); |
385 | 0 | free(tlp); |
386 | 0 | } |
387 | 0 | } |
388 | | |
389 | | /************************************************************************/ |
390 | | /* msDrawRasterIterateTileIndex() */ |
391 | | /* */ |
392 | | /* Iterate over the tile layer. */ |
393 | | /************************************************************************/ |
394 | | |
395 | | int msDrawRasterIterateTileIndex(layerObj *layer, layerObj *tlp, |
396 | | shapeObj *ptshp, /* input-output */ |
397 | | int tileitemindex, int tilesrsindex, |
398 | | char *tilename, /* output */ |
399 | | size_t sizeof_tilename, |
400 | | char *tilesrsname, /* output */ |
401 | 0 | size_t sizeof_tilesrsname) { |
402 | 0 | int status; |
403 | |
|
404 | 0 | status = msLayerNextShape(tlp, ptshp); |
405 | 0 | if (status == MS_FAILURE || status == MS_DONE) { |
406 | 0 | return status; |
407 | 0 | } |
408 | | |
409 | 0 | if (layer->data == NULL || |
410 | 0 | strlen(layer->data) == |
411 | 0 | 0) { /* assume whole filename is in attribute field */ |
412 | 0 | strlcpy(tilename, ptshp->values[tileitemindex], sizeof_tilename); |
413 | 0 | } else |
414 | 0 | snprintf(tilename, sizeof_tilename, "%s/%s", ptshp->values[tileitemindex], |
415 | 0 | layer->data); |
416 | |
|
417 | 0 | tilesrsname[0] = '\0'; |
418 | |
|
419 | 0 | if (tilesrsindex >= 0) { |
420 | 0 | if (ptshp->values[tilesrsindex] != NULL) |
421 | 0 | strlcpy(tilesrsname, ptshp->values[tilesrsindex], sizeof_tilesrsname); |
422 | 0 | } |
423 | |
|
424 | 0 | msFreeShape(ptshp); /* done with the shape */ |
425 | |
|
426 | 0 | return status; |
427 | 0 | } |
428 | | |
429 | | /************************************************************************/ |
430 | | /* msDrawRasterBuildRasterPath() */ |
431 | | /* */ |
432 | | /* Build the path of the raster to open. */ |
433 | | /************************************************************************/ |
434 | | |
435 | | int msDrawRasterBuildRasterPath(mapObj *map, layerObj *layer, |
436 | | const char *filename, |
437 | 0 | char szPath[MS_MAXPATHLEN] /* output */) { |
438 | | /* |
439 | | ** If using a tileindex then build the path relative to that file if SHAPEPATH |
440 | | * is not set, provided that layer->tileindex does not refer to a layer (to |
441 | | * save a useless file opening attempt) |
442 | | */ |
443 | 0 | if (layer->tileindex && !map->shapepath && |
444 | 0 | msGetLayerIndex(map, layer->tileindex) < 0) { |
445 | 0 | char tiAbsFilePath[MS_MAXPATHLEN]; |
446 | 0 | char *tiAbsDirPath = NULL; |
447 | |
|
448 | 0 | msTryBuildPath(tiAbsFilePath, map->mappath, |
449 | 0 | layer->tileindex); /* absolute path to tileindex file */ |
450 | 0 | tiAbsDirPath = msGetPath(tiAbsFilePath); /* tileindex file's directory */ |
451 | 0 | msBuildPath(szPath, tiAbsDirPath, filename); |
452 | 0 | free(tiAbsDirPath); |
453 | 0 | } else { |
454 | 0 | msTryBuildPath3(szPath, map->mappath, map->shapepath, filename); |
455 | 0 | } |
456 | |
|
457 | 0 | return MS_SUCCESS; |
458 | 0 | } |
459 | | |
460 | | /************************************************************************/ |
461 | | /* msDrawRasterGetCPLErrorMsg() */ |
462 | | /* */ |
463 | | /* Return the CPL error message, and filter out sensitive info. */ |
464 | | /************************************************************************/ |
465 | | |
466 | | const char *msDrawRasterGetCPLErrorMsg(const char *decrypted_path, |
467 | 0 | const char *szPath) { |
468 | 0 | const char *cpl_error_msg = CPLGetLastErrorMsg(); |
469 | | |
470 | | /* we wish to avoid reporting decrypted paths */ |
471 | 0 | if (cpl_error_msg != NULL && strstr(cpl_error_msg, decrypted_path) != NULL && |
472 | 0 | strcmp(decrypted_path, szPath) != 0) |
473 | 0 | cpl_error_msg = NULL; |
474 | | |
475 | | /* we wish to avoid reporting the stock GDALOpen error messages */ |
476 | 0 | if (cpl_error_msg != NULL && |
477 | 0 | (strstr(cpl_error_msg, "not recognised as a supported") != NULL || |
478 | 0 | strstr(cpl_error_msg, "does not exist") != NULL)) |
479 | 0 | cpl_error_msg = NULL; |
480 | |
|
481 | 0 | if (cpl_error_msg == NULL) |
482 | 0 | cpl_error_msg = ""; |
483 | |
|
484 | 0 | return cpl_error_msg; |
485 | 0 | } |
486 | | |
487 | | /************************************************************************/ |
488 | | /* msDrawRasterLoadProjection() */ |
489 | | /* */ |
490 | | /* Handle TILESRS or PROJECTION AUTO for each tile. */ |
491 | | /************************************************************************/ |
492 | | |
493 | | int msDrawRasterLoadProjection(layerObj *layer, GDALDatasetH hDS, |
494 | | const char *filename, int tilesrsindex, |
495 | 0 | const char *tilesrsname) { |
496 | | /* |
497 | | ** Generate the projection information if using TILESRS. |
498 | | */ |
499 | 0 | if (tilesrsindex >= 0) { |
500 | 0 | const char *pszWKT; |
501 | 0 | if (tilesrsname[0] != '\0') |
502 | 0 | pszWKT = tilesrsname; |
503 | 0 | else |
504 | 0 | pszWKT = GDALGetProjectionRef(hDS); |
505 | 0 | if (pszWKT != NULL && strlen(pszWKT) > 0) { |
506 | 0 | if (msOGCWKT2ProjectionObj(pszWKT, &(layer->projection), layer->debug) != |
507 | 0 | MS_SUCCESS) { |
508 | 0 | char szLongMsg[MESSAGELENGTH * 2]; |
509 | 0 | errorObj *ms_error = msGetErrorObj(); |
510 | |
|
511 | 0 | snprintf(szLongMsg, sizeof(szLongMsg), |
512 | 0 | "%s\n" |
513 | 0 | "PROJECTION '%s' cannot be used for this " |
514 | 0 | "GDAL raster (`%s').", |
515 | 0 | ms_error->message, pszWKT, filename); |
516 | 0 | szLongMsg[MESSAGELENGTH - 1] = '\0'; |
517 | |
|
518 | 0 | msSetError(MS_OGRERR, "%s", "msDrawRasterLayer()", szLongMsg); |
519 | |
|
520 | 0 | return MS_FAILURE; |
521 | 0 | } |
522 | 0 | } |
523 | 0 | } |
524 | | /* |
525 | | ** Generate the projection information if using AUTO. |
526 | | */ |
527 | 0 | else if (layer->projection.numargs > 0 && |
528 | 0 | EQUAL(layer->projection.args[0], "auto")) { |
529 | 0 | const char *pszWKT; |
530 | |
|
531 | 0 | pszWKT = GDALGetProjectionRef(hDS); |
532 | |
|
533 | 0 | if (pszWKT != NULL && strlen(pszWKT) > 0) { |
534 | 0 | if (msOGCWKT2ProjectionObj(pszWKT, &(layer->projection), layer->debug) != |
535 | 0 | MS_SUCCESS) { |
536 | 0 | char szLongMsg[MESSAGELENGTH * 2]; |
537 | 0 | errorObj *ms_error = msGetErrorObj(); |
538 | |
|
539 | 0 | snprintf(szLongMsg, sizeof(szLongMsg), |
540 | 0 | "%s\n" |
541 | 0 | "PROJECTION AUTO cannot be used for this " |
542 | 0 | "GDAL raster (`%s').", |
543 | 0 | ms_error->message, filename); |
544 | 0 | szLongMsg[MESSAGELENGTH - 1] = '\0'; |
545 | |
|
546 | 0 | msSetError(MS_OGRERR, "%s", "msDrawRasterLayer()", szLongMsg); |
547 | |
|
548 | 0 | return MS_FAILURE; |
549 | 0 | } |
550 | 0 | } |
551 | 0 | } |
552 | | |
553 | 0 | return MS_SUCCESS; |
554 | 0 | } |
555 | | |
556 | | typedef enum { |
557 | | CDRT_OK, |
558 | | CDRT_RETURN_MS_FAILURE, |
559 | | CDRT_CONTINUE_NEXT_TILE |
560 | | } CheckDatasetReturnType; |
561 | | |
562 | | /************************************************************************/ |
563 | | /* msDrawRasterLayerLowCheckDataset() */ |
564 | | /************************************************************************/ |
565 | | |
566 | | static CheckDatasetReturnType |
567 | | msDrawRasterLayerLowCheckDataset(mapObj *map, layerObj *layer, GDALDatasetH hDS, |
568 | | const char *decrypted_path, |
569 | 0 | const char *szPath) { |
570 | | /* |
571 | | ** If GDAL doesn't recognise it, and it wasn't successfully opened |
572 | | ** Generate an error. |
573 | | */ |
574 | 0 | if (hDS == NULL) { |
575 | 0 | int ignore_missing = msMapIgnoreMissingData(map); |
576 | 0 | const char *cpl_error_msg = |
577 | 0 | msDrawRasterGetCPLErrorMsg(decrypted_path, szPath); |
578 | |
|
579 | 0 | if (ignore_missing == MS_MISSING_DATA_FAIL) { |
580 | 0 | msSetError(MS_IOERR, |
581 | 0 | "Corrupt, empty or missing file '%s' for layer '%s'. %s", |
582 | 0 | "msDrawRasterLayerLow()", szPath, layer->name, cpl_error_msg); |
583 | 0 | return (CDRT_RETURN_MS_FAILURE); |
584 | 0 | } else if (ignore_missing == MS_MISSING_DATA_LOG) { |
585 | 0 | if (layer->debug || layer->map->debug) { |
586 | 0 | msDebug("Corrupt, empty or missing file '%s' for layer '%s' ... " |
587 | 0 | "ignoring this missing data. %s\n", |
588 | 0 | szPath, layer->name, cpl_error_msg); |
589 | 0 | } |
590 | 0 | return (CDRT_CONTINUE_NEXT_TILE); |
591 | 0 | } else if (ignore_missing == MS_MISSING_DATA_IGNORE) { |
592 | 0 | return (CDRT_CONTINUE_NEXT_TILE); |
593 | 0 | } else { |
594 | | /* never get here */ |
595 | 0 | msSetError(MS_IOERR, "msIgnoreMissingData returned unexpected value.", |
596 | 0 | "msDrawRasterLayerLow()"); |
597 | 0 | return (CDRT_RETURN_MS_FAILURE); |
598 | 0 | } |
599 | 0 | } |
600 | | |
601 | 0 | return CDRT_OK; |
602 | 0 | } |
603 | | |
604 | | /************************************************************************/ |
605 | | /* msDrawRasterLayerLowOpenDataset() */ |
606 | | /************************************************************************/ |
607 | | |
608 | | void *msDrawRasterLayerLowOpenDataset(mapObj *map, layerObj *layer, |
609 | | const char *filename, |
610 | | char szPath[MS_MAXPATHLEN], |
611 | 0 | char **p_decrypted_path) { |
612 | 0 | const char *pszPath; |
613 | |
|
614 | 0 | msGDALInitialize(); |
615 | |
|
616 | 0 | if (layer->debug) |
617 | 0 | msDebug("msDrawRasterLayerLow(%s): Filename is: %s\n", layer->name, |
618 | 0 | filename); |
619 | |
|
620 | 0 | if (strncmp(filename, "<VRTDataset", strlen("<VRTDataset")) == 0) { |
621 | 0 | pszPath = filename; |
622 | 0 | } else { |
623 | 0 | msDrawRasterBuildRasterPath(map, layer, filename, szPath); |
624 | 0 | pszPath = szPath; |
625 | 0 | } |
626 | 0 | if (layer->debug) |
627 | 0 | msDebug("msDrawRasterLayerLow(%s): Path is: %s\n", layer->name, pszPath); |
628 | | |
629 | | /* |
630 | | ** Note: because we do decryption after the above path expansion |
631 | | ** which depends on actually finding a file, it essentially means that |
632 | | ** fancy path manipulation is essentially disabled when using encrypted |
633 | | ** components. But that is mostly ok, since stuff like sde,postgres and |
634 | | ** oracle georaster do not use real paths. |
635 | | */ |
636 | 0 | *p_decrypted_path = msDecryptStringTokens(map, pszPath); |
637 | 0 | if (*p_decrypted_path == NULL) |
638 | 0 | return NULL; |
639 | | |
640 | 0 | msAcquireLock(TLOCK_GDAL); |
641 | 0 | if (!layer->tileindex) { |
642 | 0 | char **connectionoptions = |
643 | 0 | msGetStringListFromHashTable(&(layer->connectionoptions)); |
644 | 0 | char **papszAllowedDrivers = NULL; |
645 | | // ALLOWED_GDAL_DRIVERS typically set by msDrawWMSLayerLow() |
646 | 0 | const char *pszAllowedDrivers = |
647 | 0 | msLayerGetProcessingKey(layer, "ALLOWED_GDAL_DRIVERS"); |
648 | 0 | if (pszAllowedDrivers && !EQUAL(pszAllowedDrivers, "*")) |
649 | 0 | papszAllowedDrivers = CSLTokenizeString2(pszAllowedDrivers, ",", 0); |
650 | 0 | GDALDatasetH hDS = |
651 | 0 | GDALOpenEx(*p_decrypted_path, GDAL_OF_RASTER | GDAL_OF_SHARED, |
652 | 0 | (const char *const *)papszAllowedDrivers, |
653 | 0 | (const char *const *)connectionoptions, NULL); |
654 | 0 | CSLDestroy(papszAllowedDrivers); |
655 | 0 | CSLDestroy(connectionoptions); |
656 | | |
657 | | // Give a hint about which GDAL driver should be enabled, but only in |
658 | | // debug mode. |
659 | 0 | if (layer->debug && hDS == NULL && pszAllowedDrivers && |
660 | 0 | !EQUAL(pszAllowedDrivers, "*")) { |
661 | 0 | GDALDriverH hDrv = GDALIdentifyDriver(*p_decrypted_path, NULL); |
662 | 0 | if (hDrv) { |
663 | 0 | const char *pszDrvName = GDALGetDescription(hDrv); |
664 | 0 | bool bFound = false; |
665 | 0 | for (int i = 0; papszAllowedDrivers && papszAllowedDrivers[i]; ++i) { |
666 | 0 | if (EQUAL(papszAllowedDrivers[i], pszDrvName)) { |
667 | 0 | bFound = true; |
668 | 0 | break; |
669 | 0 | } |
670 | 0 | } |
671 | 0 | if (!bFound) { |
672 | 0 | msSetError(MS_IMGERR, |
673 | 0 | "Failed to draw layer named '%s'. The image returned " |
674 | 0 | "is recognized by GDAL driver %s, but it is not allowed " |
675 | 0 | "currently.", |
676 | 0 | "msDrawRasterLayerLowOpenDataset()", layer->name, |
677 | 0 | pszDrvName); |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } |
681 | 0 | return hDS; |
682 | 0 | } else { |
683 | 0 | return GDALOpenShared(*p_decrypted_path, GA_ReadOnly); |
684 | 0 | } |
685 | 0 | } |
686 | | |
687 | | /************************************************************************/ |
688 | | /* msDrawRasterLayerLowCloseDataset() */ |
689 | | /************************************************************************/ |
690 | | |
691 | 0 | void msDrawRasterLayerLowCloseDataset(layerObj *layer, void *hDS) { |
692 | 0 | if (hDS) { |
693 | 0 | const char *close_connection; |
694 | 0 | close_connection = msLayerGetProcessingKey(layer, "CLOSE_CONNECTION"); |
695 | |
|
696 | 0 | if (close_connection == NULL && layer->tileindex == NULL) |
697 | 0 | close_connection = "DEFER"; |
698 | |
|
699 | 0 | { |
700 | | /* Due to how GDAL processes OVERVIEW_LEVEL, datasets returned are */ |
701 | | /* not shared, despite being asked to, so close them for real */ |
702 | 0 | char **connectionoptions = |
703 | 0 | msGetStringListFromHashTable(&(layer->connectionoptions)); |
704 | 0 | if (CSLFetchNameValue(connectionoptions, "OVERVIEW_LEVEL")) |
705 | 0 | close_connection = NULL; |
706 | 0 | CSLDestroy(connectionoptions); |
707 | 0 | } |
708 | |
|
709 | 0 | if (close_connection != NULL && |
710 | 0 | strcasecmp(close_connection, "DEFER") == 0) { |
711 | 0 | GDALDereferenceDataset((GDALDatasetH)hDS); |
712 | 0 | } else { |
713 | 0 | GDALClose((GDALDatasetH)hDS); |
714 | 0 | } |
715 | 0 | msReleaseLock(TLOCK_GDAL); |
716 | 0 | } |
717 | 0 | } |
718 | | |
719 | | /************************************************************************/ |
720 | | /* msDrawRasterLayerLowCheckIfMustDraw() */ |
721 | | /* */ |
722 | | /* Return 1 if the layer should be drawn. */ |
723 | | /************************************************************************/ |
724 | | |
725 | 0 | int msDrawRasterLayerLowCheckIfMustDraw(mapObj *map, layerObj *layer) { |
726 | 0 | if (!layer->data && !layer->tileindex && |
727 | 0 | !(layer->connectiontype == MS_KERNELDENSITY || |
728 | 0 | layer->connectiontype == MS_IDW)) { |
729 | 0 | if (layer->debug) |
730 | 0 | msDebug("msDrawRasterLayerLow(%s): layer data and tileindex NULL ... " |
731 | 0 | "doing nothing.", |
732 | 0 | layer->name); |
733 | 0 | return (0); |
734 | 0 | } |
735 | | |
736 | 0 | if ((layer->status != MS_ON) && (layer->status != MS_DEFAULT)) { |
737 | 0 | if (layer->debug) |
738 | 0 | msDebug( |
739 | 0 | "msDrawRasterLayerLow(%s): not status ON or DEFAULT, doing nothing.", |
740 | 0 | layer->name); |
741 | 0 | return (0); |
742 | 0 | } |
743 | | |
744 | 0 | if (map->scaledenom > 0) { |
745 | 0 | if ((layer->maxscaledenom > 0) && |
746 | 0 | (map->scaledenom > layer->maxscaledenom)) { |
747 | 0 | if (layer->debug) |
748 | 0 | msDebug("msDrawRasterLayerLow(%s): skipping, map scale %.2g > " |
749 | 0 | "MAXSCALEDENOM=%g\n", |
750 | 0 | layer->name, map->scaledenom, layer->maxscaledenom); |
751 | 0 | return (0); |
752 | 0 | } |
753 | 0 | if ((layer->minscaledenom > 0) && |
754 | 0 | (map->scaledenom <= layer->minscaledenom)) { |
755 | 0 | if (layer->debug) |
756 | 0 | msDebug("msDrawRasterLayerLow(%s): skipping, map scale %.2g < " |
757 | 0 | "MINSCALEDENOM=%g\n", |
758 | 0 | layer->name, map->scaledenom, layer->minscaledenom); |
759 | 0 | return (0); |
760 | 0 | } |
761 | 0 | } |
762 | | |
763 | 0 | if (layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) { |
764 | 0 | if ((layer->maxgeowidth > 0) && |
765 | 0 | ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) { |
766 | 0 | if (layer->debug) |
767 | 0 | msDebug("msDrawRasterLayerLow(%s): skipping, map width %.2g > " |
768 | 0 | "MAXSCALEDENOM=%g\n", |
769 | 0 | layer->name, (map->extent.maxx - map->extent.minx), |
770 | 0 | layer->maxgeowidth); |
771 | 0 | return (0); |
772 | 0 | } |
773 | 0 | if ((layer->mingeowidth > 0) && |
774 | 0 | ((map->extent.maxx - map->extent.minx) < layer->mingeowidth)) { |
775 | 0 | if (layer->debug) |
776 | 0 | msDebug("msDrawRasterLayerLow(%s): skipping, map width %.2g < " |
777 | 0 | "MINSCALEDENOM=%g\n", |
778 | 0 | layer->name, (map->extent.maxx - map->extent.minx), |
779 | 0 | layer->mingeowidth); |
780 | 0 | return (0); |
781 | 0 | } |
782 | 0 | } |
783 | | |
784 | 0 | return 1; |
785 | 0 | } |
786 | | |
787 | | /************************************************************************/ |
788 | | /* msDrawRasterLayerLow() */ |
789 | | /* */ |
790 | | /* Check for various file types and act appropriately. Handle */ |
791 | | /* tile indexing. */ |
792 | | /************************************************************************/ |
793 | | |
794 | | int msDrawRasterLayerLow(mapObj *map, layerObj *layer, imageObj *image, |
795 | 0 | rasterBufferObj *rb) { |
796 | 0 | return msDrawRasterLayerLowWithDataset(map, layer, image, rb, NULL); |
797 | 0 | } |
798 | | |
799 | | int msDrawRasterLayerLowWithDataset(mapObj *map, layerObj *layer, |
800 | | imageObj *image, rasterBufferObj *rb, |
801 | 0 | void *hDatasetIn) { |
802 | | /* -------------------------------------------------------------------- */ |
803 | | /* As of MapServer 6.0 GDAL is required for rendering raster */ |
804 | | /* imagery. */ |
805 | | /* -------------------------------------------------------------------- */ |
806 | 0 | int status, done; |
807 | 0 | char *filename = NULL, tilename[MS_MAXPATHLEN], tilesrsname[1024]; |
808 | |
|
809 | 0 | layerObj *tlp = NULL; /* pointer to the tile layer either real or temporary */ |
810 | 0 | int tileitemindex = -1, tilelayerindex = -1, tilesrsindex = -1; |
811 | 0 | shapeObj tshp; |
812 | |
|
813 | 0 | char szPath[MS_MAXPATHLEN] = {0}; |
814 | 0 | char *decrypted_path = NULL; |
815 | 0 | int final_status = MS_SUCCESS; |
816 | |
|
817 | 0 | rectObj searchrect; |
818 | 0 | GDALDatasetH hDS; |
819 | 0 | double adfGeoTransform[6]; |
820 | 0 | void *kernel_density_cleanup_ptr = NULL; |
821 | |
|
822 | 0 | if (layer->debug > 0 || map->debug > 1) |
823 | 0 | msDebug("msDrawRasterLayerLow(%s): entering.\n", layer->name); |
824 | |
|
825 | 0 | if (hDatasetIn == NULL && !msDrawRasterLayerLowCheckIfMustDraw(map, layer)) { |
826 | 0 | return MS_SUCCESS; |
827 | 0 | } |
828 | | |
829 | 0 | msGDALInitialize(); |
830 | |
|
831 | 0 | if (layer->tileindex) { /* we have an index file */ |
832 | 0 | msInitShape(&tshp); |
833 | 0 | searchrect = map->extent; |
834 | |
|
835 | 0 | status = msDrawRasterSetupTileLayer(map, layer, &searchrect, MS_FALSE, |
836 | 0 | &tilelayerindex, &tileitemindex, |
837 | 0 | &tilesrsindex, &tlp); |
838 | 0 | if (status != MS_SUCCESS) { |
839 | 0 | if (status != MS_DONE) |
840 | 0 | final_status = status; |
841 | 0 | goto cleanup; |
842 | 0 | } |
843 | 0 | } |
844 | | |
845 | 0 | done = MS_FALSE; |
846 | 0 | while (done != MS_TRUE) { |
847 | |
|
848 | 0 | if (layer->tileindex) { |
849 | 0 | status = msDrawRasterIterateTileIndex( |
850 | 0 | layer, tlp, &tshp, tileitemindex, tilesrsindex, tilename, |
851 | 0 | sizeof(tilename), tilesrsname, sizeof(tilesrsname)); |
852 | 0 | if (status == MS_FAILURE) { |
853 | 0 | final_status = MS_FAILURE; |
854 | 0 | break; |
855 | 0 | } |
856 | | |
857 | 0 | if (status == MS_DONE) |
858 | 0 | break; /* no more tiles/images */ |
859 | 0 | filename = tilename; |
860 | 0 | } else { |
861 | 0 | filename = layer->data; |
862 | 0 | done = MS_TRUE; /* only one image so we're done after this */ |
863 | 0 | } |
864 | | |
865 | 0 | if (layer->connectiontype == MS_KERNELDENSITY || |
866 | 0 | layer->connectiontype == MS_IDW) { |
867 | 0 | msAcquireLock(TLOCK_GDAL); |
868 | 0 | status = msInterpolationDataset(map, image, layer, &hDS, |
869 | 0 | &kernel_density_cleanup_ptr); |
870 | 0 | if (status != MS_SUCCESS) { |
871 | 0 | msReleaseLock(TLOCK_GDAL); |
872 | 0 | final_status = status; |
873 | 0 | goto cleanup; |
874 | 0 | } |
875 | 0 | done = MS_TRUE; |
876 | 0 | if (msProjectionsDiffer(&map->projection, &layer->projection)) { |
877 | 0 | char *mapProjStr = msGetProjectionString(&(map->projection)); |
878 | | |
879 | | /* Set the projection to the map file projection */ |
880 | 0 | if (msLoadProjectionString(&(layer->projection), mapProjStr) != 0) { |
881 | 0 | GDALClose(hDS); |
882 | 0 | msReleaseLock(TLOCK_GDAL); |
883 | 0 | msSetError(MS_CGIERR, |
884 | 0 | "Unable to set projection on interpolation layer.", |
885 | 0 | "msDrawRasterLayerLow()"); |
886 | 0 | return (MS_FAILURE); |
887 | 0 | } |
888 | 0 | free(mapProjStr); |
889 | 0 | } |
890 | 0 | } else { |
891 | 0 | if (strlen(filename) == 0) |
892 | 0 | continue; |
893 | 0 | if (hDatasetIn) { |
894 | 0 | hDS = (GDALDatasetH)hDatasetIn; |
895 | 0 | } else { |
896 | 0 | hDS = (GDALDatasetH)msDrawRasterLayerLowOpenDataset( |
897 | 0 | map, layer, filename, szPath, &decrypted_path); |
898 | 0 | } |
899 | 0 | } |
900 | | |
901 | 0 | if (hDatasetIn == NULL) { |
902 | 0 | CheckDatasetReturnType eRet = msDrawRasterLayerLowCheckDataset( |
903 | 0 | map, layer, hDS, decrypted_path, szPath); |
904 | |
|
905 | 0 | msFree(decrypted_path); |
906 | 0 | decrypted_path = NULL; |
907 | |
|
908 | 0 | if (eRet == CDRT_CONTINUE_NEXT_TILE) { |
909 | 0 | msReleaseLock(TLOCK_GDAL); |
910 | 0 | continue; |
911 | 0 | } |
912 | 0 | if (eRet == CDRT_RETURN_MS_FAILURE) { |
913 | 0 | msReleaseLock(TLOCK_GDAL); |
914 | 0 | return MS_FAILURE; |
915 | 0 | } |
916 | 0 | } |
917 | | |
918 | 0 | if (msDrawRasterLoadProjection(layer, hDS, filename, tilesrsindex, |
919 | 0 | tilesrsname) != MS_SUCCESS) { |
920 | 0 | if (hDatasetIn == NULL) { |
921 | 0 | GDALClose(hDS); |
922 | 0 | msReleaseLock(TLOCK_GDAL); |
923 | 0 | } |
924 | 0 | final_status = MS_FAILURE; |
925 | 0 | break; |
926 | 0 | } |
927 | | |
928 | 0 | msGetGDALGeoTransform(hDS, map, layer, adfGeoTransform); |
929 | | |
930 | | /* |
931 | | ** We want to resample if the source image is rotated, if |
932 | | ** the projections differ or if resampling has been explicitly |
933 | | ** requested, or if the image has north-down instead of north-up. |
934 | | */ |
935 | |
|
936 | 0 | if (((adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 || |
937 | 0 | adfGeoTransform[5] > 0.0 || adfGeoTransform[1] < 0.0) && |
938 | 0 | layer->transform) || |
939 | 0 | msProjectionsDiffer(&(map->projection), &(layer->projection)) || |
940 | 0 | CSLFetchNameValue(layer->processing, "RESAMPLE") != NULL) { |
941 | 0 | status = msResampleGDALToMap(map, layer, image, rb, hDS); |
942 | 0 | } else { |
943 | 0 | if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0) { |
944 | 0 | if (layer->debug || map->debug) |
945 | 0 | msDebug("Layer %s has rotational coefficients but we\n" |
946 | 0 | "are unable to use them, projections support\n" |
947 | 0 | "needs to be built in.", |
948 | 0 | layer->name); |
949 | 0 | } |
950 | 0 | status = msDrawRasterLayerGDAL(map, layer, image, rb, hDS); |
951 | 0 | } |
952 | |
|
953 | 0 | if (status == -1) { |
954 | 0 | if (hDatasetIn == NULL) { |
955 | 0 | GDALClose(hDS); |
956 | 0 | msReleaseLock(TLOCK_GDAL); |
957 | 0 | } |
958 | 0 | final_status = MS_FAILURE; |
959 | 0 | break; |
960 | 0 | } |
961 | | |
962 | | /* |
963 | | ** Should we keep this file open for future use? |
964 | | ** default to keeping open for single data files, and |
965 | | ** to closing for tile indexes |
966 | | */ |
967 | 0 | if (layer->connectiontype == MS_KERNELDENSITY || |
968 | 0 | layer->connectiontype == MS_IDW) { |
969 | | /* |
970 | | ** Fix issue #5330 |
971 | | ** The in-memory kernel density heatmap gdal dataset handle (hDS) gets |
972 | | *re-used |
973 | | ** but the associated rasterband cache doesn't get flushed, which causes |
974 | | *old data |
975 | | ** to be rendered instead of the newly generated imagery. To fix, simply |
976 | | *close the |
977 | | ** the handle and prevent further re-use. |
978 | | ** Note that instead of this workaround, we could explicitly set |
979 | | ** CLOSE_CONNECTION=ALWAYS on the kerneldensity layer. |
980 | | */ |
981 | 0 | GDALClose(hDS); |
982 | 0 | msReleaseLock(TLOCK_GDAL); |
983 | 0 | } else { |
984 | 0 | if (hDatasetIn == NULL) |
985 | 0 | msDrawRasterLayerLowCloseDataset(layer, hDS); |
986 | 0 | } |
987 | |
|
988 | 0 | } /* next tile */ |
989 | | |
990 | 0 | cleanup: |
991 | 0 | if (layer->tileindex) { /* tiling clean-up */ |
992 | 0 | msDrawRasterCleanupTileLayer(tlp, tilelayerindex); |
993 | 0 | } |
994 | 0 | if (kernel_density_cleanup_ptr) { |
995 | 0 | msCleanupInterpolationDataset(map, image, layer, |
996 | 0 | kernel_density_cleanup_ptr); |
997 | 0 | } |
998 | |
|
999 | 0 | return final_status; |
1000 | 0 | } |
1001 | | |
1002 | | /************************************************************************/ |
1003 | | /* msDrawReferenceMap() */ |
1004 | | /************************************************************************/ |
1005 | | |
1006 | 0 | imageObj *msDrawReferenceMap(mapObj *map) { |
1007 | 0 | double cellsize; |
1008 | 0 | int x1, y1, x2, y2; |
1009 | 0 | char szPath[MS_MAXPATHLEN]; |
1010 | 0 | int status = MS_SUCCESS; |
1011 | |
|
1012 | 0 | imageObj *image = NULL; |
1013 | 0 | styleObj style; |
1014 | | |
1015 | | /* check to see if we have enough information to actually proceed */ |
1016 | 0 | if (!map->reference.image || map->reference.height == 0 || |
1017 | 0 | map->reference.width == 0) { |
1018 | 0 | msSetError(MS_MISCERR, "Reference map configuration error.", |
1019 | 0 | "msDrawReferenceMap()"); |
1020 | 0 | return NULL; |
1021 | 0 | } |
1022 | | |
1023 | 0 | rendererVTableObj *renderer = MS_MAP_RENDERER(map); |
1024 | 0 | rasterBufferObj *refImage = |
1025 | 0 | (rasterBufferObj *)calloc(1, sizeof(rasterBufferObj)); |
1026 | 0 | MS_CHECK_ALLOC(refImage, sizeof(rasterBufferObj), NULL); |
1027 | |
|
1028 | 0 | if (MS_SUCCESS != |
1029 | 0 | renderer->loadImageFromFile( |
1030 | 0 | msBuildPath(szPath, map->mappath, map->reference.image), refImage)) { |
1031 | 0 | msSetError(MS_MISCERR, "Error loading reference image %s.", |
1032 | 0 | "msDrawReferenceMap()", szPath); |
1033 | 0 | free(refImage); |
1034 | 0 | return NULL; |
1035 | 0 | } |
1036 | | |
1037 | 0 | image = msImageCreate(refImage->width, refImage->height, map->outputformat, |
1038 | 0 | map->web.imagepath, map->web.imageurl, map->resolution, |
1039 | 0 | map->defresolution, &(map->reference.color)); |
1040 | 0 | if (!image) { |
1041 | 0 | free(refImage); |
1042 | 0 | return NULL; |
1043 | 0 | } |
1044 | | |
1045 | 0 | status = renderer->mergeRasterBuffer(image, refImage, 1.0, 0, 0, 0, 0, |
1046 | 0 | refImage->width, refImage->height); |
1047 | 0 | msFreeRasterBuffer(refImage); |
1048 | 0 | free(refImage); |
1049 | 0 | if (MS_UNLIKELY(status == MS_FAILURE)) |
1050 | 0 | return NULL; |
1051 | | |
1052 | | /* make sure the extent given in mapfile fits the image */ |
1053 | 0 | cellsize = |
1054 | 0 | msAdjustExtent(&(map->reference.extent), image->width, image->height); |
1055 | | |
1056 | | /* convert map extent to reference image coordinates */ |
1057 | 0 | x1 = MS_MAP2IMAGE_X(map->extent.minx, map->reference.extent.minx, cellsize); |
1058 | 0 | x2 = MS_MAP2IMAGE_X(map->extent.maxx, map->reference.extent.minx, cellsize); |
1059 | 0 | y1 = MS_MAP2IMAGE_Y(map->extent.maxy, map->reference.extent.maxy, cellsize); |
1060 | 0 | y2 = MS_MAP2IMAGE_Y(map->extent.miny, map->reference.extent.maxy, cellsize); |
1061 | |
|
1062 | 0 | initStyle(&style); |
1063 | 0 | style.color = map->reference.color; |
1064 | 0 | style.outlinecolor = map->reference.outlinecolor; |
1065 | | |
1066 | | /* if extent are smaller than minbox size */ |
1067 | | /* draw that extent on the reference image */ |
1068 | 0 | if ((abs(x2 - x1) > map->reference.minboxsize) || |
1069 | 0 | (abs(y2 - y1) > map->reference.minboxsize)) { |
1070 | 0 | shapeObj rect; |
1071 | 0 | lineObj line; |
1072 | 0 | pointObj points[5]; |
1073 | 0 | msInitShape(&rect); |
1074 | |
|
1075 | 0 | line.point = points; |
1076 | 0 | line.numpoints = 5; |
1077 | 0 | rect.line = &line; |
1078 | 0 | rect.numlines = 1; |
1079 | 0 | rect.type = MS_SHAPE_POLYGON; |
1080 | |
|
1081 | 0 | line.point[0].x = x1; |
1082 | 0 | line.point[0].y = y1; |
1083 | 0 | line.point[1].x = x1; |
1084 | 0 | line.point[1].y = y2; |
1085 | 0 | line.point[2].x = x2; |
1086 | 0 | line.point[2].y = y2; |
1087 | 0 | line.point[3].x = x2; |
1088 | 0 | line.point[3].y = y1; |
1089 | 0 | line.point[4].x = line.point[0].x; |
1090 | 0 | line.point[4].y = line.point[0].y; |
1091 | |
|
1092 | 0 | line.numpoints = 5; |
1093 | |
|
1094 | 0 | if (map->reference.maxboxsize == 0 || |
1095 | 0 | ((abs(x2 - x1) < map->reference.maxboxsize) && |
1096 | 0 | (abs(y2 - y1) < map->reference.maxboxsize))) { |
1097 | 0 | if (MS_UNLIKELY(MS_FAILURE == |
1098 | 0 | msDrawShadeSymbol(map, image, &rect, &style, 1.0))) { |
1099 | 0 | msFreeImage(image); |
1100 | 0 | return NULL; |
1101 | 0 | } |
1102 | 0 | } |
1103 | |
|
1104 | 0 | } else { /* else draw the marker symbol */ |
1105 | 0 | if (map->reference.maxboxsize == 0 || |
1106 | 0 | ((abs(x2 - x1) < map->reference.maxboxsize) && |
1107 | 0 | (abs(y2 - y1) < map->reference.maxboxsize))) { |
1108 | 0 | style.size = map->reference.markersize; |
1109 | | |
1110 | | /* if the marker symbol is specify draw this symbol else draw a cross */ |
1111 | 0 | if (map->reference.marker || map->reference.markername) { |
1112 | 0 | pointObj point; |
1113 | 0 | point.x = (double)(x1 + x2) / 2; |
1114 | 0 | point.y = (double)(y1 + y2) / 2; |
1115 | |
|
1116 | 0 | if (map->reference.marker) { |
1117 | 0 | style.symbol = map->reference.marker; |
1118 | 0 | } else { |
1119 | 0 | style.symbol = msGetSymbolIndex(&map->symbolset, |
1120 | 0 | map->reference.markername, MS_TRUE); |
1121 | 0 | } |
1122 | |
|
1123 | 0 | if (MS_UNLIKELY(MS_FAILURE == |
1124 | 0 | msDrawMarkerSymbol(map, image, &point, &style, 1.0))) { |
1125 | 0 | msFreeImage(image); |
1126 | 0 | return NULL; |
1127 | 0 | } |
1128 | 0 | } else { |
1129 | 0 | int x21, y21; |
1130 | 0 | shapeObj cross; |
1131 | 0 | lineObj lines[4]; |
1132 | 0 | pointObj point[8]; |
1133 | 0 | int i; |
1134 | | /* determine the center point */ |
1135 | 0 | x21 = MS_NINT((x1 + x2) / 2); |
1136 | 0 | y21 = MS_NINT((y1 + y2) / 2); |
1137 | |
|
1138 | 0 | msInitShape(&cross); |
1139 | 0 | cross.numlines = 4; |
1140 | 0 | cross.line = lines; |
1141 | 0 | for (i = 0; i < 4; i++) { |
1142 | 0 | cross.line[i].numpoints = 2; |
1143 | 0 | cross.line[i].point = &(point[2 * i]); |
1144 | 0 | } |
1145 | | /* draw a cross */ |
1146 | 0 | cross.line[0].point[0].x = x21 - 8; |
1147 | 0 | cross.line[0].point[0].y = y21; |
1148 | 0 | cross.line[0].point[1].x = x21 - 3; |
1149 | 0 | cross.line[0].point[1].y = y21; |
1150 | 0 | cross.line[1].point[0].x = x21; |
1151 | 0 | cross.line[1].point[0].y = y21 - 8; |
1152 | 0 | cross.line[1].point[1].x = x21; |
1153 | 0 | cross.line[1].point[1].y = y21 - 3; |
1154 | 0 | cross.line[2].point[0].x = x21; |
1155 | 0 | cross.line[2].point[0].y = y21 + 3; |
1156 | 0 | cross.line[2].point[1].x = x21; |
1157 | 0 | cross.line[2].point[1].y = y21 + 8; |
1158 | 0 | cross.line[3].point[0].x = x21 + 3; |
1159 | 0 | cross.line[3].point[0].y = y21; |
1160 | 0 | cross.line[3].point[1].x = x21 + 8; |
1161 | 0 | cross.line[3].point[1].y = y21; |
1162 | |
|
1163 | 0 | if (MS_UNLIKELY(MS_FAILURE == |
1164 | 0 | msDrawLineSymbol(map, image, &cross, &style, 1.0))) { |
1165 | 0 | msFreeImage(image); |
1166 | 0 | return NULL; |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 | } |
1170 | 0 | } |
1171 | | |
1172 | 0 | return (image); |
1173 | 0 | } |