Coverage Report

Created: 2025-06-22 06:59

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