/src/MapServer/src/mapgdal.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Implementation of support for output using GDAL. |
6 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2002, Frank Warmerdam |
10 | | * |
11 | | * Permission is hereby granted, free of charge, to any person obtaining a |
12 | | * copy of this software and associated documentation files (the "Software"), |
13 | | * to deal in the Software without restriction, including without limitation |
14 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
15 | | * and/or sell copies of the Software, and to permit persons to whom the |
16 | | * Software is furnished to do so, subject to the following conditions: |
17 | | * |
18 | | * The above copyright notice and this permission notice shall be included in |
19 | | * all copies of this Software or works derived from this Software. |
20 | | * |
21 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
22 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
24 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
26 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
27 | | * DEALINGS IN THE SOFTWARE. |
28 | | ****************************************************************************/ |
29 | | |
30 | | #include "mapserver.h" |
31 | | #include "mapthread.h" |
32 | | #include "mapgdal.h" |
33 | | #include <assert.h> |
34 | | |
35 | | #include "cpl_conv.h" |
36 | | #include "cpl_string.h" |
37 | | #include "ogr_srs_api.h" |
38 | | |
39 | | #include "gdal.h" |
40 | | |
41 | | static int bGDALInitialized = 0; |
42 | | |
43 | | /************************************************************************/ |
44 | | /* msGDALInitialize() */ |
45 | | /************************************************************************/ |
46 | | |
47 | | void msGDALInitialize(void) |
48 | | |
49 | 0 | { |
50 | 0 | if (!bGDALInitialized) { |
51 | 0 | msAcquireLock(TLOCK_GDAL); |
52 | |
|
53 | 0 | GDALAllRegister(); |
54 | 0 | CPLPushErrorHandler(CPLQuietErrorHandler); |
55 | 0 | msReleaseLock(TLOCK_GDAL); |
56 | |
|
57 | 0 | bGDALInitialized = 1; |
58 | 0 | } |
59 | 0 | } |
60 | | |
61 | | /************************************************************************/ |
62 | | /* msGDALCleanup() */ |
63 | | /************************************************************************/ |
64 | | |
65 | | void msGDALCleanup(void) |
66 | | |
67 | 0 | { |
68 | 0 | if (bGDALInitialized) { |
69 | 0 | int iRepeat = 5; |
70 | | |
71 | | /* |
72 | | ** Cleanup any unreferenced but open datasets as will tend |
73 | | ** to exist due to deferred close requests. We are careful |
74 | | ** to only close one file at a time before reflecting the |
75 | | ** list as closing some datasets may cause others to be |
76 | | ** closed (subdatasets in a VRT for instance). |
77 | | */ |
78 | 0 | GDALDatasetH *pahDSList = NULL; |
79 | 0 | int nDSCount = 0; |
80 | 0 | int bDidSomething; |
81 | |
|
82 | 0 | msAcquireLock(TLOCK_GDAL); |
83 | |
|
84 | 0 | do { |
85 | 0 | int i; |
86 | 0 | GDALGetOpenDatasets(&pahDSList, &nDSCount); |
87 | 0 | bDidSomething = FALSE; |
88 | 0 | for (i = 0; i < nDSCount && !bDidSomething; i++) { |
89 | 0 | if (GDALReferenceDataset(pahDSList[i]) == 1) { |
90 | 0 | GDALClose(pahDSList[i]); |
91 | 0 | bDidSomething = TRUE; |
92 | 0 | } else |
93 | 0 | GDALDereferenceDataset(pahDSList[i]); |
94 | 0 | } |
95 | 0 | } while (bDidSomething); |
96 | |
|
97 | 0 | while (iRepeat--) |
98 | 0 | CPLPopErrorHandler(); |
99 | |
|
100 | 0 | msReleaseLock(TLOCK_GDAL); |
101 | |
|
102 | 0 | bGDALInitialized = 0; |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | | /************************************************************************/ |
107 | | /* msCleanVSIDir() */ |
108 | | /* */ |
109 | | /* For the temporary /vsimem/msout directory we need to be sure */ |
110 | | /* things are clean before we start, and after we are done. */ |
111 | | /************************************************************************/ |
112 | | |
113 | | void msCleanVSIDir(const char *pszDir) |
114 | | |
115 | 0 | { |
116 | 0 | char **papszFiles = CPLReadDir(pszDir); |
117 | 0 | int i, nFileCount = CSLCount(papszFiles); |
118 | |
|
119 | 0 | for (i = 0; i < nFileCount; i++) { |
120 | 0 | if (strcasecmp(papszFiles[i], ".") == 0 || |
121 | 0 | strcasecmp(papszFiles[i], "..") == 0) |
122 | 0 | continue; |
123 | | |
124 | 0 | VSIUnlink(CPLFormFilename(pszDir, papszFiles[i], NULL)); |
125 | 0 | } |
126 | |
|
127 | 0 | CSLDestroy(papszFiles); |
128 | 0 | } |
129 | | |
130 | | /************************************************************************/ |
131 | | /* msSaveImageGDAL() */ |
132 | | /************************************************************************/ |
133 | | |
134 | | int msSaveImageGDAL(mapObj *map, imageObj *image, const char *filenameIn) |
135 | | |
136 | 0 | { |
137 | 0 | int bFileIsTemporary = MS_FALSE; |
138 | 0 | GDALDatasetH hMemDS, hOutputDS; |
139 | 0 | GDALDriverH hMemDriver, hOutputDriver; |
140 | 0 | int nBands = 1; |
141 | 0 | int iLine; |
142 | 0 | outputFormatObj *format = image->format; |
143 | 0 | rasterBufferObj rb; |
144 | 0 | GDALDataType eDataType = GDT_Byte; |
145 | 0 | int bUseXmp = MS_FALSE; |
146 | 0 | const char *filename = NULL; |
147 | 0 | char *filenameToFree = NULL; |
148 | 0 | const char *gdal_driver_shortname = format->driver + 5; |
149 | |
|
150 | 0 | msGDALInitialize(); |
151 | 0 | memset(&rb, 0, sizeof(rasterBufferObj)); |
152 | |
|
153 | | #ifdef USE_EXEMPI |
154 | | if (map != NULL) { |
155 | | bUseXmp = msXmpPresent(map); |
156 | | } |
157 | | #endif |
158 | | |
159 | | /* -------------------------------------------------------------------- */ |
160 | | /* Identify the proposed output driver. */ |
161 | | /* -------------------------------------------------------------------- */ |
162 | 0 | msAcquireLock(TLOCK_GDAL); |
163 | 0 | hOutputDriver = GDALGetDriverByName(gdal_driver_shortname); |
164 | 0 | if (hOutputDriver == NULL) { |
165 | 0 | msReleaseLock(TLOCK_GDAL); |
166 | 0 | msSetError(MS_MISCERR, "Failed to find %s driver.", "msSaveImageGDAL()", |
167 | 0 | gdal_driver_shortname); |
168 | 0 | return MS_FAILURE; |
169 | 0 | } |
170 | | |
171 | | /* -------------------------------------------------------------------- */ |
172 | | /* We will need to write the output to a temporary file and */ |
173 | | /* then stream to stdout if no filename is passed. If the */ |
174 | | /* driver supports virtualio then we hold the temporary file in */ |
175 | | /* memory, otherwise we try to put it in a reasonable temporary */ |
176 | | /* file location. */ |
177 | | /* -------------------------------------------------------------------- */ |
178 | 0 | if (filenameIn == NULL) { |
179 | 0 | const char *pszExtension = format->extension; |
180 | 0 | if (pszExtension == NULL) |
181 | 0 | pszExtension = "img.tmp"; |
182 | |
|
183 | 0 | if (bUseXmp == MS_FALSE && |
184 | 0 | msGDALDriverSupportsVirtualIOOutput(hOutputDriver)) { |
185 | 0 | msCleanVSIDir("/vsimem/msout"); |
186 | 0 | filenameToFree = msTmpFile(map, NULL, "/vsimem/msout/", pszExtension); |
187 | 0 | } |
188 | |
|
189 | 0 | if (filenameToFree == NULL && map != NULL) |
190 | 0 | filenameToFree = msTmpFile(map, map->mappath, NULL, pszExtension); |
191 | 0 | else if (filenameToFree == NULL) { |
192 | 0 | filenameToFree = msTmpFile(map, NULL, NULL, pszExtension); |
193 | 0 | } |
194 | 0 | filename = filenameToFree; |
195 | |
|
196 | 0 | bFileIsTemporary = MS_TRUE; |
197 | 0 | } else { |
198 | 0 | filename = filenameIn; |
199 | 0 | } |
200 | | |
201 | | /* -------------------------------------------------------------------- */ |
202 | | /* Establish the characteristics of our memory, and final */ |
203 | | /* dataset. */ |
204 | | /* -------------------------------------------------------------------- */ |
205 | |
|
206 | 0 | if (format->imagemode == MS_IMAGEMODE_RGB) { |
207 | 0 | nBands = 3; |
208 | 0 | assert(MS_RENDERER_PLUGIN(format) && format->vtable->supports_pixel_buffer); |
209 | 0 | if (MS_UNLIKELY(MS_FAILURE == |
210 | 0 | format->vtable->getRasterBufferHandle(image, &rb))) { |
211 | 0 | msReleaseLock(TLOCK_GDAL); |
212 | 0 | msFree(filenameToFree); |
213 | 0 | return MS_FAILURE; |
214 | 0 | } |
215 | 0 | } else if (format->imagemode == MS_IMAGEMODE_RGBA) { |
216 | 0 | nBands = 4; |
217 | 0 | assert(MS_RENDERER_PLUGIN(format) && format->vtable->supports_pixel_buffer); |
218 | 0 | if (MS_UNLIKELY(MS_FAILURE == |
219 | 0 | format->vtable->getRasterBufferHandle(image, &rb))) { |
220 | 0 | msReleaseLock(TLOCK_GDAL); |
221 | 0 | msFree(filenameToFree); |
222 | 0 | return MS_FAILURE; |
223 | 0 | } |
224 | 0 | } else if (format->imagemode == MS_IMAGEMODE_INT16) { |
225 | 0 | nBands = format->bands; |
226 | 0 | eDataType = GDT_Int16; |
227 | 0 | } else if (format->imagemode == MS_IMAGEMODE_FLOAT32) { |
228 | 0 | nBands = format->bands; |
229 | 0 | eDataType = GDT_Float32; |
230 | 0 | } else if (format->imagemode == MS_IMAGEMODE_BYTE) { |
231 | 0 | nBands = format->bands; |
232 | 0 | eDataType = GDT_Byte; |
233 | 0 | } else { |
234 | 0 | msReleaseLock(TLOCK_GDAL); |
235 | 0 | msSetError(MS_MEMERR, "Unknown format. This is a bug.", |
236 | 0 | "msSaveImageGDAL()"); |
237 | 0 | msFree(filenameToFree); |
238 | 0 | return MS_FAILURE; |
239 | 0 | } |
240 | | |
241 | | /* -------------------------------------------------------------------- */ |
242 | | /* Create a memory dataset which we can use as a source for a */ |
243 | | /* CreateCopy(). */ |
244 | | /* -------------------------------------------------------------------- */ |
245 | 0 | hMemDriver = GDALGetDriverByName("MEM"); |
246 | 0 | if (hMemDriver == NULL) { |
247 | 0 | msReleaseLock(TLOCK_GDAL); |
248 | 0 | msSetError(MS_MISCERR, "Failed to find MEM driver.", "msSaveImageGDAL()"); |
249 | 0 | msFree(filenameToFree); |
250 | 0 | return MS_FAILURE; |
251 | 0 | } |
252 | | |
253 | 0 | hMemDS = GDALCreate(hMemDriver, "msSaveImageGDAL_temp", image->width, |
254 | 0 | image->height, nBands, eDataType, NULL); |
255 | 0 | if (hMemDS == NULL) { |
256 | 0 | msReleaseLock(TLOCK_GDAL); |
257 | 0 | msSetError(MS_MISCERR, "Failed to create MEM dataset.", |
258 | 0 | "msSaveImageGDAL()"); |
259 | 0 | msFree(filenameToFree); |
260 | 0 | return MS_FAILURE; |
261 | 0 | } |
262 | | |
263 | | /* -------------------------------------------------------------------- */ |
264 | | /* Copy the gd image into the memory dataset. */ |
265 | | /* -------------------------------------------------------------------- */ |
266 | 0 | for (iLine = 0; iLine < image->height; iLine++) { |
267 | 0 | int iBand; |
268 | |
|
269 | 0 | for (iBand = 0; iBand < nBands; iBand++) { |
270 | 0 | CPLErr eErr; |
271 | 0 | GDALRasterBandH hBand = GDALGetRasterBand(hMemDS, iBand + 1); |
272 | |
|
273 | 0 | if (format->imagemode == MS_IMAGEMODE_INT16) { |
274 | 0 | eErr = GDALRasterIO(hBand, GF_Write, 0, iLine, image->width, 1, |
275 | 0 | image->img.raw_16bit + iLine * image->width + |
276 | 0 | iBand * image->width * image->height, |
277 | 0 | image->width, 1, GDT_Int16, 2, 0); |
278 | |
|
279 | 0 | } else if (format->imagemode == MS_IMAGEMODE_FLOAT32) { |
280 | 0 | eErr = GDALRasterIO(hBand, GF_Write, 0, iLine, image->width, 1, |
281 | 0 | image->img.raw_float + iLine * image->width + |
282 | 0 | iBand * image->width * image->height, |
283 | 0 | image->width, 1, GDT_Float32, 4, 0); |
284 | 0 | } else if (format->imagemode == MS_IMAGEMODE_BYTE) { |
285 | 0 | eErr = GDALRasterIO(hBand, GF_Write, 0, iLine, image->width, 1, |
286 | 0 | image->img.raw_byte + iLine * image->width + |
287 | 0 | iBand * image->width * image->height, |
288 | 0 | image->width, 1, GDT_Byte, 1, 0); |
289 | 0 | } else { |
290 | 0 | GByte *pabyData; |
291 | 0 | unsigned char *pixptr = NULL; |
292 | 0 | assert(rb.type == MS_BUFFER_BYTE_RGBA); |
293 | 0 | switch (iBand) { |
294 | 0 | case 0: |
295 | 0 | pixptr = rb.data.rgba.r; |
296 | 0 | break; |
297 | 0 | case 1: |
298 | 0 | pixptr = rb.data.rgba.g; |
299 | 0 | break; |
300 | 0 | case 2: |
301 | 0 | pixptr = rb.data.rgba.b; |
302 | 0 | break; |
303 | 0 | case 3: |
304 | 0 | pixptr = rb.data.rgba.a; |
305 | 0 | break; |
306 | 0 | } |
307 | 0 | assert(pixptr); |
308 | 0 | if (pixptr == NULL) { |
309 | 0 | msReleaseLock(TLOCK_GDAL); |
310 | 0 | msSetError(MS_MISCERR, "Missing RGB or A buffer.\n", |
311 | 0 | "msSaveImageGDAL()"); |
312 | 0 | GDALClose(hMemDS); |
313 | 0 | msFree(filenameToFree); |
314 | 0 | return MS_FAILURE; |
315 | 0 | } |
316 | | |
317 | 0 | pabyData = (GByte *)(pixptr + iLine * rb.data.rgba.row_step); |
318 | |
|
319 | 0 | if (rb.data.rgba.a == NULL || iBand == 3) { |
320 | 0 | eErr = GDALRasterIO(hBand, GF_Write, 0, iLine, image->width, 1, |
321 | 0 | pabyData, image->width, 1, GDT_Byte, |
322 | 0 | rb.data.rgba.pixel_step, 0); |
323 | 0 | } else { /* We need to un-pre-multiple RGB by alpha. */ |
324 | 0 | GByte *pabyUPM = (GByte *)malloc(image->width); |
325 | 0 | GByte *pabyAlpha = |
326 | 0 | (GByte *)(rb.data.rgba.a + iLine * rb.data.rgba.row_step); |
327 | 0 | int i; |
328 | |
|
329 | 0 | for (i = 0; i < image->width; i++) { |
330 | 0 | int alpha = pabyAlpha[i * rb.data.rgba.pixel_step]; |
331 | |
|
332 | 0 | if (alpha == 0) |
333 | 0 | pabyUPM[i] = 0; |
334 | 0 | else { |
335 | 0 | int result = |
336 | 0 | (pabyData[i * rb.data.rgba.pixel_step] * 255) / alpha; |
337 | |
|
338 | 0 | if (result > 255) |
339 | 0 | result = 255; |
340 | |
|
341 | 0 | pabyUPM[i] = result; |
342 | 0 | } |
343 | 0 | } |
344 | |
|
345 | 0 | eErr = GDALRasterIO(hBand, GF_Write, 0, iLine, image->width, 1, |
346 | 0 | pabyUPM, image->width, 1, GDT_Byte, 1, 0); |
347 | 0 | free(pabyUPM); |
348 | 0 | } |
349 | 0 | } |
350 | 0 | if (eErr != CE_None) { |
351 | 0 | msReleaseLock(TLOCK_GDAL); |
352 | 0 | msSetError(MS_MISCERR, "GDALRasterIO() failed.\n", "msSaveImageGDAL()"); |
353 | 0 | GDALClose(hMemDS); |
354 | 0 | msFree(filenameToFree); |
355 | 0 | return MS_FAILURE; |
356 | 0 | } |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | /* -------------------------------------------------------------------- */ |
361 | | /* Attach the palette if appropriate. */ |
362 | | /* -------------------------------------------------------------------- */ |
363 | 0 | if (format->imagemode == MS_IMAGEMODE_RGB) { |
364 | 0 | GDALSetRasterColorInterpretation(GDALGetRasterBand(hMemDS, 1), GCI_RedBand); |
365 | 0 | GDALSetRasterColorInterpretation(GDALGetRasterBand(hMemDS, 2), |
366 | 0 | GCI_GreenBand); |
367 | 0 | GDALSetRasterColorInterpretation(GDALGetRasterBand(hMemDS, 3), |
368 | 0 | GCI_BlueBand); |
369 | 0 | } else if (format->imagemode == MS_IMAGEMODE_RGBA) { |
370 | 0 | GDALSetRasterColorInterpretation(GDALGetRasterBand(hMemDS, 1), GCI_RedBand); |
371 | 0 | GDALSetRasterColorInterpretation(GDALGetRasterBand(hMemDS, 2), |
372 | 0 | GCI_GreenBand); |
373 | 0 | GDALSetRasterColorInterpretation(GDALGetRasterBand(hMemDS, 3), |
374 | 0 | GCI_BlueBand); |
375 | 0 | GDALSetRasterColorInterpretation(GDALGetRasterBand(hMemDS, 4), |
376 | 0 | GCI_AlphaBand); |
377 | 0 | } |
378 | | |
379 | | /* -------------------------------------------------------------------- */ |
380 | | /* Assign the projection and coordinate system to the memory */ |
381 | | /* dataset. */ |
382 | | /* -------------------------------------------------------------------- */ |
383 | |
|
384 | 0 | if (map != NULL) { |
385 | 0 | char *pszWKT; |
386 | |
|
387 | 0 | GDALSetGeoTransform(hMemDS, map->gt.geotransform); |
388 | |
|
389 | 0 | pszWKT = msProjectionObj2OGCWKT(&(map->projection)); |
390 | 0 | if (pszWKT != NULL) { |
391 | 0 | GDALSetProjection(hMemDS, pszWKT); |
392 | 0 | msFree(pszWKT); |
393 | 0 | } |
394 | 0 | } |
395 | | |
396 | | /* -------------------------------------------------------------------- */ |
397 | | /* Possibly assign a nodata value. */ |
398 | | /* -------------------------------------------------------------------- */ |
399 | 0 | const char *nullvalue = msGetOutputFormatOption(format, "NULLVALUE", NULL); |
400 | 0 | if (nullvalue != NULL) { |
401 | 0 | const double dfNullValue = atof(nullvalue); |
402 | 0 | for (int iBand = 0; iBand < nBands; iBand++) { |
403 | 0 | GDALRasterBandH hBand = GDALGetRasterBand(hMemDS, iBand + 1); |
404 | 0 | GDALSetRasterNoDataValue(hBand, dfNullValue); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | /* -------------------------------------------------------------------- */ |
409 | | /* Try to save resolution in the output file. */ |
410 | | /* -------------------------------------------------------------------- */ |
411 | 0 | if (image->resolution > 0) { |
412 | 0 | char res[30]; |
413 | |
|
414 | 0 | snprintf(res, sizeof(res), "%lf", image->resolution); |
415 | 0 | GDALSetMetadataItem(hMemDS, "TIFFTAG_XRESOLUTION", res, NULL); |
416 | 0 | GDALSetMetadataItem(hMemDS, "TIFFTAG_YRESOLUTION", res, NULL); |
417 | 0 | GDALSetMetadataItem(hMemDS, "TIFFTAG_RESOLUTIONUNIT", "2", NULL); |
418 | 0 | } |
419 | | |
420 | | /* -------------------------------------------------------------------- */ |
421 | | /* Separate creation options from metadata items. */ |
422 | | /* -------------------------------------------------------------------- */ |
423 | 0 | std::vector<char *> apszCreationOptions; |
424 | 0 | for (int i = 0; i < format->numformatoptions; i++) { |
425 | 0 | char *option = format->formatoptions[i]; |
426 | | |
427 | | // MDI stands for MetaDataItem |
428 | 0 | if (STARTS_WITH(option, "mdi_")) { |
429 | 0 | const char *option_without_band = option + strlen("mdi_"); |
430 | 0 | GDALMajorObjectH hObject = (GDALMajorObjectH)hMemDS; |
431 | 0 | if (STARTS_WITH(option_without_band, "BAND_")) { |
432 | 0 | int nBandNumber = atoi(option_without_band + strlen("BAND_")); |
433 | 0 | if (nBandNumber > 0 && nBandNumber <= nBands) { |
434 | 0 | const char *pszAfterBand = |
435 | 0 | strchr(option_without_band + strlen("BAND_"), '_'); |
436 | 0 | if (pszAfterBand != NULL) { |
437 | 0 | hObject = (GDALMajorObjectH)GDALGetRasterBand(hMemDS, nBandNumber); |
438 | 0 | option_without_band = pszAfterBand + 1; |
439 | 0 | } |
440 | 0 | } else { |
441 | 0 | msDebug("Invalid band number %d in metadata item option %s", |
442 | 0 | nBandNumber, option); |
443 | 0 | } |
444 | 0 | } |
445 | 0 | if (hObject) { |
446 | 0 | std::string osDomain(option_without_band); |
447 | 0 | size_t nUnderscorePos = osDomain.find('_'); |
448 | 0 | if (nUnderscorePos != std::string::npos) { |
449 | 0 | std::string osKeyValue = osDomain.substr(nUnderscorePos + 1); |
450 | 0 | osDomain.resize(nUnderscorePos); |
451 | 0 | if (osDomain == "default") |
452 | 0 | osDomain.clear(); |
453 | 0 | size_t nEqualPos = osKeyValue.find('='); |
454 | 0 | if (nEqualPos != std::string::npos) { |
455 | 0 | GDALSetMetadataItem( |
456 | 0 | hObject, osKeyValue.substr(0, nEqualPos).c_str(), |
457 | 0 | osKeyValue.substr(nEqualPos + 1).c_str(), osDomain.c_str()); |
458 | 0 | } |
459 | 0 | } else { |
460 | 0 | msDebug("Invalid format in metadata item option %s", option); |
461 | 0 | } |
462 | 0 | } |
463 | 0 | } else { |
464 | 0 | apszCreationOptions.emplace_back(option); |
465 | 0 | } |
466 | 0 | } |
467 | 0 | apszCreationOptions.emplace_back(nullptr); |
468 | | |
469 | | /* -------------------------------------------------------------------- */ |
470 | | /* Create a disk image in the selected output format from the */ |
471 | | /* memory image. */ |
472 | | /* -------------------------------------------------------------------- */ |
473 | 0 | hOutputDS = GDALCreateCopy(hOutputDriver, filename, hMemDS, FALSE, |
474 | 0 | &apszCreationOptions[0], NULL, NULL); |
475 | |
|
476 | 0 | if (hOutputDS == NULL) { |
477 | 0 | GDALClose(hMemDS); |
478 | 0 | msReleaseLock(TLOCK_GDAL); |
479 | 0 | msSetError(MS_MISCERR, "Failed to create output %s file.\n%s", |
480 | 0 | "msSaveImageGDAL()", format->driver + 5, CPLGetLastErrorMsg()); |
481 | 0 | msFree(filenameToFree); |
482 | 0 | return MS_FAILURE; |
483 | 0 | } |
484 | | |
485 | | /* closing the memory DS also frees all associated resources. */ |
486 | 0 | GDALClose(hMemDS); |
487 | |
|
488 | 0 | GDALClose(hOutputDS); |
489 | 0 | msReleaseLock(TLOCK_GDAL); |
490 | | |
491 | | /* -------------------------------------------------------------------- */ |
492 | | /* Are we writing license info into the image? */ |
493 | | /* If so, add it to the temp file on disk now. */ |
494 | | /* -------------------------------------------------------------------- */ |
495 | | #ifdef USE_EXEMPI |
496 | | if (bUseXmp == MS_TRUE) { |
497 | | if (msXmpWrite(map, filename) == MS_FAILURE) { |
498 | | /* Something bad happened. */ |
499 | | msSetError(MS_MISCERR, "XMP write to %s failed.\n", "msSaveImageGDAL()", |
500 | | filename); |
501 | | msFree(filenameToFree); |
502 | | return MS_FAILURE; |
503 | | } |
504 | | } |
505 | | #endif |
506 | | |
507 | | /* -------------------------------------------------------------------- */ |
508 | | /* Is this supposed to be a temporary file? If so, stream to */ |
509 | | /* stdout and delete the file. */ |
510 | | /* -------------------------------------------------------------------- */ |
511 | 0 | if (bFileIsTemporary) { |
512 | 0 | VSILFILE *fp; |
513 | 0 | unsigned char block[4000]; |
514 | 0 | int bytes_read; |
515 | |
|
516 | 0 | if (msIO_needBinaryStdout() == MS_FAILURE) { |
517 | 0 | msFree(filenameToFree); |
518 | 0 | return MS_FAILURE; |
519 | 0 | } |
520 | | |
521 | | /* We aren't sure how far back GDAL exports the VSI*L API, so |
522 | | we only use it if we suspect we need it. But we do need it if |
523 | | holding temporary file in memory. */ |
524 | 0 | fp = VSIFOpenL(filename, "rb"); |
525 | 0 | if (fp == NULL) { |
526 | 0 | msSetError(MS_MISCERR, "Failed to open %s for streaming to stdout.", |
527 | 0 | "msSaveImageGDAL()", filename); |
528 | 0 | msFree(filenameToFree); |
529 | 0 | return MS_FAILURE; |
530 | 0 | } |
531 | | |
532 | 0 | while ((bytes_read = VSIFReadL(block, 1, sizeof(block), fp)) > 0) |
533 | 0 | msIO_fwrite(block, 1, bytes_read, stdout); |
534 | |
|
535 | 0 | VSIFCloseL(fp); |
536 | |
|
537 | 0 | VSIUnlink(filename); |
538 | 0 | msCleanVSIDir("/vsimem/msout"); |
539 | 0 | } |
540 | 0 | msFree(filenameToFree); |
541 | |
|
542 | 0 | return MS_SUCCESS; |
543 | 0 | } |
544 | | |
545 | | /************************************************************************/ |
546 | | /* msInitGDALOutputFormat() */ |
547 | | /************************************************************************/ |
548 | | |
549 | | int msInitDefaultGDALOutputFormat(outputFormatObj *format) |
550 | | |
551 | 0 | { |
552 | 0 | GDALDriverH hDriver; |
553 | |
|
554 | 0 | msGDALInitialize(); |
555 | | |
556 | | /* -------------------------------------------------------------------- */ |
557 | | /* check that this driver exists. Note visiting drivers should */ |
558 | | /* be pretty threadsafe so don't bother acquiring the GDAL */ |
559 | | /* lock. */ |
560 | | /* -------------------------------------------------------------------- */ |
561 | 0 | hDriver = GDALGetDriverByName(format->driver + 5); |
562 | 0 | if (hDriver == NULL) { |
563 | 0 | msSetError(MS_MISCERR, "No GDAL driver named `%s' available.", |
564 | 0 | "msInitGDALOutputFormat()", format->driver + 5); |
565 | 0 | return MS_FAILURE; |
566 | 0 | } |
567 | | |
568 | 0 | if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, NULL) == NULL && |
569 | 0 | GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, NULL) == NULL) { |
570 | 0 | msSetError(MS_MISCERR, "GDAL `%s' driver does not support output.", |
571 | 0 | "msInitGDALOutputFormat()", format->driver + 5); |
572 | 0 | return MS_FAILURE; |
573 | 0 | } |
574 | | |
575 | | /* -------------------------------------------------------------------- */ |
576 | | /* Initialize the object. */ |
577 | | /* -------------------------------------------------------------------- */ |
578 | 0 | format->imagemode = MS_IMAGEMODE_RGB; |
579 | 0 | format->renderer = MS_RENDER_WITH_AGG; |
580 | |
|
581 | 0 | if (GDALGetMetadataItem(hDriver, GDAL_DMD_MIMETYPE, NULL) != NULL) |
582 | 0 | format->mimetype = |
583 | 0 | msStrdup(GDALGetMetadataItem(hDriver, GDAL_DMD_MIMETYPE, NULL)); |
584 | 0 | if (GDALGetMetadataItem(hDriver, GDAL_DMD_EXTENSION, NULL) != NULL) |
585 | 0 | format->extension = |
586 | 0 | msStrdup(GDALGetMetadataItem(hDriver, GDAL_DMD_EXTENSION, NULL)); |
587 | |
|
588 | 0 | return MS_SUCCESS; |
589 | 0 | } |
590 | | |
591 | 0 | char **msGetStringListFromHashTable(hashTableObj *table) { |
592 | 0 | struct hashObj *tp = NULL; |
593 | 0 | int i; |
594 | 0 | char **papszRet = NULL; |
595 | |
|
596 | 0 | if (!table) |
597 | 0 | return NULL; |
598 | 0 | if (msHashIsEmpty(table)) |
599 | 0 | return NULL; |
600 | | |
601 | 0 | for (i = 0; i < MS_HASHSIZE; ++i) { |
602 | 0 | if (table->items[i] != NULL) { |
603 | 0 | for (tp = table->items[i]; tp != NULL; tp = tp->next) { |
604 | 0 | papszRet = CSLSetNameValue(papszRet, tp->key, tp->data); |
605 | 0 | } |
606 | 0 | } |
607 | 0 | } |
608 | 0 | return papszRet; |
609 | 0 | } |
610 | | |
611 | | /************************************************************************/ |
612 | | /* msProjectionObj2OGCWKT() */ |
613 | | /* */ |
614 | | /* We stick to the C API for OGRSpatialReference object access */ |
615 | | /* to allow MapServer+GDAL to be built without C++ */ |
616 | | /* complications. */ |
617 | | /* */ |
618 | | /* Note that this function will return NULL on failure, and the */ |
619 | | /* returned string should be freed with msFree(). */ |
620 | | /************************************************************************/ |
621 | | |
622 | | char *msProjectionObj2OGCWKT(projectionObj *projection) |
623 | | |
624 | 0 | { |
625 | 0 | OGRSpatialReferenceH hSRS; |
626 | 0 | char *pszWKT = NULL, *pszProj4, *pszInitEpsg = NULL; |
627 | 0 | int nLength = 0, i; |
628 | 0 | OGRErr eErr; |
629 | |
|
630 | 0 | if (projection->proj == NULL) |
631 | 0 | return NULL; |
632 | | |
633 | 0 | hSRS = OSRNewSpatialReference(NULL); |
634 | | /* -------------------------------------------------------------------- */ |
635 | | /* Look for an EPSG-like projection argument */ |
636 | | /* -------------------------------------------------------------------- */ |
637 | 0 | if ((projection->numargs == 1 || |
638 | 0 | (projection->numargs == 2 && |
639 | 0 | strstr(projection->args[1], "epsgaxis=") != NULL)) && |
640 | 0 | (pszInitEpsg = strcasestr(projection->args[0], "init=epsg:"))) { |
641 | 0 | int nEpsgCode = atoi(pszInitEpsg + strlen("init=epsg:")); |
642 | 0 | eErr = OSRImportFromEPSG(hSRS, nEpsgCode); |
643 | 0 | } else { |
644 | | /* -------------------------------------------------------------------- */ |
645 | | /* Form arguments into a full Proj.4 definition string. */ |
646 | | /* -------------------------------------------------------------------- */ |
647 | 0 | for (i = 0; i < projection->numargs; i++) |
648 | 0 | nLength += strlen(projection->args[i]) + 2; |
649 | |
|
650 | 0 | pszProj4 = (char *)CPLMalloc(nLength + 2); |
651 | 0 | pszProj4[0] = '\0'; |
652 | |
|
653 | 0 | for (i = 0; i < projection->numargs; i++) { |
654 | 0 | strcat(pszProj4, "+"); |
655 | 0 | strcat(pszProj4, projection->args[i]); |
656 | 0 | strcat(pszProj4, " "); |
657 | 0 | } |
658 | | |
659 | | /* -------------------------------------------------------------------- */ |
660 | | /* Ingest the string into OGRSpatialReference. */ |
661 | | /* -------------------------------------------------------------------- */ |
662 | 0 | eErr = OSRImportFromProj4(hSRS, pszProj4); |
663 | 0 | CPLFree(pszProj4); |
664 | 0 | } |
665 | | |
666 | | /* -------------------------------------------------------------------- */ |
667 | | /* Export as a WKT string. */ |
668 | | /* -------------------------------------------------------------------- */ |
669 | 0 | if (eErr == OGRERR_NONE) |
670 | 0 | OSRExportToWkt(hSRS, &pszWKT); |
671 | |
|
672 | 0 | OSRDestroySpatialReference(hSRS); |
673 | |
|
674 | 0 | if (pszWKT) { |
675 | 0 | char *pszWKT2 = msStrdup(pszWKT); |
676 | 0 | CPLFree(pszWKT); |
677 | |
|
678 | 0 | return pszWKT2; |
679 | 0 | } else |
680 | 0 | return NULL; |
681 | 0 | } |
682 | | |
683 | | /************************************************************************/ |
684 | | /* msGDALDriverSupportsVirtualIOOutput() */ |
685 | | /************************************************************************/ |
686 | | |
687 | 0 | int msGDALDriverSupportsVirtualIOOutput(GDALDriverH hDriver) { |
688 | | /* We need special testing here for the netCDF driver, since recent */ |
689 | | /* GDAL versions advertise VirtualIO support, but this is only for the */ |
690 | | /* read-side of the driver, not the write-side. */ |
691 | 0 | return GDALGetMetadataItem(hDriver, GDAL_DCAP_VIRTUALIO, NULL) != NULL && |
692 | 0 | !EQUAL(GDALGetDescription(hDriver), "netCDF"); |
693 | 0 | } |