/src/MapServer/src/mapdrawgdal.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Code for drawing GDAL raster layers. Called from |
6 | | * msDrawRasterLayerLow() in mapraster.c. |
7 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
8 | | * |
9 | | ****************************************************************************** |
10 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
11 | | * |
12 | | * Permission is hereby granted, free of charge, to any person obtaining a |
13 | | * copy of this software and associated documentation files (the "Software"), |
14 | | * to deal in the Software without restriction, including without limitation |
15 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
16 | | * and/or sell copies of the Software, and to permit persons to whom the |
17 | | * Software is furnished to do so, subject to the following conditions: |
18 | | * |
19 | | * The above copyright notice and this permission notice shall be included in |
20 | | * all copies of this Software or works derived from this Software. |
21 | | * |
22 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
23 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
25 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
27 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
28 | | * DEALINGS IN THE SOFTWARE. |
29 | | *****************************************************************************/ |
30 | | |
31 | | #include <assert.h> |
32 | | #include <stdbool.h> |
33 | | #include "mapserver.h" |
34 | | #include "mapresample.h" |
35 | | #include "mapthread.h" |
36 | | #include "maptime.h" |
37 | | |
38 | | extern int InvGeoTransform(double *gt_in, double *gt_out); |
39 | | |
40 | 0 | #define MAXCOLORS 256 |
41 | 0 | #define GEO_TRANS(tr, x, y) ((tr)[0] + (tr)[1] * (x) + (tr)[2] * (y)) |
42 | | #define SKIP_MASK(x, y) \ |
43 | 0 | (mask_rb && !*(mask_rb->data.rgba.a + (y)*mask_rb->data.rgba.row_step + \ |
44 | 0 | (x)*mask_rb->data.rgba.pixel_step)) |
45 | | |
46 | | #include "gdal.h" |
47 | | #include "cpl_string.h" |
48 | | |
49 | | #include "gdal_alg.h" |
50 | | |
51 | | static bool IsNoData(double dfValue, double dfNoDataValue); |
52 | | |
53 | | static int LoadGDALImages(GDALDatasetH hDS, int band_numbers[4], int band_count, |
54 | | layerObj *layer, int src_xoff, int src_yoff, |
55 | | int src_xsize, int src_ysize, GByte *pabyBuffer, |
56 | | int dst_xsize, int dst_ysize, int *pbHaveRGBNoData, |
57 | | int *pnNoData1, int *pnNoData2, int *pnNoData3, |
58 | | bool *pbScaled, double *pdfScaleMin, |
59 | | double *pdfScaleRatio, bool **ppbIsNoDataBuffer); |
60 | | static int msDrawRasterLayerGDAL_RawMode(mapObj *map, layerObj *layer, |
61 | | imageObj *image, GDALDatasetH hDS, |
62 | | int src_xoff, int src_yoff, |
63 | | int src_xsize, int src_ysize, |
64 | | int dst_xoff, int dst_yoff, |
65 | | int dst_xsize, int dst_ysize); |
66 | | |
67 | | static int msDrawRasterLayerGDAL_16BitClassification( |
68 | | mapObj *map, layerObj *layer, rasterBufferObj *rb, GDALRasterBandH hBand, |
69 | | int src_xoff, int src_yoff, int src_xsize, int src_ysize, int dst_xoff, |
70 | | int dst_yoff, int dst_xsize, int dst_ysize); |
71 | | |
72 | | /* |
73 | | * rasterBufferObj setting macros. |
74 | | */ |
75 | | |
76 | | #define GAMMA_CORRECT(color, gamma) \ |
77 | 0 | (int)(0.5 + 255 * pow((color) / 255.0, (gamma))) |
78 | | |
79 | | /************************************************************************/ |
80 | | /* msDrawRasterLayerGDAL() */ |
81 | | /************************************************************************/ |
82 | | |
83 | | int msDrawRasterLayerGDAL(mapObj *map, layerObj *layer, imageObj *image, |
84 | | rasterBufferObj *rb, void *hDSVoid) |
85 | | |
86 | 0 | { |
87 | 0 | int i; /* loop counters */ |
88 | 0 | int cmap[MAXCOLORS]; |
89 | 0 | #ifndef NDEBUG |
90 | 0 | int cmap_set = FALSE; |
91 | 0 | #endif |
92 | 0 | unsigned char rb_cmap[4][MAXCOLORS]; |
93 | 0 | double adfGeoTransform[6], adfInvGeoTransform[6]; |
94 | 0 | int dst_xoff, dst_yoff, dst_xsize, dst_ysize; |
95 | 0 | int src_xoff, src_yoff, src_xsize, src_ysize; |
96 | 0 | double llx, lly, urx, ury; |
97 | 0 | rectObj copyRect, mapRect; |
98 | 0 | unsigned char *pabyRaw1 = NULL, *pabyRaw2 = NULL, *pabyRaw3 = NULL, |
99 | 0 | *pabyRawAlpha = NULL; |
100 | 0 | int classified = FALSE; |
101 | 0 | int red_band = 0, green_band = 0, blue_band = 0, alpha_band = 0; |
102 | 0 | int band_count, band_numbers[4]; |
103 | 0 | GDALDatasetH hDS = hDSVoid; |
104 | 0 | GDALColorTableH hColorMap = NULL; |
105 | 0 | GDALRasterBandH hBand1 = NULL, hBand2 = NULL, hBand3 = NULL, |
106 | 0 | hBandAlpha = NULL; |
107 | 0 | int bHaveRGBNoData = FALSE; |
108 | 0 | int nNoData1 = -1, nNoData2 = -1, nNoData3 = -1; |
109 | 0 | rasterBufferObj *mask_rb = NULL; |
110 | 0 | rasterBufferObj s_mask_rb; |
111 | |
|
112 | 0 | if (layer->mask) { |
113 | 0 | int ret; |
114 | 0 | layerObj *maskLayer = GET_LAYER(map, msGetLayerIndex(map, layer->mask)); |
115 | 0 | mask_rb = &s_mask_rb; |
116 | 0 | memset(mask_rb, 0, sizeof(s_mask_rb)); |
117 | 0 | ret = MS_IMAGE_RENDERER(maskLayer->maskimage) |
118 | 0 | ->getRasterBufferHandle(maskLayer->maskimage, mask_rb); |
119 | 0 | if (ret != MS_SUCCESS) |
120 | 0 | return -1; |
121 | 0 | } |
122 | | |
123 | | /*only support rawdata and pluggable renderers*/ |
124 | 0 | assert(MS_RENDERER_RAWDATA(image->format) || |
125 | 0 | (MS_RENDERER_PLUGIN(image->format) && rb)); |
126 | | |
127 | 0 | memset(cmap, 0xff, MAXCOLORS * sizeof(int)); |
128 | 0 | memset(rb_cmap, 0, sizeof(rb_cmap)); |
129 | | |
130 | | /* -------------------------------------------------------------------- */ |
131 | | /* Test the image format instead of the map format. */ |
132 | | /* Normally the map format and the image format should be the */ |
133 | | /* same but In some cases like swf and pdf support, a temporary */ |
134 | | /* GD image object is created and used to render raster layers */ |
135 | | /* and then dumped into the SWF or the PDF file. */ |
136 | | /* -------------------------------------------------------------------- */ |
137 | |
|
138 | 0 | src_xsize = GDALGetRasterXSize(hDS); |
139 | 0 | src_ysize = GDALGetRasterYSize(hDS); |
140 | | |
141 | | /* |
142 | | * If the RAW_WINDOW attribute is set, use that to establish what to |
143 | | * load. This is normally just set by the mapresample.c module to avoid |
144 | | * problems with rotated maps. |
145 | | */ |
146 | |
|
147 | 0 | if (CSLFetchNameValue(layer->processing, "RAW_WINDOW") != NULL) { |
148 | 0 | char **papszTokens = |
149 | 0 | CSLTokenizeString(CSLFetchNameValue(layer->processing, "RAW_WINDOW")); |
150 | |
|
151 | 0 | if (layer->debug) |
152 | 0 | msDebug("msDrawGDAL(%s): using RAW_WINDOW=%s, dst=0,0,%d,%d\n", |
153 | 0 | layer->name, CSLFetchNameValue(layer->processing, "RAW_WINDOW"), |
154 | 0 | image->width, image->height); |
155 | |
|
156 | 0 | if (CSLCount(papszTokens) != 4) { |
157 | 0 | CSLDestroy(papszTokens); |
158 | 0 | msSetError(MS_IMGERR, "RAW_WINDOW PROCESSING directive corrupt.", |
159 | 0 | "msDrawGDAL()"); |
160 | 0 | return -1; |
161 | 0 | } |
162 | | |
163 | 0 | src_xoff = atoi(papszTokens[0]); |
164 | 0 | src_yoff = atoi(papszTokens[1]); |
165 | 0 | src_xsize = atoi(papszTokens[2]); |
166 | 0 | src_ysize = atoi(papszTokens[3]); |
167 | |
|
168 | 0 | dst_xoff = 0; |
169 | 0 | dst_yoff = 0; |
170 | 0 | dst_xsize = image->width; |
171 | 0 | dst_ysize = image->height; |
172 | |
|
173 | 0 | CSLDestroy(papszTokens); |
174 | 0 | } |
175 | | |
176 | | /* |
177 | | * Compute the georeferenced window of overlap, and do nothing if there |
178 | | * is no overlap between the map extents, and the file we are operating on. |
179 | | */ |
180 | 0 | else if (layer->transform) { |
181 | 0 | int dst_lrx, dst_lry; |
182 | |
|
183 | 0 | if (layer->debug) |
184 | 0 | msDebug("msDrawRasterLayerGDAL(): Entering transform.\n"); |
185 | |
|
186 | 0 | msGetGDALGeoTransform(hDS, map, layer, adfGeoTransform); |
187 | 0 | InvGeoTransform(adfGeoTransform, adfInvGeoTransform); |
188 | |
|
189 | 0 | mapRect = map->extent; |
190 | |
|
191 | 0 | mapRect.minx -= map->cellsize * 0.5; |
192 | 0 | mapRect.maxx += map->cellsize * 0.5; |
193 | 0 | mapRect.miny -= map->cellsize * 0.5; |
194 | 0 | mapRect.maxy += map->cellsize * 0.5; |
195 | |
|
196 | 0 | copyRect = mapRect; |
197 | |
|
198 | 0 | if (copyRect.minx < GEO_TRANS(adfGeoTransform, 0, src_ysize)) |
199 | 0 | copyRect.minx = GEO_TRANS(adfGeoTransform, 0, src_ysize); |
200 | 0 | if (copyRect.maxx > GEO_TRANS(adfGeoTransform, src_xsize, 0)) |
201 | 0 | copyRect.maxx = GEO_TRANS(adfGeoTransform, src_xsize, 0); |
202 | |
|
203 | 0 | if (copyRect.miny < GEO_TRANS(adfGeoTransform + 3, 0, src_ysize)) |
204 | 0 | copyRect.miny = GEO_TRANS(adfGeoTransform + 3, 0, src_ysize); |
205 | 0 | if (copyRect.maxy > GEO_TRANS(adfGeoTransform + 3, src_xsize, 0)) |
206 | 0 | copyRect.maxy = GEO_TRANS(adfGeoTransform + 3, src_xsize, 0); |
207 | |
|
208 | 0 | if (copyRect.minx >= copyRect.maxx || copyRect.miny >= copyRect.maxy) { |
209 | 0 | if (layer->debug) |
210 | 0 | msDebug("msDrawRasterLayerGDAL(): Error in overlap calculation.\n"); |
211 | 0 | return 0; |
212 | 0 | } |
213 | | |
214 | | /* |
215 | | * Copy the source and destination raster coordinates. |
216 | | */ |
217 | 0 | llx = GEO_TRANS(adfInvGeoTransform + 0, copyRect.minx, copyRect.miny); |
218 | 0 | lly = GEO_TRANS(adfInvGeoTransform + 3, copyRect.minx, copyRect.miny); |
219 | 0 | urx = GEO_TRANS(adfInvGeoTransform + 0, copyRect.maxx, copyRect.maxy); |
220 | 0 | ury = GEO_TRANS(adfInvGeoTransform + 3, copyRect.maxx, copyRect.maxy); |
221 | |
|
222 | 0 | src_xoff = MS_MAX(0, (int)floor(llx + 0.5)); |
223 | 0 | src_yoff = MS_MAX(0, (int)floor(ury + 0.5)); |
224 | 0 | src_xsize = MS_MIN(MS_MAX(0, (int)(urx - llx + 0.5)), |
225 | 0 | GDALGetRasterXSize(hDS) - src_xoff); |
226 | 0 | src_ysize = MS_MIN(MS_MAX(0, (int)(lly - ury + 0.5)), |
227 | 0 | GDALGetRasterYSize(hDS) - src_yoff); |
228 | | |
229 | | /* We want very small windows to use at least one source pixel (#4172) */ |
230 | 0 | if (src_xsize == 0 && (urx - llx) > 0.0) { |
231 | 0 | src_xsize = 1; |
232 | 0 | src_xoff = MS_MIN(src_xoff, GDALGetRasterXSize(hDS) - 1); |
233 | 0 | } |
234 | 0 | if (src_ysize == 0 && (lly - ury) > 0.0) { |
235 | 0 | src_ysize = 1; |
236 | 0 | src_yoff = MS_MIN(src_yoff, GDALGetRasterYSize(hDS) - 1); |
237 | 0 | } |
238 | |
|
239 | 0 | if (src_xsize == 0 || src_ysize == 0) { |
240 | 0 | if (layer->debug) |
241 | 0 | msDebug("msDrawRasterLayerGDAL(): no apparent overlap between map view " |
242 | 0 | "and this window(1).\n"); |
243 | 0 | return 0; |
244 | 0 | } |
245 | | |
246 | 0 | if (map->cellsize == 0) { |
247 | 0 | if (layer->debug) |
248 | 0 | msDebug("msDrawRasterLayerGDAL(): Cellsize can't be 0.\n"); |
249 | 0 | return 0; |
250 | 0 | } |
251 | | |
252 | 0 | dst_xoff = (int)((copyRect.minx - mapRect.minx) / map->cellsize); |
253 | 0 | dst_yoff = (int)((mapRect.maxy - copyRect.maxy) / map->cellsize); |
254 | |
|
255 | 0 | dst_lrx = (int)((copyRect.maxx - mapRect.minx) / map->cellsize + 0.5); |
256 | 0 | dst_lry = (int)((mapRect.maxy - copyRect.miny) / map->cellsize + 0.5); |
257 | 0 | dst_lrx = MS_MAX(0, MS_MIN(image->width, dst_lrx)); |
258 | 0 | dst_lry = MS_MAX(0, MS_MIN(image->height, dst_lry)); |
259 | |
|
260 | 0 | dst_xsize = MS_MAX(0, MS_MIN(image->width, dst_lrx - dst_xoff)); |
261 | 0 | dst_ysize = MS_MAX(0, MS_MIN(image->height, dst_lry - dst_yoff)); |
262 | |
|
263 | 0 | if (dst_xsize == 0 || dst_ysize == 0) { |
264 | 0 | if (layer->debug) |
265 | 0 | msDebug("msDrawRasterLayerGDAL(): no apparent overlap between map view " |
266 | 0 | "and this window(2).\n"); |
267 | 0 | return 0; |
268 | 0 | } |
269 | | |
270 | 0 | if (layer->debug) |
271 | 0 | msDebug("msDrawRasterLayerGDAL(): src=%d,%d,%d,%d, dst=%d,%d,%d,%d\n", |
272 | 0 | src_xoff, src_yoff, src_xsize, src_ysize, dst_xoff, dst_yoff, |
273 | 0 | dst_xsize, dst_ysize); |
274 | 0 | #ifndef notdef |
275 | 0 | if (layer->debug) { |
276 | 0 | double d_src_xoff, d_src_yoff, geo_x, geo_y; |
277 | |
|
278 | 0 | geo_x = mapRect.minx + dst_xoff * map->cellsize; |
279 | 0 | geo_y = mapRect.maxy - dst_yoff * map->cellsize; |
280 | |
|
281 | 0 | d_src_xoff = (geo_x - adfGeoTransform[0]) / adfGeoTransform[1]; |
282 | 0 | d_src_yoff = (geo_y - adfGeoTransform[3]) / adfGeoTransform[5]; |
283 | |
|
284 | 0 | msDebug("msDrawRasterLayerGDAL(): source raster PL (%.3f,%.3f) for dst " |
285 | 0 | "PL (%d,%d).\n", |
286 | 0 | d_src_xoff, d_src_yoff, dst_xoff, dst_yoff); |
287 | 0 | } |
288 | 0 | #endif |
289 | 0 | } |
290 | | |
291 | | /* |
292 | | * If layer transforms are turned off, just map 1:1. |
293 | | */ |
294 | 0 | else { |
295 | 0 | dst_xoff = src_xoff = 0; |
296 | 0 | dst_yoff = src_yoff = 0; |
297 | 0 | dst_xsize = src_xsize = MS_MIN(image->width, src_xsize); |
298 | 0 | dst_ysize = src_ysize = MS_MIN(image->height, src_ysize); |
299 | 0 | } |
300 | | |
301 | | /* |
302 | | * In RAWDATA mode we don't fool with colors. Do the raw processing, |
303 | | * and return from the function early. |
304 | | */ |
305 | 0 | if (MS_RENDERER_RAWDATA(image->format)) { |
306 | 0 | return msDrawRasterLayerGDAL_RawMode( |
307 | 0 | map, layer, image, hDS, src_xoff, src_yoff, src_xsize, src_ysize, |
308 | 0 | dst_xoff, dst_yoff, dst_xsize, dst_ysize); |
309 | 0 | } |
310 | | |
311 | | /* |
312 | | * Is this image classified? We consider it classified if there are |
313 | | * classes with an expression string *or* a color range. We don't want |
314 | | * to treat the raster as classified if there is just a bogus class here |
315 | | * to force inclusion in the legend. |
316 | | */ |
317 | 0 | for (i = 0; i < layer->numclasses; i++) { |
318 | 0 | int s; |
319 | | |
320 | | /* change colour based on colour range? */ |
321 | 0 | for (s = 0; s < layer->class[i] -> numstyles; s++) { |
322 | 0 | if (MS_VALID_COLOR(layer->class[i] -> styles[s] -> mincolor) && |
323 | 0 | MS_VALID_COLOR(layer->class[i] -> styles[s] -> maxcolor)) { |
324 | 0 | classified = TRUE; |
325 | 0 | break; |
326 | 0 | } |
327 | 0 | } |
328 | |
|
329 | 0 | if (layer->class[i] -> expression.string != NULL) { |
330 | 0 | classified = TRUE; |
331 | 0 | break; |
332 | 0 | } |
333 | 0 | } |
334 | | |
335 | | /* |
336 | | * Set up the band selection. We look for a BANDS directive in the |
337 | | * the PROCESSING options. If not found we default to grey=1, grey=1,alpha=2, |
338 | | * red=1,green=2,blue=3 or red=1,green=2,blue=3,alpha>=4. |
339 | | */ |
340 | |
|
341 | 0 | if (CSLFetchNameValue(layer->processing, "BANDS") == NULL) { |
342 | 0 | const int gdal_band_count = GDALGetRasterCount(hDS); |
343 | 0 | red_band = 1; |
344 | |
|
345 | 0 | if (gdal_band_count >= 4) { |
346 | | /* The alpha band is not necessarily the 4th one */ |
347 | 0 | for (i = 4; i <= gdal_band_count; i++) { |
348 | 0 | if (GDALGetRasterColorInterpretation(GDALGetRasterBand(hDS, i)) == |
349 | 0 | GCI_AlphaBand) { |
350 | 0 | alpha_band = i; |
351 | 0 | break; |
352 | 0 | } |
353 | 0 | } |
354 | 0 | } |
355 | |
|
356 | 0 | if (gdal_band_count >= 3) { |
357 | 0 | green_band = 2; |
358 | 0 | blue_band = 3; |
359 | 0 | } |
360 | |
|
361 | 0 | if (gdal_band_count == 2 && GDALGetRasterColorInterpretation( |
362 | 0 | GDALGetRasterBand(hDS, 2)) == GCI_AlphaBand) |
363 | 0 | alpha_band = 2; |
364 | |
|
365 | 0 | hBand1 = GDALGetRasterBand(hDS, red_band); |
366 | 0 | if (classified || GDALGetRasterColorTable(hBand1) != NULL) { |
367 | 0 | alpha_band = 0; |
368 | 0 | green_band = 0; |
369 | 0 | blue_band = 0; |
370 | 0 | } |
371 | 0 | } else { |
372 | 0 | int *band_list; |
373 | |
|
374 | 0 | band_list = msGetGDALBandList(layer, hDS, 4, &band_count); |
375 | 0 | if (band_list == NULL) |
376 | 0 | return -1; |
377 | | |
378 | 0 | if (band_count > 0) |
379 | 0 | red_band = band_list[0]; |
380 | 0 | else |
381 | 0 | red_band = 0; |
382 | 0 | if (band_count > 2) { |
383 | 0 | green_band = band_list[1]; |
384 | 0 | blue_band = band_list[2]; |
385 | 0 | } else { |
386 | 0 | green_band = 0; |
387 | 0 | blue_band = 0; |
388 | 0 | } |
389 | |
|
390 | 0 | if (band_count > 3) |
391 | 0 | alpha_band = band_list[3]; |
392 | 0 | else |
393 | 0 | alpha_band = 0; |
394 | |
|
395 | 0 | free(band_list); |
396 | 0 | } |
397 | | |
398 | 0 | band_numbers[0] = red_band; |
399 | 0 | band_numbers[1] = green_band; |
400 | 0 | band_numbers[2] = blue_band; |
401 | 0 | band_numbers[3] = 0; |
402 | |
|
403 | 0 | if (blue_band != 0 && alpha_band != 0) { |
404 | 0 | band_numbers[3] = alpha_band; |
405 | 0 | band_count = 4; |
406 | 0 | } else if (blue_band != 0 && alpha_band == 0) |
407 | 0 | band_count = 3; |
408 | 0 | else if (alpha_band != 0) { |
409 | 0 | band_numbers[1] = alpha_band; |
410 | 0 | band_count = 2; |
411 | 0 | } else |
412 | 0 | band_count = 1; |
413 | |
|
414 | 0 | if (layer->debug > 1 || (layer->debug > 0 && green_band != 0)) { |
415 | 0 | msDebug( |
416 | 0 | "msDrawRasterLayerGDAL(): red,green,blue,alpha bands = %d,%d,%d,%d\n", |
417 | 0 | red_band, green_band, blue_band, alpha_band); |
418 | 0 | } |
419 | | |
420 | | /* |
421 | | * Get band handles for PC256, RGB or RGBA cases. |
422 | | */ |
423 | 0 | hBand1 = GDALGetRasterBand(hDS, red_band); |
424 | 0 | if (hBand1 == NULL) |
425 | 0 | return -1; |
426 | | |
427 | 0 | hBand2 = hBand3 = hBandAlpha = NULL; |
428 | |
|
429 | 0 | if (green_band != 0) { |
430 | 0 | hBand1 = GDALGetRasterBand(hDS, red_band); |
431 | 0 | hBand2 = GDALGetRasterBand(hDS, green_band); |
432 | 0 | hBand3 = GDALGetRasterBand(hDS, blue_band); |
433 | 0 | if (hBand1 == NULL || hBand2 == NULL || hBand3 == NULL) |
434 | 0 | return -1; |
435 | 0 | } |
436 | | |
437 | 0 | if (alpha_band != 0) |
438 | 0 | hBandAlpha = GDALGetRasterBand(hDS, alpha_band); |
439 | | |
440 | | /* |
441 | | * The logic for a classification rendering of non-8bit raster bands |
442 | | * is sufficiently different than the normal mechanism of loading |
443 | | * into an 8bit buffer, that we isolate it into it's own subfunction. |
444 | | */ |
445 | 0 | if (classified && hBand1 != NULL && |
446 | 0 | GDALGetRasterDataType(hBand1) != GDT_Byte) { |
447 | 0 | return msDrawRasterLayerGDAL_16BitClassification( |
448 | 0 | map, layer, rb, hBand1, src_xoff, src_yoff, src_xsize, src_ysize, |
449 | 0 | dst_xoff, dst_yoff, dst_xsize, dst_ysize); |
450 | 0 | } |
451 | | |
452 | | /* |
453 | | * Get colormap for this image. If there isn't one, and we have only |
454 | | * one band create a greyscale colormap. |
455 | | */ |
456 | 0 | bool isDefaultGreyscale = false; |
457 | |
|
458 | 0 | if (hBand2 != NULL) |
459 | 0 | hColorMap = NULL; |
460 | 0 | else { |
461 | 0 | hColorMap = GDALGetRasterColorTable(hBand1); |
462 | 0 | if (hColorMap != NULL) |
463 | 0 | hColorMap = GDALCloneColorTable(hColorMap); |
464 | 0 | else { |
465 | 0 | hColorMap = GDALCreateColorTable(GPI_RGB); |
466 | 0 | isDefaultGreyscale = true; |
467 | |
|
468 | 0 | for (i = 0; i < 256; i++) { |
469 | 0 | colorObj pixel = {i, i, i, 0}; |
470 | 0 | GDALColorEntry sEntry; |
471 | |
|
472 | 0 | if (MS_COMPARE_COLORS(pixel, layer->offsite)) { |
473 | 0 | sEntry.c1 = 0; |
474 | 0 | sEntry.c2 = 0; |
475 | 0 | sEntry.c3 = 0; |
476 | 0 | sEntry.c4 = 0; /* alpha set to zero */ |
477 | 0 | } else { |
478 | 0 | sEntry.c1 = i; |
479 | 0 | sEntry.c2 = i; |
480 | 0 | sEntry.c3 = i; |
481 | 0 | sEntry.c4 = 255; |
482 | 0 | } |
483 | |
|
484 | 0 | GDALSetColorEntry(hColorMap, i, &sEntry); |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | | /* |
489 | | ** If we have a known NODATA value, mark it now as transparent. |
490 | | */ |
491 | 0 | { |
492 | 0 | int bGotNoData; |
493 | 0 | double dfNoDataValue = msGetGDALNoDataValue(layer, hBand1, &bGotNoData); |
494 | |
|
495 | 0 | if (bGotNoData && dfNoDataValue >= 0 && |
496 | 0 | dfNoDataValue < GDALGetColorEntryCount(hColorMap)) { |
497 | 0 | GDALColorEntry sEntry; |
498 | |
|
499 | 0 | memcpy(&sEntry, GDALGetColorEntry(hColorMap, (int)dfNoDataValue), |
500 | 0 | sizeof(GDALColorEntry)); |
501 | |
|
502 | 0 | sEntry.c4 = 0; |
503 | 0 | GDALSetColorEntry(hColorMap, (int)dfNoDataValue, &sEntry); |
504 | 0 | } |
505 | 0 | } |
506 | 0 | } |
507 | | |
508 | | /* |
509 | | * Allocate imagery buffers. |
510 | | */ |
511 | 0 | pabyRaw1 = |
512 | 0 | (unsigned char *)malloc(((size_t)dst_xsize) * dst_ysize * band_count); |
513 | 0 | if (pabyRaw1 == NULL) { |
514 | 0 | msSetError(MS_MEMERR, "Allocating work image of size %dx%dx%d failed.", |
515 | 0 | "msDrawRasterLayerGDAL()", dst_xsize, dst_ysize, band_count); |
516 | 0 | return -1; |
517 | 0 | } |
518 | | |
519 | 0 | if (hBand2 != NULL && hBand3 != NULL) { |
520 | 0 | pabyRaw2 = pabyRaw1 + dst_xsize * dst_ysize * 1; |
521 | 0 | pabyRaw3 = pabyRaw1 + dst_xsize * dst_ysize * 2; |
522 | 0 | } |
523 | |
|
524 | 0 | if (hBandAlpha != NULL) { |
525 | 0 | if (hBand2 != NULL) |
526 | 0 | pabyRawAlpha = pabyRaw1 + dst_xsize * dst_ysize * 3; |
527 | 0 | else |
528 | 0 | pabyRawAlpha = pabyRaw1 + dst_xsize * dst_ysize * 1; |
529 | 0 | } |
530 | | |
531 | | /* |
532 | | * Load image data into buffers with scaling, etc. |
533 | | */ |
534 | 0 | bool bScaled = false; |
535 | 0 | double dfScaleMin = 0; |
536 | 0 | double dfScaleRatio = 0; |
537 | 0 | bool *pbIsNoDataBuffer = NULL; |
538 | 0 | if (LoadGDALImages(hDS, band_numbers, band_count, layer, src_xoff, src_yoff, |
539 | 0 | src_xsize, src_ysize, pabyRaw1, dst_xsize, dst_ysize, |
540 | 0 | &bHaveRGBNoData, &nNoData1, &nNoData2, &nNoData3, &bScaled, |
541 | 0 | &dfScaleMin, &dfScaleRatio, &pbIsNoDataBuffer) == -1) { |
542 | 0 | free(pabyRaw1); |
543 | 0 | free(pbIsNoDataBuffer); |
544 | 0 | return -1; |
545 | 0 | } |
546 | | |
547 | 0 | const char *sgamma = msLayerGetProcessingKey(layer, "GAMMA"); |
548 | 0 | const double gamma = sgamma ? CPLAtof(sgamma) : 1.0; |
549 | | |
550 | | /* |
551 | | * Setup the mapping between source eight bit pixel values, and the |
552 | | * output images color table. There are two general cases, where the |
553 | | * class colors are provided by the MAP file, or where we use the native |
554 | | * color table. |
555 | | */ |
556 | 0 | if (classified) { |
557 | 0 | int c; |
558 | 0 | const char *pszRangeColorspace = |
559 | 0 | msLayerGetProcessingKey(layer, "RANGE_COLORSPACE"); |
560 | 0 | colorspace iRangeColorspace; |
561 | |
|
562 | 0 | #ifndef NDEBUG |
563 | 0 | cmap_set = TRUE; |
564 | 0 | #endif |
565 | |
|
566 | 0 | if (hColorMap == NULL) { |
567 | 0 | msSetError(MS_IOERR, |
568 | 0 | "Attempt to classify 24bit image, this is unsupported.", |
569 | 0 | "drawGDAL()"); |
570 | 0 | free(pabyRaw1); |
571 | 0 | free(pbIsNoDataBuffer); |
572 | 0 | return -1; |
573 | 0 | } |
574 | | |
575 | 0 | if (!pszRangeColorspace || !strcasecmp(pszRangeColorspace, "RGB")) { |
576 | 0 | iRangeColorspace = MS_COLORSPACE_RGB; |
577 | 0 | } else if (!strcasecmp(pszRangeColorspace, "HSL")) { |
578 | 0 | iRangeColorspace = MS_COLORSPACE_HSL; |
579 | 0 | } else { |
580 | 0 | msSetError(MS_MISCERR, |
581 | 0 | "Unknown RANGE_COLORSPACE \"%s\", expecting RGB or HSL", |
582 | 0 | "drawGDAL()", pszRangeColorspace); |
583 | 0 | GDALDestroyColorTable(hColorMap); |
584 | 0 | free(pabyRaw1); |
585 | 0 | free(pbIsNoDataBuffer); |
586 | 0 | return -1; |
587 | 0 | } |
588 | | |
589 | 0 | int color_count = GDALGetColorEntryCount(hColorMap); |
590 | 0 | const bool bScaleColors = bScaled && !isDefaultGreyscale; |
591 | |
|
592 | 0 | if (!bScaleColors && color_count > 256) |
593 | 0 | color_count = 256; |
594 | |
|
595 | 0 | for (i = 0; i < color_count; i++) { |
596 | 0 | colorObj pixel; |
597 | 0 | int colormap_index; |
598 | 0 | GDALColorEntry sEntry; |
599 | |
|
600 | 0 | GDALGetColorEntryAsRGB(hColorMap, i, &sEntry); |
601 | |
|
602 | 0 | const int j = |
603 | 0 | bScaleColors |
604 | 0 | ? MAX(0, MIN(255, (int)((i - dfScaleMin) * dfScaleRatio))) |
605 | 0 | : i; |
606 | |
|
607 | 0 | pixel.red = sEntry.c1; |
608 | 0 | pixel.green = sEntry.c2; |
609 | 0 | pixel.blue = sEntry.c3; |
610 | 0 | colormap_index = i; |
611 | |
|
612 | 0 | if (!MS_COMPARE_COLORS(pixel, layer->offsite)) { |
613 | 0 | c = msGetClass(layer, &pixel, colormap_index); |
614 | |
|
615 | 0 | if (c != -1) { /* belongs to any class */ |
616 | 0 | int s; |
617 | | |
618 | | /* change colour based on colour range? Currently we |
619 | | only address the greyscale case properly. */ |
620 | |
|
621 | 0 | if (MS_VALID_COLOR(layer->class[c] -> styles[0] -> mincolor)) { |
622 | 0 | for (s = 0; s < layer->class[c] -> numstyles; s++) { |
623 | 0 | if (MS_VALID_COLOR(layer->class[c] -> styles[s] -> mincolor) && |
624 | 0 | MS_VALID_COLOR(layer->class[c] -> styles[s] -> maxcolor)) { |
625 | 0 | if (sEntry.c1 >= layer->class[c] -> styles[s] |
626 | 0 | -> minvalue && |
627 | 0 | sEntry.c1 <= |
628 | 0 | layer->class[c] -> styles[s] -> maxvalue) { |
629 | 0 | msValueToRange(layer->class[c] -> styles[s], sEntry.c1, |
630 | 0 | iRangeColorspace); |
631 | 0 | if (MS_VALID_COLOR(layer->class[c] -> styles[s] -> color)) { |
632 | 0 | rb_cmap[0][j] = layer->class[c]->styles[s]->color.red; |
633 | 0 | rb_cmap[1][j] = layer->class[c]->styles[s]->color.green; |
634 | 0 | rb_cmap[2][j] = layer->class[c]->styles[s]->color.blue; |
635 | 0 | rb_cmap[3][j] = |
636 | 0 | (layer->class[c] -> styles[s] -> color.alpha != 255) |
637 | 0 | ? (layer->class[c] -> styles[s] -> color.alpha) |
638 | 0 | : (255 * layer->class[c] -> styles[0] -> opacity / |
639 | 0 | 100); |
640 | 0 | break; |
641 | 0 | } |
642 | 0 | } |
643 | 0 | } |
644 | 0 | } |
645 | 0 | } else if (MS_TRANSPARENT_COLOR( |
646 | 0 | layer->class[c] -> styles[0] -> color)) |
647 | 0 | /* leave it transparent */; |
648 | | |
649 | 0 | else if (MS_VALID_COLOR(layer->class[c] -> styles[0] -> color)) { |
650 | 0 | rb_cmap[0][j] = layer->class[c]->styles[0]->color.red; |
651 | 0 | rb_cmap[1][j] = layer->class[c]->styles[0]->color.green; |
652 | 0 | rb_cmap[2][j] = layer->class[c]->styles[0]->color.blue; |
653 | 0 | rb_cmap[3][j] = |
654 | 0 | (255 * layer->class[c] -> styles[0] -> opacity / 100); |
655 | 0 | } |
656 | | |
657 | 0 | else { /* Use raster color */ |
658 | 0 | rb_cmap[0][j] = pixel.red; |
659 | 0 | rb_cmap[1][j] = pixel.green; |
660 | 0 | rb_cmap[2][j] = pixel.blue; |
661 | 0 | rb_cmap[3][j] = 255; |
662 | 0 | } |
663 | |
|
664 | 0 | if (gamma != 1.0) { |
665 | 0 | rb_cmap[0][j] = GAMMA_CORRECT(rb_cmap[0][j], gamma); |
666 | 0 | rb_cmap[1][j] = GAMMA_CORRECT(rb_cmap[1][j], gamma); |
667 | 0 | rb_cmap[2][j] = GAMMA_CORRECT(rb_cmap[2][j], gamma); |
668 | 0 | } |
669 | 0 | } |
670 | 0 | } |
671 | 0 | } |
672 | 0 | } else if (hBand2 == NULL && hColorMap != NULL && |
673 | 0 | rb->type == MS_BUFFER_BYTE_RGBA) { |
674 | 0 | int color_count; |
675 | 0 | #ifndef NDEBUG |
676 | 0 | cmap_set = TRUE; |
677 | 0 | #endif |
678 | |
|
679 | 0 | color_count = MS_MIN(256, GDALGetColorEntryCount(hColorMap)); |
680 | 0 | const bool bScaleColors = bScaled && !isDefaultGreyscale; |
681 | |
|
682 | 0 | for (i = 0; i < color_count; i++) { |
683 | 0 | GDALColorEntry sEntry; |
684 | |
|
685 | 0 | GDALGetColorEntryAsRGB(hColorMap, i, &sEntry); |
686 | 0 | const int j = |
687 | 0 | bScaleColors |
688 | 0 | ? MAX(0, MIN(255, (int)((i - dfScaleMin) * dfScaleRatio))) |
689 | 0 | : i; |
690 | |
|
691 | 0 | if (sEntry.c4 != 0 && |
692 | 0 | (!MS_VALID_COLOR(layer->offsite) || layer->offsite.red != sEntry.c1 || |
693 | 0 | layer->offsite.green != sEntry.c2 || |
694 | 0 | layer->offsite.blue != sEntry.c3)) { |
695 | 0 | rb_cmap[0][j] = sEntry.c1; |
696 | 0 | rb_cmap[1][j] = sEntry.c2; |
697 | 0 | rb_cmap[2][j] = sEntry.c3; |
698 | 0 | rb_cmap[3][j] = sEntry.c4; |
699 | 0 | if (gamma != 1.0) { |
700 | 0 | rb_cmap[0][j] = GAMMA_CORRECT(rb_cmap[0][j], gamma); |
701 | 0 | rb_cmap[1][j] = GAMMA_CORRECT(rb_cmap[1][j], gamma); |
702 | 0 | rb_cmap[2][j] = GAMMA_CORRECT(rb_cmap[2][j], gamma); |
703 | 0 | } |
704 | 0 | } |
705 | 0 | } |
706 | 0 | } |
707 | | |
708 | 0 | if (bHaveRGBNoData && layer->debug) |
709 | 0 | msDebug("msDrawGDAL(): using RGB nodata values from GDAL dataset.\n"); |
710 | | |
711 | | /* -------------------------------------------------------------------- */ |
712 | | /* If there was no alpha band, but we have a dataset level mask */ |
713 | | /* load it as massage it so it will function as our alpha for */ |
714 | | /* transparency purposes. */ |
715 | | /* -------------------------------------------------------------------- */ |
716 | 0 | if (hBandAlpha == NULL) { |
717 | 0 | int nMaskFlags = GDALGetMaskFlags(hBand1); |
718 | |
|
719 | 0 | if (CSLTestBoolean( |
720 | 0 | CSLFetchNameValueDef(layer->processing, "USE_MASK_BAND", "YES")) && |
721 | 0 | (nMaskFlags & GMF_PER_DATASET) != 0 && |
722 | 0 | (nMaskFlags & (GMF_NODATA | GMF_ALL_VALID)) == 0) { |
723 | 0 | CPLErr eErr; |
724 | 0 | unsigned char *pabyOrig = pabyRaw1; |
725 | |
|
726 | 0 | if (layer->debug) |
727 | 0 | msDebug("msDrawGDAL(): using GDAL mask band for alpha.\n"); |
728 | |
|
729 | 0 | band_count++; |
730 | |
|
731 | 0 | pabyRaw1 = (unsigned char *)realloc(pabyOrig, ((size_t)dst_xsize) * |
732 | 0 | dst_ysize * band_count); |
733 | |
|
734 | 0 | if (pabyRaw1 == NULL) { |
735 | 0 | msSetError(MS_MEMERR, "Allocating work image of size %dx%dx%d failed.", |
736 | 0 | "msDrawRasterLayerGDAL()", dst_xsize, dst_ysize, band_count); |
737 | 0 | free(pabyOrig); |
738 | 0 | if (hColorMap != NULL) |
739 | 0 | GDALDestroyColorTable(hColorMap); |
740 | 0 | free(pbIsNoDataBuffer); |
741 | 0 | return -1; |
742 | 0 | } |
743 | | |
744 | 0 | if (hBand2 != NULL) { |
745 | 0 | pabyRaw2 = pabyRaw1 + dst_xsize * dst_ysize * 1; |
746 | 0 | pabyRaw3 = pabyRaw1 + dst_xsize * dst_ysize * 2; |
747 | 0 | pabyRawAlpha = pabyRaw1 + dst_xsize * dst_ysize * 3; |
748 | 0 | } else { |
749 | 0 | pabyRawAlpha = pabyRaw1 + dst_xsize * dst_ysize * 1; |
750 | 0 | } |
751 | |
|
752 | 0 | hBandAlpha = GDALGetMaskBand(hBand1); |
753 | |
|
754 | 0 | eErr = GDALRasterIO(hBandAlpha, GF_Read, src_xoff, src_yoff, src_xsize, |
755 | 0 | src_ysize, pabyRawAlpha, dst_xsize, dst_ysize, |
756 | 0 | GDT_Byte, 0, 0); |
757 | |
|
758 | 0 | if (eErr != CE_None) { |
759 | 0 | msSetError(MS_IOERR, "GDALRasterIO() failed: %s", "drawGDAL()", |
760 | 0 | CPLGetLastErrorMsg()); |
761 | 0 | if (hColorMap != NULL) |
762 | 0 | GDALDestroyColorTable(hColorMap); |
763 | 0 | free(pabyRaw1); |
764 | 0 | free(pbIsNoDataBuffer); |
765 | 0 | return -1; |
766 | 0 | } |
767 | | |
768 | | /* In case the mask is not an alpha channel, expand values of 1 to 255, */ |
769 | | /* so we can deal as it was an alpha band afterwards */ |
770 | 0 | if ((nMaskFlags & GMF_ALPHA) == 0) { |
771 | 0 | for (i = 0; i < dst_xsize * dst_ysize; i++) |
772 | 0 | if (pabyRawAlpha[i]) |
773 | 0 | pabyRawAlpha[i] = 255; |
774 | 0 | } |
775 | 0 | } |
776 | 0 | } |
777 | | |
778 | | /* -------------------------------------------------------------------- */ |
779 | | /* Single band plus colormap and alpha to truecolor. (RB) */ |
780 | | /* -------------------------------------------------------------------- */ |
781 | 0 | if (hBand2 == NULL && rb->type == MS_BUFFER_BYTE_RGBA && hBandAlpha != NULL) { |
782 | 0 | assert(cmap_set); |
783 | | |
784 | 0 | int k = 0; |
785 | 0 | for (i = dst_yoff; i < dst_yoff + dst_ysize; i++) { |
786 | 0 | for (int j = dst_xoff; j < dst_xoff + dst_xsize; j++, k++) { |
787 | 0 | int src_pixel, src_alpha, cmap_alpha, merged_alpha; |
788 | 0 | if (SKIP_MASK(j, i) || (pbIsNoDataBuffer && pbIsNoDataBuffer[k])) |
789 | 0 | continue; |
790 | | |
791 | 0 | src_pixel = pabyRaw1[k]; |
792 | 0 | src_alpha = pabyRawAlpha[k]; |
793 | 0 | cmap_alpha = rb_cmap[3][src_pixel]; |
794 | |
|
795 | 0 | merged_alpha = (src_alpha * cmap_alpha) / 255; |
796 | |
|
797 | 0 | if (merged_alpha < 2) |
798 | 0 | /* do nothing - transparent */; |
799 | 0 | else if (merged_alpha > 253) { |
800 | 0 | RB_SET_PIXEL(rb, j, i, rb_cmap[0][src_pixel], rb_cmap[1][src_pixel], |
801 | 0 | rb_cmap[2][src_pixel], cmap_alpha); |
802 | 0 | } else { |
803 | 0 | RB_MIX_PIXEL(rb, j, i, rb_cmap[0][src_pixel], rb_cmap[1][src_pixel], |
804 | 0 | rb_cmap[2][src_pixel], merged_alpha); |
805 | 0 | } |
806 | 0 | } |
807 | 0 | } |
808 | 0 | } |
809 | | |
810 | | /* -------------------------------------------------------------------- */ |
811 | | /* Single band plus colormap (no alpha) to truecolor (RB) */ |
812 | | /* -------------------------------------------------------------------- */ |
813 | 0 | else if (hBand2 == NULL && rb->type == MS_BUFFER_BYTE_RGBA) { |
814 | 0 | assert(cmap_set); |
815 | | |
816 | 0 | int k = 0; |
817 | 0 | for (i = dst_yoff; i < dst_yoff + dst_ysize; i++) { |
818 | 0 | for (int j = dst_xoff; j < dst_xoff + dst_xsize; j++, k++) { |
819 | 0 | int src_pixel = pabyRaw1[k]; |
820 | 0 | if (SKIP_MASK(j, i) || (pbIsNoDataBuffer && pbIsNoDataBuffer[k])) |
821 | 0 | continue; |
822 | | |
823 | 0 | if (rb_cmap[3][src_pixel] > 253) { |
824 | 0 | RB_SET_PIXEL(rb, j, i, rb_cmap[0][src_pixel], rb_cmap[1][src_pixel], |
825 | 0 | rb_cmap[2][src_pixel], rb_cmap[3][src_pixel]); |
826 | 0 | } else if (rb_cmap[3][src_pixel] > 1) { |
827 | 0 | RB_MIX_PIXEL(rb, j, i, rb_cmap[0][src_pixel], rb_cmap[1][src_pixel], |
828 | 0 | rb_cmap[2][src_pixel], rb_cmap[3][src_pixel]); |
829 | 0 | } |
830 | 0 | } |
831 | 0 | } |
832 | 0 | } |
833 | | |
834 | | /* -------------------------------------------------------------------- */ |
835 | | /* Input is 3 band RGB. Alpha blending is mixed into the loop */ |
836 | | /* since this case is less commonly used and has lots of other */ |
837 | | /* overhead. (RB) */ |
838 | | /* -------------------------------------------------------------------- */ |
839 | 0 | else if (hBand3 != NULL && rb->type == MS_BUFFER_BYTE_RGBA) { |
840 | 0 | int k = 0; |
841 | 0 | for (i = dst_yoff; i < dst_yoff + dst_ysize; i++) { |
842 | 0 | for (int j = dst_xoff; j < dst_xoff + dst_xsize; j++, k++) { |
843 | 0 | if (SKIP_MASK(j, i) || (pbIsNoDataBuffer && pbIsNoDataBuffer[k])) |
844 | 0 | continue; |
845 | 0 | int red = pabyRaw1[k]; |
846 | 0 | int green = pabyRaw2[k]; |
847 | 0 | int blue = pabyRaw3[k]; |
848 | 0 | if (MS_VALID_COLOR(layer->offsite) && red == layer->offsite.red && |
849 | 0 | green == layer->offsite.green && blue == layer->offsite.blue) |
850 | 0 | continue; |
851 | | |
852 | 0 | if (bHaveRGBNoData && red == nNoData1 && green == nNoData2 && |
853 | 0 | blue == nNoData3) |
854 | 0 | continue; |
855 | | |
856 | 0 | if (pabyRawAlpha == NULL || pabyRawAlpha[k] == 255) { |
857 | 0 | if (gamma != 1.0) { |
858 | 0 | red = GAMMA_CORRECT(red, gamma); |
859 | 0 | green = GAMMA_CORRECT(green, gamma); |
860 | 0 | blue = GAMMA_CORRECT(blue, gamma); |
861 | 0 | } |
862 | |
|
863 | 0 | RB_SET_PIXEL(rb, j, i, red, green, blue, 255); |
864 | 0 | } else if (pabyRawAlpha[k] != 0) { |
865 | 0 | if (gamma != 1.0) { |
866 | 0 | red = GAMMA_CORRECT(red, gamma); |
867 | 0 | green = GAMMA_CORRECT(green, gamma); |
868 | 0 | blue = GAMMA_CORRECT(blue, gamma); |
869 | 0 | } |
870 | |
|
871 | 0 | RB_MIX_PIXEL(rb, j, i, red, green, blue, pabyRawAlpha[k]); |
872 | 0 | } |
873 | 0 | } |
874 | 0 | } |
875 | 0 | } |
876 | | |
877 | | /* |
878 | | ** Cleanup |
879 | | */ |
880 | 0 | free(pabyRaw1); |
881 | 0 | free(pbIsNoDataBuffer); |
882 | |
|
883 | 0 | if (hColorMap != NULL) |
884 | 0 | GDALDestroyColorTable(hColorMap); |
885 | |
|
886 | 0 | return 0; |
887 | 0 | } |
888 | | |
889 | | /************************************************************************/ |
890 | | /* ParseDefaultLUT() */ |
891 | | /************************************************************************/ |
892 | | |
893 | | static int ParseDefaultLUT(const char *lut_def, GByte *lut, int nMaxValIn) |
894 | | |
895 | 0 | { |
896 | 0 | const char *lut_read; |
897 | 0 | int last_in = 0, last_out = 0, all_done = FALSE; |
898 | | |
899 | | /* -------------------------------------------------------------------- */ |
900 | | /* Parse definition, applying to lut on the fly. */ |
901 | | /* -------------------------------------------------------------------- */ |
902 | 0 | lut_read = lut_def; |
903 | 0 | while (!all_done) { |
904 | 0 | int this_in = 0, this_out = 0; |
905 | 0 | int lut_i; |
906 | |
|
907 | 0 | while (isspace(*lut_read)) |
908 | 0 | lut_read++; |
909 | | |
910 | | /* if we are at end, assume nMaxValIn:255 */ |
911 | 0 | if (*lut_read == '\0') { |
912 | 0 | all_done = TRUE; |
913 | 0 | if (last_in != nMaxValIn) { |
914 | 0 | this_in = nMaxValIn; |
915 | 0 | this_out = 255; |
916 | 0 | } |
917 | 0 | } |
918 | | |
919 | | /* otherwise read "in:out", and skip past */ |
920 | 0 | else { |
921 | 0 | this_in = atoi(lut_read); |
922 | 0 | while (*lut_read != ':' && *lut_read) |
923 | 0 | lut_read++; |
924 | 0 | if (*lut_read == ':') |
925 | 0 | lut_read++; |
926 | 0 | while (isspace(*lut_read)) |
927 | 0 | lut_read++; |
928 | 0 | this_out = atoi(lut_read); |
929 | 0 | while (*lut_read && *lut_read != ',' && *lut_read != '\n') |
930 | 0 | lut_read++; |
931 | 0 | if (*lut_read == ',' || *lut_read == '\n') |
932 | 0 | lut_read++; |
933 | 0 | while (isspace(*lut_read)) |
934 | 0 | lut_read++; |
935 | 0 | } |
936 | |
|
937 | 0 | this_in = MS_MAX(0, MS_MIN(nMaxValIn, this_in)); |
938 | 0 | this_out = MS_MAX(0, MS_MIN(255, this_out)); |
939 | | |
940 | | /* apply linear values from last in:out to this in:out */ |
941 | 0 | for (lut_i = last_in; lut_i <= this_in; lut_i++) { |
942 | 0 | double ratio; |
943 | |
|
944 | 0 | if (last_in == this_in) |
945 | 0 | ratio = 1.0; |
946 | 0 | else |
947 | 0 | ratio = (lut_i - last_in) / (double)(this_in - last_in); |
948 | |
|
949 | 0 | lut[lut_i] = |
950 | 0 | (int)floor(((1.0 - ratio) * last_out + ratio * this_out) + 0.5); |
951 | 0 | } |
952 | |
|
953 | 0 | last_in = this_in; |
954 | 0 | last_out = this_out; |
955 | 0 | } |
956 | |
|
957 | 0 | return 0; |
958 | 0 | } |
959 | | |
960 | | /************************************************************************/ |
961 | | /* LutFromGimpLine() */ |
962 | | /************************************************************************/ |
963 | | |
964 | | static int LutFromGimpLine(char *lut_line, GByte *lut) |
965 | | |
966 | 0 | { |
967 | 0 | char wrkLUTDef[1000]; |
968 | 0 | int i, count = 0; |
969 | 0 | char **tokens; |
970 | | |
971 | | /* cleanup white space at end of line (DOS LF, etc) */ |
972 | 0 | i = strlen(lut_line) - 1; |
973 | 0 | while (i > 0 && isspace(lut_line[i])) |
974 | 0 | lut_line[i--] = '\0'; |
975 | |
|
976 | 0 | while (*lut_line == 10 || *lut_line == 13) |
977 | 0 | lut_line++; |
978 | | |
979 | | /* tokenize line */ |
980 | 0 | tokens = CSLTokenizeString(lut_line); |
981 | 0 | if (CSLCount(tokens) != 17 * 2) { |
982 | 0 | CSLDestroy(tokens); |
983 | 0 | msSetError(MS_MISCERR, "GIMP curve file appears corrupt.", |
984 | 0 | "LutFromGimpLine()"); |
985 | 0 | return -1; |
986 | 0 | } |
987 | | |
988 | | /* Convert to our own format */ |
989 | 0 | wrkLUTDef[0] = '\0'; |
990 | 0 | for (i = 0; i < 17; i++) { |
991 | 0 | if (atoi(tokens[i * 2]) >= 0) { |
992 | 0 | if (count++ > 0) |
993 | 0 | strlcat(wrkLUTDef, ",", sizeof(wrkLUTDef)); |
994 | |
|
995 | 0 | snprintf(wrkLUTDef + strlen(wrkLUTDef), |
996 | 0 | sizeof(wrkLUTDef) - strlen(wrkLUTDef), "%s:%s", tokens[i * 2], |
997 | 0 | tokens[i * 2 + 1]); |
998 | 0 | } |
999 | 0 | } |
1000 | |
|
1001 | 0 | CSLDestroy(tokens); |
1002 | |
|
1003 | 0 | return ParseDefaultLUT(wrkLUTDef, lut, 255); |
1004 | 0 | } |
1005 | | |
1006 | | /************************************************************************/ |
1007 | | /* ParseGimpLUT() */ |
1008 | | /* */ |
1009 | | /* Parse a Gimp style LUT. */ |
1010 | | /************************************************************************/ |
1011 | | |
1012 | | static int ParseGimpLUT(const char *lut_def, GByte *lut, int iColorIndex) |
1013 | | |
1014 | 0 | { |
1015 | 0 | int i; |
1016 | 0 | GByte lutValue[256]; |
1017 | 0 | GByte lutColorBand[256]; |
1018 | 0 | char **lines = CSLTokenizeStringComplex(lut_def, "\n", FALSE, FALSE); |
1019 | |
|
1020 | 0 | if (!EQUALN(lines[0], "# GIMP Curves File", 18) || CSLCount(lines) < 6) { |
1021 | 0 | msSetError(MS_MISCERR, "GIMP curve file appears corrupt.", |
1022 | 0 | "ParseGimpLUT()"); |
1023 | 0 | CSLDestroy(lines); |
1024 | 0 | return -1; |
1025 | 0 | } |
1026 | | |
1027 | | /* |
1028 | | * Convert the overall curve, and the color band specific curve into LUTs. |
1029 | | */ |
1030 | 0 | if (LutFromGimpLine(lines[1], lutValue) != 0 || |
1031 | 0 | LutFromGimpLine(lines[iColorIndex + 1], lutColorBand) != 0) { |
1032 | 0 | CSLDestroy(lines); |
1033 | 0 | return -1; |
1034 | 0 | } |
1035 | 0 | CSLDestroy(lines); |
1036 | | |
1037 | | /* |
1038 | | * Compose the two luts as if the raw value passed through the color band |
1039 | | * specific lut, and then the general value lut. Usually one or the |
1040 | | * other will be the identity mapping, but not always. |
1041 | | */ |
1042 | |
|
1043 | 0 | for (i = 0; i < 256; i++) { |
1044 | 0 | lut[i] = lutValue[lutColorBand[i]]; |
1045 | 0 | } |
1046 | |
|
1047 | 0 | return 0; |
1048 | 0 | } |
1049 | | |
1050 | | /************************************************************************/ |
1051 | | /* LoadLUT() */ |
1052 | | /* */ |
1053 | | /* Load a LUT according to RFC 21. */ |
1054 | | /************************************************************************/ |
1055 | | |
1056 | | static int LoadLUT(layerObj *layer, int iColorIndex, char **ppszLutDef) |
1057 | | |
1058 | 0 | { |
1059 | 0 | const char *lut_def; |
1060 | 0 | char key[20], lut_def_fromfile[2500]; |
1061 | |
|
1062 | 0 | *ppszLutDef = NULL; |
1063 | | |
1064 | | /* -------------------------------------------------------------------- */ |
1065 | | /* Get lut specifier from processing directives. Do nothing if */ |
1066 | | /* none are found. */ |
1067 | | /* -------------------------------------------------------------------- */ |
1068 | 0 | sprintf(key, "LUT_%d", iColorIndex); |
1069 | 0 | lut_def = msLayerGetProcessingKey(layer, key); |
1070 | 0 | if (lut_def == NULL) |
1071 | 0 | lut_def = msLayerGetProcessingKey(layer, "LUT"); |
1072 | 0 | if (lut_def == NULL) |
1073 | 0 | return 0; |
1074 | | |
1075 | | /* -------------------------------------------------------------------- */ |
1076 | | /* Does this look like a file? If so, read it into memory. */ |
1077 | | /* -------------------------------------------------------------------- */ |
1078 | 0 | if (strspn(lut_def, "0123456789:, ") != strlen(lut_def)) { |
1079 | 0 | FILE *fp; |
1080 | 0 | char path[MS_MAXPATHLEN]; |
1081 | 0 | int len; |
1082 | 0 | msBuildPath(path, layer->map->mappath, lut_def); |
1083 | 0 | fp = fopen(path, "rb"); |
1084 | 0 | if (fp == NULL) { |
1085 | 0 | msSetError(MS_IOERR, "Failed to open LUT file '%s'.", "drawGDAL()", path); |
1086 | 0 | return -1; |
1087 | 0 | } |
1088 | | |
1089 | 0 | len = fread(lut_def_fromfile, 1, sizeof(lut_def_fromfile), fp); |
1090 | 0 | fclose(fp); |
1091 | |
|
1092 | 0 | if (len == sizeof(lut_def_fromfile)) { |
1093 | 0 | msSetError(MS_IOERR, |
1094 | 0 | "LUT definition from file %s longer than maximum buffer size " |
1095 | 0 | "(%d bytes).", |
1096 | 0 | "drawGDAL()", path, (int)sizeof(lut_def_fromfile)); |
1097 | 0 | return -1; |
1098 | 0 | } |
1099 | | |
1100 | 0 | lut_def_fromfile[len] = '\0'; |
1101 | |
|
1102 | 0 | lut_def = lut_def_fromfile; |
1103 | 0 | } |
1104 | | |
1105 | 0 | *ppszLutDef = msStrdup(lut_def); |
1106 | |
|
1107 | 0 | return 0; |
1108 | 0 | } |
1109 | | |
1110 | | /************************************************************************/ |
1111 | | /* FreeLUTs() */ |
1112 | | /************************************************************************/ |
1113 | | |
1114 | 0 | static void FreeLUTs(char **apszLUTs) { |
1115 | 0 | int i; |
1116 | 0 | for (i = 0; i < 4; i++) |
1117 | 0 | msFree(apszLUTs[i]); |
1118 | 0 | msFree(apszLUTs); |
1119 | 0 | } |
1120 | | |
1121 | | /************************************************************************/ |
1122 | | /* LoadLUTs() */ |
1123 | | /* */ |
1124 | | /* Return an array of 4 strings (some possibly NULL) with loaded LUTs */ |
1125 | | /* or NULL in case of failure. */ |
1126 | | /************************************************************************/ |
1127 | | |
1128 | 0 | static char **LoadLUTs(layerObj *layer, int band_count) { |
1129 | 0 | int i; |
1130 | 0 | char **apszLUTs; |
1131 | |
|
1132 | 0 | assert(band_count <= 4); |
1133 | | |
1134 | 0 | apszLUTs = (char **)msSmallCalloc(4, sizeof(char *)); |
1135 | 0 | for (i = 0; i < band_count; i++) { |
1136 | 0 | if (LoadLUT(layer, i + 1, &apszLUTs[i]) != 0) { |
1137 | 0 | FreeLUTs(apszLUTs); |
1138 | 0 | return NULL; |
1139 | 0 | } |
1140 | 0 | } |
1141 | | |
1142 | 0 | return apszLUTs; |
1143 | 0 | } |
1144 | | |
1145 | | /************************************************************************/ |
1146 | | /* GetDataTypeAppropriateForLUTS() */ |
1147 | | /* */ |
1148 | | /* This does a quick examination of the LUT strings to determine */ |
1149 | | /* if they have input values > 255, in which case the raster data */ |
1150 | | /* must be queries on 16-bits. */ |
1151 | | /************************************************************************/ |
1152 | | |
1153 | 0 | static GDALDataType GetDataTypeAppropriateForLUTS(char **apszLUTs) { |
1154 | 0 | GDALDataType eDT = GDT_Byte; |
1155 | 0 | int i; |
1156 | 0 | for (i = 0; i < 4; i++) { |
1157 | 0 | const char *pszLastTuple; |
1158 | 0 | int nLastInValue; |
1159 | 0 | if (apszLUTs[i] == NULL) |
1160 | 0 | continue; |
1161 | 0 | if (EQUALN(apszLUTs[i], "# GIMP", 6)) |
1162 | 0 | continue; |
1163 | | /* Find last in:out tuple in string */ |
1164 | 0 | pszLastTuple = strrchr(apszLUTs[i], ','); |
1165 | 0 | if (pszLastTuple == NULL) |
1166 | 0 | pszLastTuple = apszLUTs[i]; |
1167 | 0 | else |
1168 | 0 | pszLastTuple++; |
1169 | 0 | while (*pszLastTuple == ' ') |
1170 | 0 | pszLastTuple++; |
1171 | 0 | nLastInValue = atoi(pszLastTuple); |
1172 | 0 | if (nLastInValue > 255) { |
1173 | 0 | eDT = GDT_UInt16; |
1174 | 0 | break; |
1175 | 0 | } |
1176 | 0 | } |
1177 | 0 | return eDT; |
1178 | 0 | } |
1179 | | |
1180 | | /************************************************************************/ |
1181 | | /* ApplyLUT() */ |
1182 | | /************************************************************************/ |
1183 | | |
1184 | | static int ApplyLUT(int iColorIndex, const char *lut_def, const void *pInBuffer, |
1185 | 0 | GDALDataType eDT, GByte *pabyOutBuffer, int nPixelCount) { |
1186 | 0 | int i, err; |
1187 | 0 | GByte byteLut[256]; |
1188 | 0 | GByte *uint16Lut = NULL; |
1189 | |
|
1190 | 0 | assert(eDT == GDT_Byte || eDT == GDT_UInt16); |
1191 | | |
1192 | 0 | if (lut_def == NULL) { |
1193 | 0 | if (pInBuffer != pabyOutBuffer) { |
1194 | 0 | GDALCopyWords((void *)pInBuffer, eDT, GDALGetDataTypeSize(eDT) / 8, |
1195 | 0 | pabyOutBuffer, GDT_Byte, 1, nPixelCount); |
1196 | 0 | } |
1197 | 0 | return 0; |
1198 | 0 | } |
1199 | | |
1200 | | /* -------------------------------------------------------------------- */ |
1201 | | /* Parse the LUT description. */ |
1202 | | /* -------------------------------------------------------------------- */ |
1203 | 0 | if (EQUALN(lut_def, "# GIMP", 6)) { |
1204 | 0 | if (eDT != GDT_Byte) { |
1205 | 0 | msSetError(MS_MISCERR, "Cannot apply a GIMP LUT on a 16-bit buffer", |
1206 | 0 | "ApplyLUT()"); |
1207 | 0 | return -1; |
1208 | 0 | } |
1209 | 0 | err = ParseGimpLUT(lut_def, byteLut, iColorIndex); |
1210 | 0 | } else { |
1211 | 0 | if (eDT == GDT_Byte) |
1212 | 0 | err = ParseDefaultLUT(lut_def, byteLut, 255); |
1213 | 0 | else { |
1214 | 0 | uint16Lut = (GByte *)malloc(65536); |
1215 | 0 | if (uint16Lut == NULL) { |
1216 | 0 | msSetError(MS_MEMERR, "Cannot allocate 16-bit LUT", "ApplyLUT()"); |
1217 | 0 | return -1; |
1218 | 0 | } |
1219 | 0 | err = ParseDefaultLUT(lut_def, uint16Lut, 65535); |
1220 | 0 | } |
1221 | 0 | } |
1222 | | |
1223 | | /* -------------------------------------------------------------------- */ |
1224 | | /* Apply LUT. */ |
1225 | | /* -------------------------------------------------------------------- */ |
1226 | 0 | if (eDT == GDT_Byte) { |
1227 | 0 | for (i = 0; i < nPixelCount; i++) |
1228 | 0 | pabyOutBuffer[i] = byteLut[((GByte *)pInBuffer)[i]]; |
1229 | 0 | } else { |
1230 | 0 | for (i = 0; i < nPixelCount; i++) |
1231 | 0 | pabyOutBuffer[i] = uint16Lut[((GUInt16 *)pInBuffer)[i]]; |
1232 | 0 | free(uint16Lut); |
1233 | 0 | } |
1234 | |
|
1235 | 0 | return err; |
1236 | 0 | } |
1237 | | |
1238 | | /************************************************************************/ |
1239 | | /* LoadGDALImages() */ |
1240 | | /* */ |
1241 | | /* This call will load and process 1-4 bands of input for the */ |
1242 | | /* selected rectangle, loading the result into the passed 8bit */ |
1243 | | /* buffer. The processing options include scaling. */ |
1244 | | /************************************************************************/ |
1245 | | |
1246 | | static int LoadGDALImages(GDALDatasetH hDS, int band_numbers[4], int band_count, |
1247 | | layerObj *layer, int src_xoff, int src_yoff, |
1248 | | int src_xsize, int src_ysize, GByte *pabyWholeBuffer, |
1249 | | int dst_xsize, int dst_ysize, int *pbHaveRGBNoData, |
1250 | | int *pnNoData1, int *pnNoData2, int *pnNoData3, |
1251 | | bool *pbScaled, double *pdfScaleMin, |
1252 | | double *pdfScaleRatio, bool **ppbIsNoDataBuffer) |
1253 | | |
1254 | 0 | { |
1255 | 0 | int iColorIndex, result_code = 0; |
1256 | 0 | CPLErr eErr; |
1257 | 0 | float *pafWholeRawData; |
1258 | 0 | char **papszLUTs; |
1259 | |
|
1260 | 0 | *pbScaled = false; |
1261 | 0 | *pdfScaleMin = 0; |
1262 | 0 | *pdfScaleRatio = 0; |
1263 | 0 | *ppbIsNoDataBuffer = NULL; |
1264 | | |
1265 | | /* -------------------------------------------------------------------- */ |
1266 | | /* If we have no alpha band, but we do have three input */ |
1267 | | /* bands, then check for nodata values. If we only have one */ |
1268 | | /* input band, then nodata will already have been adderssed as */ |
1269 | | /* part of the real or manufactured color table. */ |
1270 | | /* -------------------------------------------------------------------- */ |
1271 | 0 | if (band_count == 3) { |
1272 | 0 | *pnNoData1 = (int)msGetGDALNoDataValue( |
1273 | 0 | layer, GDALGetRasterBand(hDS, band_numbers[0]), pbHaveRGBNoData); |
1274 | |
|
1275 | 0 | if (*pbHaveRGBNoData) |
1276 | 0 | *pnNoData2 = (int)msGetGDALNoDataValue( |
1277 | 0 | layer, GDALGetRasterBand(hDS, band_numbers[1]), pbHaveRGBNoData); |
1278 | 0 | if (*pbHaveRGBNoData) |
1279 | 0 | *pnNoData3 = (int)msGetGDALNoDataValue( |
1280 | 0 | layer, GDALGetRasterBand(hDS, band_numbers[2]), pbHaveRGBNoData); |
1281 | 0 | } |
1282 | | |
1283 | | /* -------------------------------------------------------------------- */ |
1284 | | /* Are we doing a simple, non-scaling case? If so, read directly */ |
1285 | | /* and return. */ |
1286 | | /* -------------------------------------------------------------------- */ |
1287 | 0 | if (CSLFetchNameValue(layer->processing, "SCALE") == NULL && |
1288 | 0 | CSLFetchNameValue(layer->processing, "SCALE_1") == NULL && |
1289 | 0 | CSLFetchNameValue(layer->processing, "SCALE_2") == NULL && |
1290 | 0 | CSLFetchNameValue(layer->processing, "SCALE_3") == NULL && |
1291 | 0 | CSLFetchNameValue(layer->processing, "SCALE_4") == NULL) { |
1292 | |
|
1293 | 0 | GDALDataType eDT; |
1294 | 0 | GUInt16 *panBuffer = NULL; |
1295 | 0 | void *pBuffer; |
1296 | |
|
1297 | 0 | papszLUTs = LoadLUTs(layer, band_count); |
1298 | 0 | if (papszLUTs == NULL) { |
1299 | 0 | return -1; |
1300 | 0 | } |
1301 | | |
1302 | 0 | eDT = GetDataTypeAppropriateForLUTS(papszLUTs); |
1303 | 0 | if (eDT == GDT_UInt16) { |
1304 | 0 | panBuffer = (GUInt16 *)malloc(sizeof(GUInt16) * dst_xsize * dst_ysize * |
1305 | 0 | band_count); |
1306 | |
|
1307 | 0 | if (panBuffer == NULL) { |
1308 | 0 | msSetError(MS_MEMERR, |
1309 | 0 | "Allocating work uint16 image of size %dx%dx%d failed.", |
1310 | 0 | "msDrawRasterLayerGDAL()", dst_xsize, dst_ysize, band_count); |
1311 | 0 | FreeLUTs(papszLUTs); |
1312 | 0 | return -1; |
1313 | 0 | } |
1314 | 0 | pBuffer = panBuffer; |
1315 | 0 | } else |
1316 | 0 | pBuffer = pabyWholeBuffer; |
1317 | | |
1318 | 0 | eErr = GDALDatasetRasterIO(hDS, GF_Read, src_xoff, src_yoff, src_xsize, |
1319 | 0 | src_ysize, pBuffer, dst_xsize, dst_ysize, eDT, |
1320 | 0 | band_count, band_numbers, 0, 0, 0); |
1321 | |
|
1322 | 0 | if (eErr != CE_None) { |
1323 | 0 | msSetError(MS_IOERR, "GDALDatasetRasterIO() failed: %s", "drawGDAL()", |
1324 | 0 | CPLGetLastErrorMsg()); |
1325 | 0 | FreeLUTs(papszLUTs); |
1326 | 0 | msFree(panBuffer); |
1327 | 0 | return -1; |
1328 | 0 | } |
1329 | | |
1330 | 0 | for (iColorIndex = 0; iColorIndex < band_count && result_code == 0; |
1331 | 0 | iColorIndex++) { |
1332 | 0 | result_code = |
1333 | 0 | ApplyLUT(iColorIndex + 1, papszLUTs[iColorIndex], |
1334 | 0 | (GByte *)pBuffer + dst_xsize * dst_ysize * iColorIndex * |
1335 | 0 | (GDALGetDataTypeSize(eDT) / 8), |
1336 | 0 | eDT, pabyWholeBuffer + dst_xsize * dst_ysize * iColorIndex, |
1337 | 0 | dst_xsize * dst_ysize); |
1338 | 0 | } |
1339 | |
|
1340 | 0 | FreeLUTs(papszLUTs); |
1341 | 0 | msFree(panBuffer); |
1342 | 0 | return result_code; |
1343 | 0 | } |
1344 | | |
1345 | | /* -------------------------------------------------------------------- */ |
1346 | | /* Disable use of nodata if we are doing scaling. */ |
1347 | | /* -------------------------------------------------------------------- */ |
1348 | 0 | *pbHaveRGBNoData = FALSE; |
1349 | | |
1350 | | /* -------------------------------------------------------------------- */ |
1351 | | /* We need to do some scaling. Will load into either a 16bit */ |
1352 | | /* unsigned or a floating point buffer depending on the source */ |
1353 | | /* data. We offer a special case for 16U data because it is */ |
1354 | | /* common and it is a substantial win to avoid a lot of floating */ |
1355 | | /* point operations on it. */ |
1356 | | /* -------------------------------------------------------------------- */ |
1357 | | /* TODO */ |
1358 | | |
1359 | | /* -------------------------------------------------------------------- */ |
1360 | | /* Allocate the raw imagery buffer, and load into it (band */ |
1361 | | /* interleaved). */ |
1362 | | /* -------------------------------------------------------------------- */ |
1363 | 0 | pafWholeRawData = |
1364 | 0 | (float *)malloc(sizeof(float) * dst_xsize * dst_ysize * band_count); |
1365 | |
|
1366 | 0 | if (pafWholeRawData == NULL) { |
1367 | 0 | msSetError(MS_MEMERR, |
1368 | 0 | "Allocating work float image of size %dx%dx%d failed.", |
1369 | 0 | "msDrawRasterLayerGDAL()", dst_xsize, dst_ysize, band_count); |
1370 | 0 | return -1; |
1371 | 0 | } |
1372 | | |
1373 | 0 | eErr = GDALDatasetRasterIO(hDS, GF_Read, src_xoff, src_yoff, src_xsize, |
1374 | 0 | src_ysize, pafWholeRawData, dst_xsize, dst_ysize, |
1375 | 0 | GDT_Float32, band_count, band_numbers, 0, 0, 0); |
1376 | |
|
1377 | 0 | if (eErr != CE_None) { |
1378 | 0 | msSetError(MS_IOERR, "GDALDatasetRasterIO() failed: %s", "drawGDAL()", |
1379 | 0 | CPLGetLastErrorMsg()); |
1380 | |
|
1381 | 0 | free(pafWholeRawData); |
1382 | 0 | return -1; |
1383 | 0 | } |
1384 | | |
1385 | 0 | papszLUTs = LoadLUTs(layer, band_count); |
1386 | 0 | if (papszLUTs == NULL) { |
1387 | 0 | free(pafWholeRawData); |
1388 | 0 | return -1; |
1389 | 0 | } |
1390 | | |
1391 | 0 | if (GetDataTypeAppropriateForLUTS(papszLUTs) != GDT_Byte) { |
1392 | 0 | msDebug("LoadGDALImage(%s): One of the LUT contains a input value > 255.\n" |
1393 | 0 | "This is not properly supported in combination with SCALE\n", |
1394 | 0 | layer->name); |
1395 | 0 | } |
1396 | | |
1397 | | /* -------------------------------------------------------------------- */ |
1398 | | /* Fetch the scale processing option. */ |
1399 | | /* -------------------------------------------------------------------- */ |
1400 | 0 | for (iColorIndex = 0; iColorIndex < band_count; iColorIndex++) { |
1401 | 0 | unsigned char *pabyBuffer; |
1402 | 0 | double dfScaleMin = 0.0, dfScaleMax = 255.0, dfScaleRatio = 0, |
1403 | 0 | dfNoDataValue; |
1404 | 0 | const char *pszScaleInfo; |
1405 | 0 | float *pafRawData; |
1406 | 0 | int nPixelCount = dst_xsize * dst_ysize, i, bGotNoData = FALSE; |
1407 | 0 | GDALRasterBandH hBand = GDALGetRasterBand(hDS, band_numbers[iColorIndex]); |
1408 | 0 | pszScaleInfo = CSLFetchNameValue(layer->processing, "SCALE"); |
1409 | 0 | const bool bIsAllBandScale = pszScaleInfo != NULL; |
1410 | 0 | if (pszScaleInfo == NULL) { |
1411 | 0 | char szBandScalingName[20]; |
1412 | |
|
1413 | 0 | sprintf(szBandScalingName, "SCALE_%d", iColorIndex + 1); |
1414 | 0 | pszScaleInfo = CSLFetchNameValue(layer->processing, szBandScalingName); |
1415 | 0 | } |
1416 | |
|
1417 | 0 | if (pszScaleInfo != NULL) { |
1418 | 0 | char **papszTokens; |
1419 | |
|
1420 | 0 | papszTokens = CSLTokenizeStringComplex(pszScaleInfo, " ,", FALSE, FALSE); |
1421 | 0 | if (CSLCount(papszTokens) == 1 && EQUAL(papszTokens[0], "AUTO")) { |
1422 | 0 | if (bIsAllBandScale && |
1423 | 0 | GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand && |
1424 | 0 | GDALGetRasterDataType(hBand) == GDT_Byte) { |
1425 | | // Do not try to scale the alpha band, as it is going to produce |
1426 | | // wrong results |
1427 | 0 | dfScaleMin = 0.0; |
1428 | 0 | dfScaleMax = 255.0; |
1429 | 0 | dfScaleRatio = 1.0; |
1430 | 0 | } else { |
1431 | 0 | dfScaleMin = dfScaleMax = 0.0; |
1432 | 0 | } |
1433 | 0 | } else if (CSLCount(papszTokens) != 2) { |
1434 | 0 | free(pafWholeRawData); |
1435 | 0 | CSLDestroy(papszTokens); |
1436 | 0 | FreeLUTs(papszLUTs); |
1437 | 0 | msSetError(MS_MISCERR, |
1438 | 0 | "SCALE PROCESSING option unparsable for layer %s.", |
1439 | 0 | "msDrawGDAL()", layer->name); |
1440 | 0 | return -1; |
1441 | 0 | } else { |
1442 | 0 | dfScaleMin = atof(papszTokens[0]); |
1443 | 0 | dfScaleMax = atof(papszTokens[1]); |
1444 | 0 | } |
1445 | 0 | CSLDestroy(papszTokens); |
1446 | 0 | } |
1447 | | |
1448 | | /* -------------------------------------------------------------------- */ |
1449 | | /* If we are using autoscaling, then compute the max and min */ |
1450 | | /* now. Perhaps we should eventually honour the offsite value */ |
1451 | | /* as a nodata value, or get it from GDAL. */ |
1452 | | /* -------------------------------------------------------------------- */ |
1453 | 0 | pafRawData = pafWholeRawData + iColorIndex * dst_xsize * dst_ysize; |
1454 | |
|
1455 | 0 | dfNoDataValue = msGetGDALNoDataValue(layer, hBand, &bGotNoData); |
1456 | | |
1457 | | /* we force assignment to a float rather than letting pafRawData[i] |
1458 | | get promoted to double later to avoid float precision issues. */ |
1459 | 0 | float fNoDataValue = (float)dfNoDataValue; |
1460 | |
|
1461 | 0 | if (dfScaleMin == dfScaleMax) { |
1462 | 0 | int bMinMaxSet = 0; |
1463 | | |
1464 | | /* we force assignment to a float rather than letting pafRawData[i] |
1465 | | get promoted to double later to avoid float precision issues. */ |
1466 | 0 | float fNoDataValue = (float)dfNoDataValue; |
1467 | |
|
1468 | 0 | for (i = 0; i < nPixelCount; i++) { |
1469 | 0 | if (bGotNoData && IsNoData(pafRawData[i], fNoDataValue)) |
1470 | 0 | continue; |
1471 | | |
1472 | 0 | if (CPLIsNan(pafRawData[i])) |
1473 | 0 | continue; |
1474 | | |
1475 | 0 | if (!bMinMaxSet) { |
1476 | 0 | dfScaleMin = dfScaleMax = pafRawData[i]; |
1477 | 0 | bMinMaxSet = TRUE; |
1478 | 0 | } |
1479 | |
|
1480 | 0 | dfScaleMin = MS_MIN(dfScaleMin, pafRawData[i]); |
1481 | 0 | dfScaleMax = MS_MAX(dfScaleMax, pafRawData[i]); |
1482 | 0 | } |
1483 | |
|
1484 | 0 | if (dfScaleMin == dfScaleMax) |
1485 | 0 | dfScaleMax = dfScaleMin + 1.0; |
1486 | 0 | } |
1487 | |
|
1488 | 0 | if (layer->debug > 0) |
1489 | 0 | msDebug("msDrawGDAL(%s): scaling to 8bit, src range=%g,%g\n", layer->name, |
1490 | 0 | dfScaleMin, dfScaleMax); |
1491 | | |
1492 | | /* -------------------------------------------------------------------- */ |
1493 | | /* Now process the data. */ |
1494 | | /* -------------------------------------------------------------------- */ |
1495 | 0 | if (dfScaleRatio == 0.0) |
1496 | 0 | dfScaleRatio = 256.0 / (dfScaleMax - dfScaleMin); |
1497 | 0 | pabyBuffer = pabyWholeBuffer + iColorIndex * nPixelCount; |
1498 | |
|
1499 | 0 | if (iColorIndex == 0 && bGotNoData) |
1500 | 0 | *ppbIsNoDataBuffer = (bool *)calloc(nPixelCount, 1); |
1501 | |
|
1502 | 0 | for (i = 0; i < nPixelCount; i++) { |
1503 | 0 | float fScaledValue; |
1504 | |
|
1505 | 0 | if (bGotNoData && IsNoData(pafRawData[i], fNoDataValue)) { |
1506 | 0 | if (iColorIndex == 0) |
1507 | 0 | (*ppbIsNoDataBuffer)[i] = true; |
1508 | 0 | continue; |
1509 | 0 | } |
1510 | | |
1511 | 0 | fScaledValue = (float)((pafRawData[i] - dfScaleMin) * dfScaleRatio); |
1512 | |
|
1513 | 0 | if (fScaledValue < 0.0) |
1514 | 0 | pabyBuffer[i] = 0; |
1515 | 0 | else if (fScaledValue > 255.0) |
1516 | 0 | pabyBuffer[i] = 255; |
1517 | 0 | else |
1518 | 0 | pabyBuffer[i] = (int)fScaledValue; |
1519 | 0 | } |
1520 | |
|
1521 | 0 | if (iColorIndex == 0) { |
1522 | 0 | *pbScaled = true; |
1523 | 0 | *pdfScaleMin = dfScaleMin; |
1524 | 0 | *pdfScaleRatio = dfScaleRatio; |
1525 | 0 | } |
1526 | | |
1527 | | /* -------------------------------------------------------------------- */ |
1528 | | /* Report a warning if NODATA keyword was applied. We are */ |
1529 | | /* unable to utilize it since we can't return any pixels marked */ |
1530 | | /* as nodata from this function. Need to fix someday. */ |
1531 | | /* -------------------------------------------------------------------- */ |
1532 | 0 | if (bGotNoData) |
1533 | 0 | msDebug("LoadGDALImage(%s): NODATA value %g in GDAL\n" |
1534 | 0 | "file or PROCESSING directive largely ignored. Not yet fully " |
1535 | 0 | "supported for\n" |
1536 | 0 | "unclassified scaled data. The NODATA value is excluded from " |
1537 | 0 | "auto-scaling\n" |
1538 | 0 | "min/max computation, but will not be transparent.\n", |
1539 | 0 | layer->name, dfNoDataValue); |
1540 | | |
1541 | | /* -------------------------------------------------------------------- */ |
1542 | | /* Apply LUT if there is one. */ |
1543 | | /* -------------------------------------------------------------------- */ |
1544 | 0 | result_code = ApplyLUT(iColorIndex + 1, papszLUTs[iColorIndex], pabyBuffer, |
1545 | 0 | GDT_Byte, pabyBuffer, dst_xsize * dst_ysize); |
1546 | 0 | if (result_code == -1) { |
1547 | 0 | free(pafWholeRawData); |
1548 | 0 | FreeLUTs(papszLUTs); |
1549 | 0 | return result_code; |
1550 | 0 | } |
1551 | 0 | } |
1552 | | |
1553 | 0 | free(pafWholeRawData); |
1554 | 0 | FreeLUTs(papszLUTs); |
1555 | |
|
1556 | 0 | return result_code; |
1557 | 0 | } |
1558 | | |
1559 | | /************************************************************************/ |
1560 | | /* msGetGDALGeoTransform() */ |
1561 | | /* */ |
1562 | | /* Cover function that tries GDALGetGeoTransform(), a world */ |
1563 | | /* file or OWS extents. It is assumed that TLOCK_GDAL is held */ |
1564 | | /* before this function is called. */ |
1565 | | /************************************************************************/ |
1566 | | |
1567 | | int msGetGDALGeoTransform(GDALDatasetH hDS, mapObj *map, layerObj *layer, |
1568 | | double *padfGeoTransform) |
1569 | | |
1570 | 0 | { |
1571 | 0 | const char *extent_priority = NULL; |
1572 | 0 | const char *value; |
1573 | 0 | const char *gdalDesc; |
1574 | 0 | const char *fullPath; |
1575 | 0 | char *fileExtension = NULL; |
1576 | 0 | char szPath[MS_MAXPATHLEN]; |
1577 | 0 | int fullPathLen; |
1578 | 0 | int success = 0; |
1579 | 0 | rectObj rect; |
1580 | | |
1581 | | /* -------------------------------------------------------------------- */ |
1582 | | /* some GDAL drivers (ie. GIF) don't set geotransform on failure. */ |
1583 | | /* -------------------------------------------------------------------- */ |
1584 | 0 | padfGeoTransform[0] = 0.0; |
1585 | 0 | padfGeoTransform[1] = 1.0; |
1586 | 0 | padfGeoTransform[2] = 0.0; |
1587 | 0 | padfGeoTransform[3] = GDALGetRasterYSize(hDS); |
1588 | 0 | padfGeoTransform[4] = 0.0; |
1589 | 0 | padfGeoTransform[5] = -1.0; |
1590 | | |
1591 | | /* -------------------------------------------------------------------- */ |
1592 | | /* Do we want to override GDAL with a worldfile if one is present? */ |
1593 | | /* -------------------------------------------------------------------- */ |
1594 | 0 | extent_priority = CSLFetchNameValue(layer->processing, "EXTENT_PRIORITY"); |
1595 | |
|
1596 | 0 | if (extent_priority != NULL && EQUALN(extent_priority, "WORLD", 5)) { |
1597 | | /* is there a user configured worldfile? */ |
1598 | 0 | value = CSLFetchNameValue(layer->processing, "WORLDFILE"); |
1599 | |
|
1600 | 0 | if (value != NULL) { |
1601 | | /* at this point, fullPath may be a filePath or path only */ |
1602 | 0 | fullPath = msBuildPath(szPath, map->mappath, value); |
1603 | | |
1604 | | /* fullPath is a path only, so let's append the dataset filename */ |
1605 | 0 | if (strrchr(szPath, '.') == NULL) { |
1606 | 0 | fullPathLen = strlen(fullPath); |
1607 | 0 | gdalDesc = msStripPath((char *)GDALGetDescription(hDS)); |
1608 | |
|
1609 | 0 | if ((fullPathLen + strlen(gdalDesc)) < MS_MAXPATHLEN) { |
1610 | 0 | strcpy((char *)(fullPath + fullPathLen), gdalDesc); |
1611 | 0 | } else { |
1612 | 0 | msDebug( |
1613 | 0 | "Path length to alternative worldfile exceeds MS_MAXPATHLEN.\n"); |
1614 | 0 | fullPath = NULL; |
1615 | 0 | } |
1616 | 0 | } |
1617 | | /* fullPath has a filename included, so get the extension */ |
1618 | 0 | else { |
1619 | 0 | fileExtension = msStrdup(strrchr(szPath, '.') + 1); |
1620 | 0 | } |
1621 | 0 | } |
1622 | | /* common behavior with worldfile generated from basename + .wld */ |
1623 | 0 | else { |
1624 | 0 | fullPath = GDALGetDescription(hDS); |
1625 | 0 | fileExtension = msStrdup("wld"); |
1626 | 0 | } |
1627 | |
|
1628 | 0 | if (fullPath) |
1629 | 0 | success = GDALReadWorldFile(fullPath, fileExtension, padfGeoTransform); |
1630 | |
|
1631 | 0 | if (success && layer->debug >= MS_DEBUGLEVEL_VVV) { |
1632 | 0 | msDebug("Worldfile location: %s.\n", fullPath); |
1633 | 0 | } else if (layer->debug >= MS_DEBUGLEVEL_VVV) { |
1634 | 0 | msDebug("Failed using worldfile location: %s.\n", |
1635 | 0 | fullPath ? fullPath : "(null)"); |
1636 | 0 | } |
1637 | |
|
1638 | 0 | msFree(fileExtension); |
1639 | |
|
1640 | 0 | if (success) |
1641 | 0 | return MS_SUCCESS; |
1642 | 0 | } |
1643 | | |
1644 | | /* -------------------------------------------------------------------- */ |
1645 | | /* Try GDAL. */ |
1646 | | /* */ |
1647 | | /* Make sure that ymax is always at the top, and ymin at the */ |
1648 | | /* bottom ... that is flip any files without the usual */ |
1649 | | /* orientation. This is intended to enable display of "raw" */ |
1650 | | /* files with no coordinate system otherwise they break down in */ |
1651 | | /* many ways. */ |
1652 | | /* -------------------------------------------------------------------- */ |
1653 | 0 | if (GDALGetGeoTransform(hDS, padfGeoTransform) == CE_None) { |
1654 | 0 | if (padfGeoTransform[5] == 1.0 && padfGeoTransform[3] == 0.0) { |
1655 | 0 | padfGeoTransform[5] = -1.0; |
1656 | 0 | padfGeoTransform[3] = GDALGetRasterYSize(hDS); |
1657 | 0 | } |
1658 | |
|
1659 | 0 | return MS_SUCCESS; |
1660 | 0 | } |
1661 | | |
1662 | | /* -------------------------------------------------------------------- */ |
1663 | | /* Try worldfile. */ |
1664 | | /* -------------------------------------------------------------------- */ |
1665 | 0 | if (GDALGetDescription(hDS) != NULL && |
1666 | 0 | GDALReadWorldFile(GDALGetDescription(hDS), "wld", padfGeoTransform)) { |
1667 | 0 | return MS_SUCCESS; |
1668 | 0 | } |
1669 | | |
1670 | | /* -------------------------------------------------------------------- */ |
1671 | | /* Do we have the extent keyword on the layer? We only use */ |
1672 | | /* this if we have a single raster associated with the layer as */ |
1673 | | /* opposed to a tile index. */ |
1674 | | /* */ |
1675 | | /* Arguably this ought to take priority over all the other */ |
1676 | | /* stuff. */ |
1677 | | /* -------------------------------------------------------------------- */ |
1678 | 0 | if (MS_VALID_EXTENT(layer->extent) && layer->data != NULL) { |
1679 | 0 | rect = layer->extent; |
1680 | |
|
1681 | 0 | padfGeoTransform[0] = rect.minx; |
1682 | 0 | padfGeoTransform[1] = |
1683 | 0 | (rect.maxx - rect.minx) / (double)GDALGetRasterXSize(hDS); |
1684 | 0 | padfGeoTransform[2] = 0; |
1685 | 0 | padfGeoTransform[3] = rect.maxy; |
1686 | 0 | padfGeoTransform[4] = 0; |
1687 | 0 | padfGeoTransform[5] = |
1688 | 0 | (rect.miny - rect.maxy) / (double)GDALGetRasterYSize(hDS); |
1689 | |
|
1690 | 0 | return MS_SUCCESS; |
1691 | 0 | } |
1692 | | |
1693 | | /* -------------------------------------------------------------------- */ |
1694 | | /* We didn't find any info ... use the default. */ |
1695 | | /* Reset our default geotransform. GDALGetGeoTransform() may */ |
1696 | | /* have altered it even if GDALGetGeoTransform() failed. */ |
1697 | | /* -------------------------------------------------------------------- */ |
1698 | 0 | padfGeoTransform[0] = 0.0; |
1699 | 0 | padfGeoTransform[1] = 1.0; |
1700 | 0 | padfGeoTransform[2] = 0.0; |
1701 | 0 | padfGeoTransform[3] = GDALGetRasterYSize(hDS); |
1702 | 0 | padfGeoTransform[4] = 0.0; |
1703 | 0 | padfGeoTransform[5] = -1.0; |
1704 | |
|
1705 | 0 | return MS_FAILURE; |
1706 | 0 | } |
1707 | | |
1708 | | /************************************************************************/ |
1709 | | /* msDrawRasterLayerGDAL_RawMode() */ |
1710 | | /* */ |
1711 | | /* Handle the loading and application of data in rawmode. */ |
1712 | | /************************************************************************/ |
1713 | | |
1714 | | static int |
1715 | | msDrawRasterLayerGDAL_RawMode(mapObj *map, layerObj *layer, imageObj *image, |
1716 | | GDALDatasetH hDS, int src_xoff, int src_yoff, |
1717 | | int src_xsize, int src_ysize, int dst_xoff, |
1718 | | int dst_yoff, int dst_xsize, int dst_ysize) |
1719 | | |
1720 | 0 | { |
1721 | 0 | void *pBuffer; |
1722 | 0 | GDALDataType eDataType; |
1723 | 0 | int *band_list, band_count; |
1724 | 0 | int i, j, k, band; |
1725 | 0 | CPLErr eErr; |
1726 | 0 | float *f_nodatas = NULL; |
1727 | 0 | unsigned char *b_nodatas = NULL; |
1728 | 0 | GInt16 *i_nodatas = NULL; |
1729 | 0 | int got_nodata = FALSE; |
1730 | 0 | rasterBufferObj *mask_rb = NULL; |
1731 | 0 | rasterBufferObj s_mask_rb; |
1732 | 0 | if (layer->mask) { |
1733 | 0 | int ret; |
1734 | 0 | layerObj *maskLayer = GET_LAYER(map, msGetLayerIndex(map, layer->mask)); |
1735 | 0 | mask_rb = &s_mask_rb; |
1736 | 0 | memset(mask_rb, 0, sizeof(s_mask_rb)); |
1737 | 0 | ret = MS_IMAGE_RENDERER(maskLayer->maskimage) |
1738 | 0 | ->getRasterBufferHandle(maskLayer->maskimage, mask_rb); |
1739 | 0 | if (ret != MS_SUCCESS) |
1740 | 0 | return -1; |
1741 | 0 | } |
1742 | | |
1743 | 0 | if (image->format->bands > 256) { |
1744 | 0 | msSetError(MS_IMGERR, "Too many bands (more than 256).", |
1745 | 0 | "msDrawRasterLayerGDAL_RawMode()"); |
1746 | 0 | return -1; |
1747 | 0 | } |
1748 | | |
1749 | | /* -------------------------------------------------------------------- */ |
1750 | | /* What data type do we need? */ |
1751 | | /* -------------------------------------------------------------------- */ |
1752 | 0 | if (image->format->imagemode == MS_IMAGEMODE_INT16) |
1753 | 0 | eDataType = GDT_Int16; |
1754 | 0 | else if (image->format->imagemode == MS_IMAGEMODE_FLOAT32) |
1755 | 0 | eDataType = GDT_Float32; |
1756 | 0 | else if (image->format->imagemode == MS_IMAGEMODE_BYTE) |
1757 | 0 | eDataType = GDT_Byte; |
1758 | 0 | else |
1759 | 0 | return -1; |
1760 | | |
1761 | | /* -------------------------------------------------------------------- */ |
1762 | | /* Work out the band list. */ |
1763 | | /* -------------------------------------------------------------------- */ |
1764 | 0 | band_list = msGetGDALBandList(layer, hDS, image->format->bands, &band_count); |
1765 | 0 | if (band_list == NULL) |
1766 | 0 | return -1; |
1767 | | |
1768 | 0 | if (band_count != image->format->bands) { |
1769 | 0 | free(band_list); |
1770 | 0 | msSetError(MS_IMGERR, |
1771 | 0 | "BANDS PROCESSING directive has wrong number of bands, expected " |
1772 | 0 | "%d, got %d.", |
1773 | 0 | "msDrawRasterLayerGDAL_RawMode()", image->format->bands, |
1774 | 0 | band_count); |
1775 | 0 | return -1; |
1776 | 0 | } |
1777 | | |
1778 | | /* -------------------------------------------------------------------- */ |
1779 | | /* Do we have nodata values? */ |
1780 | | /* -------------------------------------------------------------------- */ |
1781 | 0 | f_nodatas = (float *)calloc(band_count, sizeof(float)); |
1782 | 0 | if (f_nodatas == NULL) { |
1783 | 0 | msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", |
1784 | 0 | "msDrawRasterLayerGDAL_RawMode()", __FILE__, __LINE__, |
1785 | 0 | (unsigned int)(sizeof(float) * band_count)); |
1786 | 0 | free(band_list); |
1787 | 0 | return -1; |
1788 | 0 | } |
1789 | | |
1790 | 0 | if (band_count <= 3 && |
1791 | 0 | (layer->offsite.red != -1 || layer->offsite.green != -1 || |
1792 | 0 | layer->offsite.blue != -1)) { |
1793 | 0 | if (band_count > 0) |
1794 | 0 | f_nodatas[0] = layer->offsite.red; |
1795 | 0 | if (band_count > 1) |
1796 | 0 | f_nodatas[1] = layer->offsite.green; |
1797 | 0 | if (band_count > 2) |
1798 | 0 | f_nodatas[2] = layer->offsite.blue; |
1799 | 0 | got_nodata = TRUE; |
1800 | 0 | } |
1801 | |
|
1802 | 0 | if (!got_nodata) { |
1803 | 0 | got_nodata = TRUE; |
1804 | 0 | for (band = 0; band < band_count && got_nodata; band++) { |
1805 | 0 | f_nodatas[band] = msGetGDALNoDataValue( |
1806 | 0 | layer, GDALGetRasterBand(hDS, band_list[band]), &got_nodata); |
1807 | 0 | } |
1808 | 0 | } |
1809 | |
|
1810 | 0 | if (!got_nodata) { |
1811 | 0 | free(f_nodatas); |
1812 | 0 | f_nodatas = NULL; |
1813 | 0 | } else if (eDataType == GDT_Byte) { |
1814 | 0 | b_nodatas = (unsigned char *)f_nodatas; |
1815 | 0 | for (band = 0; band < band_count; band++) |
1816 | 0 | b_nodatas[band] = (unsigned char)f_nodatas[band]; |
1817 | 0 | } else if (eDataType == GDT_Int16) { |
1818 | 0 | i_nodatas = (GInt16 *)f_nodatas; |
1819 | 0 | for (band = 0; band < band_count; band++) |
1820 | 0 | i_nodatas[band] = (GInt16)f_nodatas[band]; |
1821 | 0 | } |
1822 | | |
1823 | | /* -------------------------------------------------------------------- */ |
1824 | | /* Allocate buffer, and read data into it. */ |
1825 | | /* -------------------------------------------------------------------- */ |
1826 | 0 | pBuffer = malloc(((size_t)dst_xsize) * dst_ysize * image->format->bands * |
1827 | 0 | (GDALGetDataTypeSize(eDataType) / 8)); |
1828 | 0 | if (pBuffer == NULL) { |
1829 | 0 | msSetError(MS_MEMERR, "Allocating work image of size %dx%d failed.", |
1830 | 0 | "msDrawRasterLayerGDAL()", dst_xsize, dst_ysize); |
1831 | 0 | free(band_list); |
1832 | 0 | free(f_nodatas); |
1833 | 0 | return -1; |
1834 | 0 | } |
1835 | | |
1836 | 0 | eErr = |
1837 | 0 | GDALDatasetRasterIO(hDS, GF_Read, src_xoff, src_yoff, src_xsize, |
1838 | 0 | src_ysize, pBuffer, dst_xsize, dst_ysize, eDataType, |
1839 | 0 | image->format->bands, band_list, 0, 0, 0); |
1840 | 0 | free(band_list); |
1841 | |
|
1842 | 0 | if (eErr != CE_None) { |
1843 | 0 | msSetError(MS_IOERR, "GDALRasterIO() failed: %s", |
1844 | 0 | "msDrawRasterLayerGDAL_RawMode()", CPLGetLastErrorMsg()); |
1845 | 0 | free(pBuffer); |
1846 | 0 | free(f_nodatas); |
1847 | 0 | return -1; |
1848 | 0 | } |
1849 | | |
1850 | | /* -------------------------------------------------------------------- */ |
1851 | | /* Transfer the data to the imageObj. */ |
1852 | | /* -------------------------------------------------------------------- */ |
1853 | 0 | k = 0; |
1854 | 0 | for (band = 0; band < image->format->bands; band++) { |
1855 | 0 | for (i = dst_yoff; i < dst_yoff + dst_ysize; i++) { |
1856 | 0 | if (image->format->imagemode == MS_IMAGEMODE_INT16) { |
1857 | 0 | for (j = dst_xoff; j < dst_xoff + dst_xsize; j++) { |
1858 | 0 | int off = j + i * image->width + band * image->width * image->height; |
1859 | 0 | int off_mask = j + i * image->width; |
1860 | |
|
1861 | 0 | if ((i_nodatas && ((GInt16 *)pBuffer)[k] == i_nodatas[band]) || |
1862 | 0 | SKIP_MASK(j, i)) { |
1863 | 0 | k++; |
1864 | 0 | continue; |
1865 | 0 | } |
1866 | | |
1867 | 0 | image->img.raw_16bit[off] = ((GInt16 *)pBuffer)[k++]; |
1868 | 0 | MS_SET_BIT(image->img_mask, off_mask); |
1869 | 0 | } |
1870 | 0 | } else if (image->format->imagemode == MS_IMAGEMODE_FLOAT32) { |
1871 | 0 | for (j = dst_xoff; j < dst_xoff + dst_xsize; j++) { |
1872 | 0 | int off = j + i * image->width + band * image->width * image->height; |
1873 | 0 | int off_mask = j + i * image->width; |
1874 | |
|
1875 | 0 | if ((f_nodatas && ((float *)pBuffer)[k] == f_nodatas[band]) || |
1876 | 0 | SKIP_MASK(j, i)) { |
1877 | 0 | k++; |
1878 | 0 | continue; |
1879 | 0 | } |
1880 | | |
1881 | 0 | image->img.raw_float[off] = ((float *)pBuffer)[k++]; |
1882 | 0 | MS_SET_BIT(image->img_mask, off_mask); |
1883 | 0 | } |
1884 | 0 | } else if (image->format->imagemode == MS_IMAGEMODE_BYTE) { |
1885 | 0 | for (j = dst_xoff; j < dst_xoff + dst_xsize; j++) { |
1886 | 0 | int off = j + i * image->width + band * image->width * image->height; |
1887 | 0 | int off_mask = j + i * image->width; |
1888 | |
|
1889 | 0 | if ((b_nodatas && ((unsigned char *)pBuffer)[k] == b_nodatas[band]) || |
1890 | 0 | SKIP_MASK(j, i)) { |
1891 | 0 | k++; |
1892 | 0 | continue; |
1893 | 0 | } |
1894 | | |
1895 | 0 | image->img.raw_byte[off] = ((unsigned char *)pBuffer)[k++]; |
1896 | 0 | MS_SET_BIT(image->img_mask, off_mask); |
1897 | 0 | } |
1898 | 0 | } |
1899 | 0 | } |
1900 | 0 | } |
1901 | |
|
1902 | 0 | free(pBuffer); |
1903 | 0 | free(f_nodatas); |
1904 | |
|
1905 | 0 | return 0; |
1906 | 0 | } |
1907 | | |
1908 | | /************************************************************************/ |
1909 | | /* msDrawRasterLayerGDAL_16BitClassifcation() */ |
1910 | | /* */ |
1911 | | /* Handle the rendering of rasters going through a 16bit */ |
1912 | | /* classification lookup instead of the more common 8bit one. */ |
1913 | | /* */ |
1914 | | /* Currently we are using one data path where we load the */ |
1915 | | /* raster into a floating point buffer, and then scale to */ |
1916 | | /* 16bit. Eventually we could add optimizations for some of */ |
1917 | | /* the 16bit cases at the cost of some complication. */ |
1918 | | /************************************************************************/ |
1919 | | |
1920 | | static int msDrawRasterLayerGDAL_16BitClassification( |
1921 | | mapObj *map, layerObj *layer, rasterBufferObj *rb, GDALRasterBandH hBand, |
1922 | | int src_xoff, int src_yoff, int src_xsize, int src_ysize, int dst_xoff, |
1923 | | int dst_yoff, int dst_xsize, int dst_ysize) |
1924 | | |
1925 | 0 | { |
1926 | 0 | float *pafRawData; |
1927 | 0 | double dfScaleMin = 0.0, dfScaleMax = 0.0, dfScaleRatio; |
1928 | 0 | int nPixelCount = dst_xsize * dst_ysize, i, nBucketCount = 0; |
1929 | 0 | GDALDataType eDataType; |
1930 | 0 | float fDataMin = 0.0, fDataMax = 255.0, fNoDataValue; |
1931 | 0 | const char *pszScaleInfo; |
1932 | 0 | const char *pszBuckets; |
1933 | 0 | int *cmap, c, j, k, bGotNoData = FALSE, bGotFirstValue; |
1934 | 0 | unsigned char *rb_cmap[4]; |
1935 | 0 | CPLErr eErr; |
1936 | 0 | rasterBufferObj *mask_rb = NULL; |
1937 | 0 | rasterBufferObj s_mask_rb; |
1938 | 0 | int lastC; |
1939 | 0 | struct mstimeval starttime = {0}, endtime = {0}; |
1940 | |
|
1941 | 0 | const char *pszClassifyScaled; |
1942 | 0 | int bClassifyScaled = FALSE; |
1943 | |
|
1944 | 0 | if (layer->mask) { |
1945 | 0 | int ret; |
1946 | 0 | layerObj *maskLayer = GET_LAYER(map, msGetLayerIndex(map, layer->mask)); |
1947 | 0 | mask_rb = &s_mask_rb; |
1948 | 0 | memset(mask_rb, 0, sizeof(s_mask_rb)); |
1949 | 0 | ret = MS_IMAGE_RENDERER(maskLayer->maskimage) |
1950 | 0 | ->getRasterBufferHandle(maskLayer->maskimage, mask_rb); |
1951 | 0 | if (ret != MS_SUCCESS) |
1952 | 0 | return -1; |
1953 | 0 | } |
1954 | | |
1955 | 0 | assert(rb->type == MS_BUFFER_BYTE_RGBA); |
1956 | | |
1957 | | /* ==================================================================== */ |
1958 | | /* Read the requested data in one gulp into a floating point */ |
1959 | | /* buffer. */ |
1960 | | /* ==================================================================== */ |
1961 | 0 | pafRawData = (float *)malloc(sizeof(float) * dst_xsize * dst_ysize); |
1962 | 0 | if (pafRawData == NULL) { |
1963 | 0 | msSetError(MS_MEMERR, "Out of memory allocating working buffer.", |
1964 | 0 | "msDrawRasterLayerGDAL_16BitClassification()"); |
1965 | 0 | return -1; |
1966 | 0 | } |
1967 | | |
1968 | 0 | eErr = GDALRasterIO(hBand, GF_Read, src_xoff, src_yoff, src_xsize, src_ysize, |
1969 | 0 | pafRawData, dst_xsize, dst_ysize, GDT_Float32, 0, 0); |
1970 | |
|
1971 | 0 | if (eErr != CE_None) { |
1972 | 0 | free(pafRawData); |
1973 | 0 | msSetError(MS_IOERR, "GDALRasterIO() failed: %s", |
1974 | 0 | "msDrawRasterLayerGDAL_16BitClassification()", |
1975 | 0 | CPLGetLastErrorMsg()); |
1976 | 0 | return -1; |
1977 | 0 | } |
1978 | | |
1979 | 0 | fNoDataValue = (float)msGetGDALNoDataValue(layer, hBand, &bGotNoData); |
1980 | | |
1981 | | /* ==================================================================== */ |
1982 | | /* Determine scaling. */ |
1983 | | /* ==================================================================== */ |
1984 | 0 | eDataType = GDALGetRasterDataType(hBand); |
1985 | | |
1986 | | /* -------------------------------------------------------------------- */ |
1987 | | /* Scan for absolute min/max of this block. */ |
1988 | | /* -------------------------------------------------------------------- */ |
1989 | 0 | bGotFirstValue = FALSE; |
1990 | |
|
1991 | 0 | for (i = 1; i < nPixelCount; i++) { |
1992 | 0 | if (bGotNoData && IsNoData(pafRawData[i], fNoDataValue)) |
1993 | 0 | continue; |
1994 | | |
1995 | 0 | if (CPLIsNan(pafRawData[i])) |
1996 | 0 | continue; |
1997 | | |
1998 | 0 | if (!bGotFirstValue) { |
1999 | 0 | fDataMin = fDataMax = pafRawData[i]; |
2000 | 0 | bGotFirstValue = TRUE; |
2001 | 0 | } else { |
2002 | 0 | fDataMin = MS_MIN(fDataMin, pafRawData[i]); |
2003 | 0 | fDataMax = MS_MAX(fDataMax, pafRawData[i]); |
2004 | 0 | } |
2005 | 0 | } |
2006 | | |
2007 | | /* -------------------------------------------------------------------- */ |
2008 | | /* Fetch the scale classification option. */ |
2009 | | /* -------------------------------------------------------------------- */ |
2010 | 0 | pszClassifyScaled = CSLFetchNameValue(layer->processing, "CLASSIFY_SCALED"); |
2011 | 0 | if (pszClassifyScaled != NULL) |
2012 | 0 | bClassifyScaled = CSLTestBoolean(pszClassifyScaled); |
2013 | | |
2014 | | /* -------------------------------------------------------------------- */ |
2015 | | /* Fetch the scale processing option. */ |
2016 | | /* -------------------------------------------------------------------- */ |
2017 | 0 | pszBuckets = CSLFetchNameValue(layer->processing, "SCALE_BUCKETS"); |
2018 | 0 | pszScaleInfo = CSLFetchNameValue(layer->processing, "SCALE"); |
2019 | |
|
2020 | 0 | if (pszScaleInfo != NULL) { |
2021 | 0 | char **papszTokens; |
2022 | |
|
2023 | 0 | papszTokens = CSLTokenizeStringComplex(pszScaleInfo, " ,", FALSE, FALSE); |
2024 | 0 | if (CSLCount(papszTokens) == 1 && EQUAL(papszTokens[0], "AUTO")) { |
2025 | 0 | dfScaleMin = dfScaleMax = 0.0; |
2026 | 0 | } else if (CSLCount(papszTokens) != 2) { |
2027 | 0 | free(pafRawData); |
2028 | 0 | CSLDestroy(papszTokens); |
2029 | 0 | msSetError(MS_MISCERR, "SCALE PROCESSING option unparsable for layer %s.", |
2030 | 0 | "msDrawGDAL()", layer->name); |
2031 | 0 | return -1; |
2032 | 0 | } else { |
2033 | 0 | dfScaleMin = atof(papszTokens[0]); |
2034 | 0 | dfScaleMax = atof(papszTokens[1]); |
2035 | 0 | } |
2036 | 0 | CSLDestroy(papszTokens); |
2037 | 0 | } |
2038 | | |
2039 | | /* -------------------------------------------------------------------- */ |
2040 | | /* Special integer cases for scaling. */ |
2041 | | /* */ |
2042 | | /* TODO: Treat Int32 and UInt32 case the same way *if* the min */ |
2043 | | /* and max are less than 65536 apart. */ |
2044 | | /* -------------------------------------------------------------------- */ |
2045 | 0 | if (eDataType == GDT_Byte || eDataType == GDT_Int16 || |
2046 | 0 | eDataType == GDT_UInt16) { |
2047 | 0 | if (pszScaleInfo == NULL) { |
2048 | 0 | dfScaleMin = fDataMin - 0.5; |
2049 | 0 | dfScaleMax = fDataMax + 0.5; |
2050 | 0 | } |
2051 | |
|
2052 | 0 | if (pszBuckets == NULL) { |
2053 | 0 | nBucketCount = (int)floor(fDataMax - fDataMin + 1.1); |
2054 | 0 | } |
2055 | 0 | } |
2056 | | |
2057 | | /* -------------------------------------------------------------------- */ |
2058 | | /* General case if no scaling values provided in mapfile. */ |
2059 | | /* -------------------------------------------------------------------- */ |
2060 | 0 | else if (dfScaleMin == 0.0 && dfScaleMax == 0.0) { |
2061 | 0 | double dfEpsilon = (fDataMax - fDataMin) / (65536 * 2); |
2062 | 0 | dfScaleMin = fDataMin - dfEpsilon; |
2063 | 0 | dfScaleMax = fDataMax + dfEpsilon; |
2064 | 0 | } |
2065 | | |
2066 | | /* -------------------------------------------------------------------- */ |
2067 | | /* How many buckets? Only set if we don't already have a value. */ |
2068 | | /* -------------------------------------------------------------------- */ |
2069 | 0 | if (pszBuckets == NULL) { |
2070 | 0 | if (nBucketCount == 0) |
2071 | 0 | nBucketCount = 65536; |
2072 | 0 | } else { |
2073 | 0 | nBucketCount = atoi(pszBuckets); |
2074 | 0 | if (nBucketCount < 2) { |
2075 | 0 | free(pafRawData); |
2076 | 0 | msSetError( |
2077 | 0 | MS_MISCERR, |
2078 | 0 | "SCALE_BUCKETS PROCESSING option is not a value of 2 or more: %s.", |
2079 | 0 | "msDrawRasterLayerGDAL_16BitClassification()", pszBuckets); |
2080 | 0 | return -1; |
2081 | 0 | } |
2082 | 0 | } |
2083 | | |
2084 | | /* -------------------------------------------------------------------- */ |
2085 | | /* Compute scaling ratio. */ |
2086 | | /* -------------------------------------------------------------------- */ |
2087 | 0 | if (dfScaleMax == dfScaleMin) |
2088 | 0 | dfScaleMax = dfScaleMin + 1.0; |
2089 | |
|
2090 | 0 | dfScaleRatio = nBucketCount / (dfScaleMax - dfScaleMin); |
2091 | |
|
2092 | 0 | if (layer->debug > 0) |
2093 | 0 | msDebug("msDrawRasterGDAL_16BitClassification(%s):\n" |
2094 | 0 | " scaling to %d buckets from range=%g,%g.\n", |
2095 | 0 | layer->name, nBucketCount, dfScaleMin, dfScaleMax); |
2096 | | |
2097 | | /* ==================================================================== */ |
2098 | | /* Compute classification lookup table. */ |
2099 | | /* ==================================================================== */ |
2100 | |
|
2101 | 0 | cmap = (int *)msSmallCalloc(sizeof(int), nBucketCount); |
2102 | 0 | rb_cmap[0] = (unsigned char *)msSmallCalloc(1, nBucketCount); |
2103 | 0 | rb_cmap[1] = (unsigned char *)msSmallCalloc(1, nBucketCount); |
2104 | 0 | rb_cmap[2] = (unsigned char *)msSmallCalloc(1, nBucketCount); |
2105 | 0 | rb_cmap[3] = (unsigned char *)msSmallCalloc(1, nBucketCount); |
2106 | |
|
2107 | 0 | if (layer->debug >= MS_DEBUGLEVEL_TUNING) { |
2108 | 0 | msGettimeofday(&starttime, NULL); |
2109 | 0 | } |
2110 | |
|
2111 | 0 | const char *sgamma = msLayerGetProcessingKey(layer, "GAMMA"); |
2112 | 0 | const double gamma = sgamma ? CPLAtof(sgamma) : 1.0; |
2113 | |
|
2114 | 0 | lastC = -1; |
2115 | 0 | for (i = 0; i < nBucketCount; i++) { |
2116 | 0 | double dfOriginalValue; |
2117 | |
|
2118 | 0 | cmap[i] = -1; |
2119 | | |
2120 | | // i = (int) ((dfOriginalValue - dfScaleMin) * dfScaleRatio+1)-1; |
2121 | 0 | dfOriginalValue = (i + 0.5) / dfScaleRatio + dfScaleMin; |
2122 | | |
2123 | | /* The creation of buckets takes a significant time when they are many, and |
2124 | | many classes as well. When iterating over buckets, a faster strategy is |
2125 | | to reuse first the last used class index. */ |
2126 | 0 | if (bClassifyScaled == TRUE) |
2127 | 0 | c = msGetClass_FloatRGB_WithFirstClassToTry(layer, (float)i, -1, -1, -1, |
2128 | 0 | lastC); |
2129 | 0 | else |
2130 | 0 | c = msGetClass_FloatRGB_WithFirstClassToTry(layer, (float)dfOriginalValue, |
2131 | 0 | -1, -1, -1, lastC); |
2132 | |
|
2133 | 0 | lastC = c; |
2134 | 0 | if (c != -1) { |
2135 | 0 | int s; |
2136 | 0 | int styleindex = 0; |
2137 | | |
2138 | | /* change colour based on colour range? */ |
2139 | 0 | if (MS_VALID_COLOR(layer->class[c] -> styles[0] -> mincolor)) { |
2140 | 0 | styleindex = -1; |
2141 | 0 | for (s = 0; s < layer->class[c] -> numstyles; s++) { |
2142 | 0 | if (MS_VALID_COLOR(layer->class[c] -> styles[s] -> mincolor) && |
2143 | 0 | MS_VALID_COLOR(layer->class[c] -> styles[s] -> maxcolor) && |
2144 | 0 | dfOriginalValue + 0.5 / dfScaleRatio >= |
2145 | 0 | layer->class[c] -> styles[s] |
2146 | 0 | -> minvalue && dfOriginalValue - 0.5 / dfScaleRatio <= |
2147 | 0 | layer -> class[c] -> styles[s] -> maxvalue) { |
2148 | 0 | msValueToRange(layer->class[c] -> styles[s], dfOriginalValue, |
2149 | 0 | MS_COLORSPACE_RGB); |
2150 | 0 | styleindex = s; |
2151 | 0 | break; |
2152 | 0 | } |
2153 | 0 | } |
2154 | 0 | } |
2155 | 0 | if (styleindex >= 0) { |
2156 | 0 | if (MS_TRANSPARENT_COLOR( |
2157 | 0 | layer->class[c] -> styles[styleindex] -> color)) { |
2158 | | /* leave it transparent */ |
2159 | 0 | } else if (MS_VALID_COLOR( |
2160 | 0 | layer->class[c] -> styles[styleindex] -> color)) { |
2161 | | /* use class color */ |
2162 | 0 | rb_cmap[0][i] = layer->class[c]->styles[styleindex]->color.red; |
2163 | 0 | rb_cmap[1][i] = layer->class[c]->styles[styleindex]->color.green; |
2164 | 0 | rb_cmap[2][i] = layer->class[c]->styles[styleindex]->color.blue; |
2165 | 0 | rb_cmap[3][i] = |
2166 | 0 | (255 * layer->class[c] -> styles[styleindex] -> opacity / 100); |
2167 | |
|
2168 | 0 | if (gamma != 1.0) { |
2169 | 0 | rb_cmap[0][i] = GAMMA_CORRECT(rb_cmap[0][i], gamma); |
2170 | 0 | rb_cmap[1][i] = GAMMA_CORRECT(rb_cmap[1][i], gamma); |
2171 | 0 | rb_cmap[2][i] = GAMMA_CORRECT(rb_cmap[2][i], gamma); |
2172 | 0 | } |
2173 | 0 | } |
2174 | 0 | } |
2175 | 0 | } |
2176 | 0 | } |
2177 | |
|
2178 | 0 | if (layer->debug >= MS_DEBUGLEVEL_TUNING) { |
2179 | 0 | msGettimeofday(&endtime, NULL); |
2180 | 0 | msDebug( |
2181 | 0 | "msDrawRasterGDAL_16BitClassification() bucket creation time: %.3fs\n", |
2182 | 0 | (endtime.tv_sec + endtime.tv_usec / 1.0e6) - |
2183 | 0 | (starttime.tv_sec + starttime.tv_usec / 1.0e6)); |
2184 | 0 | } |
2185 | | |
2186 | | /* ==================================================================== */ |
2187 | | /* Now process the data, applying to the working imageObj. */ |
2188 | | /* ==================================================================== */ |
2189 | 0 | k = 0; |
2190 | |
|
2191 | 0 | for (i = dst_yoff; i < dst_yoff + dst_ysize; i++) { |
2192 | 0 | for (j = dst_xoff; j < dst_xoff + dst_xsize; j++) { |
2193 | 0 | float fRawValue = pafRawData[k++]; |
2194 | 0 | int iMapIndex; |
2195 | | |
2196 | | /* |
2197 | | * Skip nodata pixels ... no processing. |
2198 | | */ |
2199 | 0 | if (bGotNoData && IsNoData(fRawValue, fNoDataValue)) |
2200 | 0 | continue; |
2201 | | |
2202 | 0 | if (CPLIsNan(fRawValue)) |
2203 | 0 | continue; |
2204 | | |
2205 | 0 | if (SKIP_MASK(j, i)) |
2206 | 0 | continue; |
2207 | | |
2208 | | /* |
2209 | | * The funny +1/-1 is to avoid odd rounding around zero. |
2210 | | * We could use floor() but sometimes it is expensive. |
2211 | | */ |
2212 | 0 | iMapIndex = (int)((fRawValue - dfScaleMin) * dfScaleRatio + 1) - 1; |
2213 | |
|
2214 | 0 | if (iMapIndex >= nBucketCount || iMapIndex < 0) { |
2215 | 0 | continue; |
2216 | 0 | } |
2217 | | /* currently we never have partial alpha so keep simple */ |
2218 | 0 | if (rb_cmap[3][iMapIndex] > 0) |
2219 | 0 | RB_SET_PIXEL(rb, j, i, rb_cmap[0][iMapIndex], rb_cmap[1][iMapIndex], |
2220 | 0 | rb_cmap[2][iMapIndex], rb_cmap[3][iMapIndex]); |
2221 | 0 | } |
2222 | 0 | } |
2223 | | |
2224 | | /* -------------------------------------------------------------------- */ |
2225 | | /* Cleanup */ |
2226 | | /* -------------------------------------------------------------------- */ |
2227 | 0 | free(pafRawData); |
2228 | 0 | free(cmap); |
2229 | 0 | free(rb_cmap[0]); |
2230 | 0 | free(rb_cmap[1]); |
2231 | 0 | free(rb_cmap[2]); |
2232 | 0 | free(rb_cmap[3]); |
2233 | |
|
2234 | 0 | assert(k == dst_xsize * dst_ysize); |
2235 | | |
2236 | 0 | return 0; |
2237 | 0 | } |
2238 | | |
2239 | | /************************************************************************/ |
2240 | | /* IsNoData() */ |
2241 | | /************************************************************************/ |
2242 | 0 | static bool IsNoData(double dfValue, double dfNoDataValue) { |
2243 | 0 | return isnan(dfValue) || dfValue == dfNoDataValue; |
2244 | 0 | } |
2245 | | |
2246 | | /************************************************************************/ |
2247 | | /* msGetGDALNoDataValue() */ |
2248 | | /************************************************************************/ |
2249 | | double msGetGDALNoDataValue(layerObj *layer, void *hBand, int *pbGotNoData) |
2250 | | |
2251 | 0 | { |
2252 | 0 | const char *pszNODATAOpt; |
2253 | |
|
2254 | 0 | *pbGotNoData = FALSE; |
2255 | | |
2256 | | /* -------------------------------------------------------------------- */ |
2257 | | /* Check for a NODATA setting in the .map file. */ |
2258 | | /* -------------------------------------------------------------------- */ |
2259 | 0 | pszNODATAOpt = CSLFetchNameValue(layer->processing, "NODATA"); |
2260 | 0 | if (pszNODATAOpt != NULL) { |
2261 | 0 | if (EQUAL(pszNODATAOpt, "OFF") || strlen(pszNODATAOpt) == 0) |
2262 | 0 | return -1234567.0; |
2263 | 0 | if (!EQUAL(pszNODATAOpt, "AUTO")) { |
2264 | 0 | *pbGotNoData = TRUE; |
2265 | 0 | return atof(pszNODATAOpt); |
2266 | 0 | } |
2267 | 0 | } |
2268 | | |
2269 | | /* -------------------------------------------------------------------- */ |
2270 | | /* Check for a NODATA setting on the raw file. */ |
2271 | | /* -------------------------------------------------------------------- */ |
2272 | 0 | if (hBand == NULL) |
2273 | 0 | return -1234567.0; |
2274 | 0 | else |
2275 | 0 | return GDALGetRasterNoDataValue(hBand, pbGotNoData); |
2276 | 0 | } |
2277 | | |
2278 | | /************************************************************************/ |
2279 | | /* msGetGDALBandList() */ |
2280 | | /* */ |
2281 | | /* max_bands - pass zero for no limit. */ |
2282 | | /* band_count - location in which length of the return band */ |
2283 | | /* list is placed. */ |
2284 | | /* */ |
2285 | | /* returns malloc() list of band numbers (*band_count long). */ |
2286 | | /************************************************************************/ |
2287 | | |
2288 | | int *msGetGDALBandList(layerObj *layer, void *hDS, int max_bands, |
2289 | | int *band_count) |
2290 | | |
2291 | 0 | { |
2292 | 0 | int i, file_bands; |
2293 | 0 | int *band_list = NULL; |
2294 | |
|
2295 | 0 | file_bands = GDALGetRasterCount(hDS); |
2296 | 0 | if (file_bands == 0) { |
2297 | 0 | msSetError(MS_IMGERR, |
2298 | 0 | "Attempt to operate on GDAL file with no bands, layer=%s.", |
2299 | 0 | "msGetGDALBandList()", layer->name); |
2300 | |
|
2301 | 0 | return NULL; |
2302 | 0 | } |
2303 | | |
2304 | | /* -------------------------------------------------------------------- */ |
2305 | | /* Use all (or first) bands. */ |
2306 | | /* -------------------------------------------------------------------- */ |
2307 | 0 | if (CSLFetchNameValue(layer->processing, "BANDS") == NULL) { |
2308 | 0 | if (max_bands > 0) |
2309 | 0 | *band_count = MS_MIN(file_bands, max_bands); |
2310 | 0 | else |
2311 | 0 | *band_count = file_bands; |
2312 | |
|
2313 | 0 | band_list = (int *)malloc(sizeof(int) * *band_count); |
2314 | | |
2315 | | /* FIXME MS_CHECK_ALLOC leaks papszItems */ |
2316 | 0 | MS_CHECK_ALLOC(band_list, sizeof(int) * *band_count, NULL); |
2317 | |
|
2318 | 0 | for (i = 0; i < *band_count; i++) |
2319 | 0 | band_list[i] = i + 1; |
2320 | 0 | return band_list; |
2321 | 0 | } |
2322 | | |
2323 | | /* -------------------------------------------------------------------- */ |
2324 | | /* get bands from BANDS processing directive. */ |
2325 | | /* -------------------------------------------------------------------- */ |
2326 | 0 | else { |
2327 | 0 | char **papszItems = CSLTokenizeStringComplex( |
2328 | 0 | CSLFetchNameValue(layer->processing, "BANDS"), " ,", FALSE, FALSE); |
2329 | |
|
2330 | 0 | if (CSLCount(papszItems) == 0) { |
2331 | 0 | CSLDestroy(papszItems); |
2332 | 0 | msSetError(MS_IMGERR, "BANDS PROCESSING directive has no items.", |
2333 | 0 | "msGetGDALBandList()"); |
2334 | 0 | return NULL; |
2335 | 0 | } else if (max_bands != 0 && CSLCount(papszItems) > max_bands) { |
2336 | 0 | msSetError(MS_IMGERR, |
2337 | 0 | "BANDS PROCESSING directive has wrong number of bands, " |
2338 | 0 | "expected at most %d, got %d.", |
2339 | 0 | "msGetGDALBandList()", max_bands, CSLCount(papszItems)); |
2340 | 0 | CSLDestroy(papszItems); |
2341 | 0 | return NULL; |
2342 | 0 | } |
2343 | | |
2344 | 0 | *band_count = CSLCount(papszItems); |
2345 | 0 | band_list = (int *)malloc(sizeof(int) * *band_count); |
2346 | 0 | MS_CHECK_ALLOC(band_list, sizeof(int) * *band_count, NULL); |
2347 | |
|
2348 | 0 | for (i = 0; i < *band_count; i++) { |
2349 | 0 | band_list[i] = atoi(papszItems[i]); |
2350 | 0 | if (band_list[i] < 1 || band_list[i] > GDALGetRasterCount(hDS)) { |
2351 | 0 | msSetError(MS_IMGERR, |
2352 | 0 | "BANDS PROCESSING directive includes illegal band '%s', " |
2353 | 0 | "should be from 1 to %d.", |
2354 | 0 | "msGetGDALBandList()", papszItems[i], |
2355 | 0 | GDALGetRasterCount(hDS)); |
2356 | 0 | CSLDestroy(papszItems); |
2357 | 0 | free(band_list); |
2358 | 0 | return NULL; |
2359 | 0 | } |
2360 | 0 | } |
2361 | 0 | CSLDestroy(papszItems); |
2362 | |
|
2363 | 0 | return band_list; |
2364 | 0 | } |
2365 | 0 | } |