Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/mapwcs.cpp
Line
Count
Source
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OpenGIS Web Coverage Server (WCS) Implementation.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 *****************************************************************************/
29
30
#include "mapserver.h"
31
#include "maperror.h"
32
#include "mapthread.h"
33
#include "mapows.h"
34
#include <assert.h>
35
36
#include <map>
37
#include <string>
38
39
#if defined(USE_WCS_SVR)
40
41
#include "mapwcs.h"
42
43
#include "maptime.h"
44
#include <time.h>
45
46
#include "gdal.h"
47
#include "cpl_string.h" /* GDAL string handling */
48
49
/************************************************************************/
50
/*                    msWCSValidateRangeSetParam()                      */
51
/************************************************************************/
52
static int msWCSValidateRangeSetParam(layerObj *lp, char *name,
53
0
                                      const char *value) {
54
0
  char **allowed_ri_values;
55
0
  char **client_ri_values;
56
0
  int allowed_count, client_count;
57
0
  int i_client, i, all_match = 1;
58
0
  char *tmpname = NULL;
59
0
  const char *ri_values_list;
60
61
0
  if (name == NULL)
62
0
    return MS_FAILURE;
63
64
  /* Fetch the available values list for the rangeset item and tokenize */
65
0
  const size_t nSize = strlen(name) + strlen("_values") + 1;
66
0
  tmpname = (char *)msSmallMalloc(nSize);
67
0
  snprintf(tmpname, nSize, "%s_values", name);
68
0
  ri_values_list = msOWSLookupMetadata(&(lp->metadata), "CO", tmpname);
69
0
  msFree(tmpname);
70
71
0
  if (ri_values_list == NULL)
72
0
    return MS_FAILURE;
73
74
0
  allowed_ri_values = msStringSplit(ri_values_list, ',', &allowed_count);
75
76
  /* Parse the client value list into tokens. */
77
0
  client_ri_values = msStringSplit(value, ',', &client_count);
78
79
  /* test each client value against the allowed list. */
80
81
0
  for (i_client = 0; all_match && i_client < client_count; i_client++) {
82
0
    for (i = 0; i < allowed_count && strcasecmp(client_ri_values[i_client],
83
0
                                                allowed_ri_values[i]) != 0;
84
0
         i++) {
85
0
    }
86
87
0
    if (i == allowed_count)
88
0
      all_match = 0;
89
0
  }
90
91
0
  msFreeCharArray(allowed_ri_values, allowed_count);
92
0
  msFreeCharArray(client_ri_values, client_count);
93
94
0
  if (all_match == 0)
95
0
    return MS_FAILURE;
96
0
  else
97
0
    return MS_SUCCESS;
98
0
}
99
100
/************************************************************************/
101
/*                    msWCSConvertRangeSetToString()                    */
102
/************************************************************************/
103
0
static char *msWCSConvertRangeSetToString(const char *value) {
104
0
  char **tokens;
105
0
  int numtokens;
106
0
  double min, max, res;
107
0
  double val;
108
0
  char buf1[128], *buf2 = NULL;
109
110
0
  if (strchr(value, '/')) { /* value is min/max/res */
111
0
    tokens = msStringSplit(value, '/', &numtokens);
112
0
    if (tokens == NULL || numtokens != 3) {
113
0
      msFreeCharArray(tokens, numtokens);
114
0
      return NULL; /* not a set of equally spaced intervals */
115
0
    }
116
117
0
    min = atof(tokens[0]);
118
0
    max = atof(tokens[1]);
119
0
    res = atof(tokens[2]);
120
0
    msFreeCharArray(tokens, numtokens);
121
122
0
    for (val = min; val <= max; val += res) {
123
0
      if (val == min)
124
0
        snprintf(buf1, sizeof(buf1), "%g", val);
125
0
      else
126
0
        snprintf(buf1, sizeof(buf1), ",%g", val);
127
0
      buf2 = msStringConcatenate(buf2, buf1);
128
0
    }
129
130
0
    return buf2;
131
0
  } else
132
0
    return msStrdup(value);
133
0
}
134
135
/************************************************************************/
136
/*                           msWCSException()                           */
137
/************************************************************************/
138
int msWCSException(mapObj *map, const char *code, const char *locator,
139
1.61k
                   const char *version) {
140
1.61k
  char *pszEncodedVal = NULL;
141
1.61k
  char version_string[OWS_VERSION_MAXLEN];
142
143
1.61k
  if (version == NULL)
144
556
    version = "1.0.0";
145
146
1.61k
#if defined(USE_LIBXML2)
147
1.61k
  if (msOWSParseVersionString(version) >= OWS_2_0_0)
148
748
    return msWCSException20(
149
748
        map, code, locator,
150
748
        msOWSGetVersionString(msOWSParseVersionString(version),
151
748
                              version_string));
152
870
#endif
153
154
870
  if (msOWSParseVersionString(version) >= OWS_1_1_0)
155
29
    return msWCSException11(
156
29
        map, code, locator,
157
29
        msOWSGetVersionString(msOWSParseVersionString(version),
158
29
                              version_string));
159
160
841
  msIO_setHeader("Content-Type", "application/vnd.ogc.se_xml; charset=UTF-8");
161
841
  msIO_sendHeaders();
162
163
  /* msIO_printf("Content-Type: text/xml%c%c",10,10); */
164
165
841
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
166
167
841
  msIO_printf("<ServiceExceptionReport version=\"1.2.0\"\n");
168
841
  msIO_printf("xmlns=\"http://www.opengis.net/ogc\" ");
169
841
  msIO_printf("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
170
841
  pszEncodedVal = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
171
841
  msIO_printf("xsi:schemaLocation=\"http://www.opengis.net/ogc "
172
841
              "%s/wcs/1.0.0/OGC-exception.xsd\">\n",
173
841
              pszEncodedVal);
174
841
  msFree(pszEncodedVal);
175
841
  msIO_printf("  <ServiceException");
176
841
  if (code) {
177
841
    msIO_printf(" code=\"%s\"", code);
178
841
  }
179
841
  if (locator) {
180
841
    msIO_printf(" locator=\"%s\"", locator);
181
841
  }
182
841
  msIO_printf(">");
183
841
  msWriteErrorXML(stdout);
184
841
  msIO_printf("  </ServiceException>\n");
185
841
  msIO_printf("</ServiceExceptionReport>\n");
186
187
841
  msResetErrorList();
188
189
841
  return MS_FAILURE;
190
870
}
191
192
/************************************************************************/
193
/*                    msWCSPrintRequestCapability()                     */
194
/************************************************************************/
195
196
static void msWCSPrintRequestCapability(const char *request_tag,
197
3.25k
                                        const char *script_url) {
198
3.25k
  msIO_printf("    <%s>\n", request_tag);
199
200
3.25k
  msIO_printf("      <DCPType>\n");
201
3.25k
  msIO_printf("        <HTTP>\n");
202
3.25k
  msIO_printf("          <Get><OnlineResource xlink:type=\"simple\" "
203
3.25k
              "xlink:href=\"%s\" /></Get>\n",
204
3.25k
              script_url);
205
3.25k
  msIO_printf("        </HTTP>\n");
206
3.25k
  msIO_printf("      </DCPType>\n");
207
3.25k
  msIO_printf("      <DCPType>\n");
208
3.25k
  msIO_printf("        <HTTP>\n");
209
3.25k
  msIO_printf("          <Post><OnlineResource xlink:type=\"simple\" "
210
3.25k
              "xlink:href=\"%s\" /></Post>\n",
211
3.25k
              script_url);
212
3.25k
  msIO_printf("        </HTTP>\n");
213
3.25k
  msIO_printf("      </DCPType>\n");
214
215
3.25k
  msIO_printf("    </%s>\n", request_tag);
216
3.25k
}
217
218
/************************************************************************/
219
/*                         msWCSCreateParams()                          */
220
/************************************************************************/
221
1.33k
static wcsParamsObj *msWCSCreateParams() {
222
1.33k
  wcsParamsObj *params;
223
224
1.33k
  params = (wcsParamsObj *)calloc(1, sizeof(wcsParamsObj));
225
1.33k
  MS_CHECK_ALLOC(params, sizeof(wcsParamsObj), NULL);
226
227
1.33k
  return params;
228
1.33k
}
229
230
/************************************************************************/
231
/*                          msWCSFreeParams()                           */
232
/************************************************************************/
233
1.33k
void msWCSFreeParams(wcsParamsObj *params) {
234
1.33k
  if (params) {
235
    /* TODO */
236
1.33k
    if (params->version)
237
1.32k
      free(params->version);
238
1.33k
    if (params->updatesequence)
239
1.21k
      free(params->updatesequence);
240
1.33k
    if (params->request)
241
1.33k
      free(params->request);
242
1.33k
    if (params->service)
243
1.33k
      free(params->service);
244
1.33k
    if (params->section)
245
144
      free(params->section);
246
1.33k
    if (params->crs)
247
205
      free(params->crs);
248
1.33k
    if (params->response_crs)
249
7
      free(params->response_crs);
250
1.33k
    if (params->format)
251
11
      free(params->format);
252
1.33k
    if (params->exceptions)
253
0
      free(params->exceptions);
254
1.33k
    if (params->time)
255
9
      free(params->time);
256
1.33k
    if (params->interpolation)
257
11
      free(params->interpolation);
258
1.33k
    CSLDestroy(params->coverages);
259
1.33k
  }
260
1.33k
}
261
262
/************************************************************************/
263
/*                       msWCSIsLayerSupported()                        */
264
/************************************************************************/
265
266
0
int msWCSIsLayerSupported(layerObj *layer) {
267
  /* only raster layers, are elligible to be served via WCS, WMS rasters are not
268
   * ok */
269
0
  if ((layer->type == MS_LAYER_RASTER) && layer->connectiontype != MS_WMS &&
270
0
      layer->name != NULL)
271
0
    return MS_TRUE;
272
273
0
  return MS_FALSE;
274
0
}
275
276
/************************************************************************/
277
/*                      msWCSGetRequestParameter()                      */
278
/*                                                                      */
279
/************************************************************************/
280
281
0
const char *msWCSGetRequestParameter(cgiRequestObj *request, const char *name) {
282
0
  int i;
283
284
0
  if (!request || !name) /* nothing to do */
285
0
    return NULL;
286
287
0
  if (request->NumParams > 0) {
288
0
    for (i = 0; i < request->NumParams; i++) {
289
0
      if (strcasecmp(request->ParamNames[i], name) == 0)
290
0
        return request->ParamValues[i];
291
0
    }
292
0
  }
293
294
0
  return NULL;
295
0
}
296
297
/************************************************************************/
298
/*                  msWCSSetDefaultBandsRangeSetInfo()                  */
299
/************************************************************************/
300
301
void msWCSSetDefaultBandsRangeSetInfo(wcsParamsObj *params,
302
0
                                      coverageMetadataObj *cm, layerObj *lp) {
303
0
  (void)params;
304
305
  /* This function will provide default rangeset information for the "special"
306
   */
307
  /* "bands" rangeset if it appears in the axes list but has no specifics
308
   * provided */
309
  /* in the metadata.   */
310
311
0
  const char *value;
312
0
  char *bandlist;
313
0
  size_t bufferSize = 0;
314
0
  int i;
315
316
  /* Does this item exist in the axes list?  */
317
318
0
  value = msOWSLookupMetadata(&(lp->metadata), "CO", "rangeset_axes");
319
0
  if (value == NULL)
320
0
    return;
321
322
0
  value = strstr(value, "bands");
323
0
  if (value == NULL || (value[5] != '\0' && value[5] != ' '))
324
0
    return;
325
326
  /* Are there any w*s_bands_ metadata already? If so, skip out. */
327
0
  if (msOWSLookupMetadata(&(lp->metadata), "CO", "bands_description") != NULL ||
328
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_name") != NULL ||
329
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_label") != NULL ||
330
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values") != NULL ||
331
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values_semantic") !=
332
0
          NULL ||
333
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values_type") != NULL ||
334
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_rangeitem") != NULL ||
335
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_semantic") != NULL ||
336
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_refsys") != NULL ||
337
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_refsyslabel") != NULL ||
338
0
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_interval") != NULL)
339
0
    return;
340
341
  /* OK, we have decided to fill in the information. */
342
343
0
  msInsertHashTable(&(lp->metadata), "wcs_bands_name", "bands");
344
0
  msInsertHashTable(&(lp->metadata), "wcs_bands_label",
345
0
                    "Bands/Channels/Samples");
346
0
  msInsertHashTable(&(lp->metadata), "wcs_bands_rangeitem", "_bands"); /* ? */
347
348
0
  bufferSize = cm->bandcount * 30 + 30;
349
0
  bandlist = (char *)msSmallMalloc(bufferSize);
350
0
  strcpy(bandlist, "1");
351
0
  for (i = 1; i < cm->bandcount; i++)
352
0
    snprintf(bandlist + strlen(bandlist), bufferSize - strlen(bandlist), ",%d",
353
0
             i + 1);
354
355
0
  msInsertHashTable(&(lp->metadata), "wcs_bands_values", bandlist);
356
0
  free(bandlist);
357
0
}
358
359
/************************************************************************/
360
/*                             msWCSSetParam                            */
361
/************************************************************************/
362
363
static int msWCSSetParam(char **ppszOut, cgiRequestObj *request, int i,
364
56.0k
                         const char *pszExpectedParamName) {
365
56.0k
  if (strcasecmp(request->ParamNames[i], pszExpectedParamName) == 0) {
366
    /* Keep the first value supplied and ignore later duplicates. This avoids
367
       leaking the previously allocated value when the same parameter appears
368
       multiple times in a request (consistent with msWFSSetParam()). */
369
5.01k
    if (*ppszOut == NULL)
370
4.06k
      *ppszOut = msStrdup(request->ParamValues[i]);
371
5.01k
    return 1;
372
5.01k
  }
373
51.0k
  return 0;
374
56.0k
}
375
376
/************************************************************************/
377
/*                         msWCSParseRequest()                          */
378
/************************************************************************/
379
380
static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
381
1.33k
                             mapObj *map) {
382
1.33k
  int i, n;
383
1.33k
  char **tokens;
384
385
1.33k
  if (!request || !params) /* nothing to do */
386
0
    return MS_SUCCESS;
387
388
  /* -------------------------------------------------------------------- */
389
  /*      Check if this appears to be an XML POST WCS request.            */
390
  /* -------------------------------------------------------------------- */
391
392
1.33k
  msDebug("msWCSParseRequest(): request is %s.\n",
393
1.33k
          (request->type == MS_POST_REQUEST) ? "POST" : "KVP");
394
395
1.33k
  if (request->type == MS_POST_REQUEST && request->postrequest) {
396
0
#if defined(USE_LIBXML2)
397
0
    xmlDocPtr doc = NULL;
398
0
    xmlNodePtr root = NULL, child = NULL;
399
0
    char *tmp = NULL;
400
401
    /* parse to DOM-Structure and get root element */
402
0
    if ((doc = xmlParseMemory(request->postrequest,
403
0
                              strlen(request->postrequest))) == NULL) {
404
0
      const xmlError *error = xmlGetLastError();
405
0
      msSetError(MS_WCSERR, "XML parsing error: %s", "msWCSParseRequest()",
406
0
                 error->message);
407
0
      return MS_FAILURE;
408
0
    }
409
0
    root = xmlDocGetRootElement(doc);
410
411
    /* Get service, version and request from root */
412
0
    params->request = msStrdup((char *)root->name);
413
0
    if ((tmp = (char *)xmlGetProp(root, BAD_CAST "service")) != NULL)
414
0
      params->service = tmp;
415
0
    if ((tmp = (char *)xmlGetProp(root, BAD_CAST "version")) != NULL)
416
0
      params->version = tmp;
417
418
    /* search first level children, either CoverageID,  */
419
0
    for (child = root->children; child != NULL; child = child->next) {
420
0
      if (EQUAL((char *)child->name, "AcceptVersions")) {
421
        /* will be overridden to 1.1.1 anyway */
422
0
      } else if (EQUAL((char *)child->name, "UpdateSequence")) {
423
0
        params->updatesequence = (char *)xmlNodeGetContent(child);
424
0
      } else if (EQUAL((char *)child->name, "Sections")) {
425
0
        xmlNodePtr sectionNode = NULL;
426
        /* concatenate all sections by ',' */
427
0
        for (sectionNode = child->children; sectionNode != NULL;
428
0
             sectionNode = sectionNode->next) {
429
0
          char *content;
430
0
          if (!EQUAL((char *)sectionNode->name, "Section"))
431
0
            continue;
432
0
          content = (char *)xmlNodeGetContent(sectionNode);
433
0
          if (!params->section) {
434
0
            params->section = content;
435
0
          } else {
436
0
            params->section = msStringConcatenate(params->section, ",");
437
0
            params->section = msStringConcatenate(params->section, content);
438
0
            xmlFree(content);
439
0
          }
440
0
        }
441
0
      } else if (EQUAL((char *)child->name, "AcceptFormats")) {
442
        /* TODO: implement */
443
0
      } else if (EQUAL((char *)child->name, "Identifier")) {
444
0
        char *content = (char *)xmlNodeGetContent(child);
445
0
        params->coverages = CSLAddString(params->coverages, content);
446
0
        xmlFree(content);
447
0
      } else if (EQUAL((char *)child->name, "DomainSubset")) {
448
0
        xmlNodePtr tmpNode = NULL;
449
0
        for (tmpNode = child->children; tmpNode != NULL;
450
0
             tmpNode = tmpNode->next) {
451
0
          if (EQUAL((char *)tmpNode->name, "BoundingBox")) {
452
0
            xmlNodePtr cornerNode = NULL;
453
0
            params->crs = (char *)xmlGetProp(tmpNode, BAD_CAST "crs");
454
0
            if (!params->crs) {
455
0
              msSetError(MS_WCSERR, "Required parameter CRS was not supplied.",
456
0
                         "msWCSGetCoverage()");
457
0
              msWCSException(map, "MissingParameterValue", "crs",
458
0
                             params->version);
459
0
              xmlFreeDoc(doc);
460
0
              xmlCleanupParser();
461
0
              return MS_DONE;
462
0
            }
463
0
            if (strncasecmp(params->crs, "urn:ogc:def:crs:", 16) == 0 &&
464
0
                strncasecmp(params->crs + strlen(params->crs) - 8, "imageCRS",
465
0
                            8) == 0)
466
0
              strcpy(params->crs, "imageCRS");
467
0
            for (cornerNode = tmpNode->children; cornerNode != NULL;
468
0
                 cornerNode = cornerNode->next) {
469
0
              if (EQUAL((char *)cornerNode->name, "LowerCorner")) {
470
0
                char *value = (char *)xmlNodeGetContent(cornerNode);
471
0
                tokens = msStringSplit(value, ' ', &n);
472
0
                if (tokens == NULL || n < 2) {
473
0
                  msSetError(MS_WCSERR,
474
0
                             "Wrong number of arguments for LowerCorner",
475
0
                             "msWCSParseRequest()");
476
0
                  msWCSException(map, "InvalidParameterValue", "LowerCorner",
477
0
                                 params->version);
478
0
                  msFreeCharArray(tokens, n);
479
0
                  xmlFree(value);
480
0
                  xmlFreeDoc(doc);
481
0
                  xmlCleanupParser();
482
0
                  return MS_DONE;
483
0
                }
484
0
                params->bbox.minx = atof(tokens[0]);
485
0
                params->bbox.miny = atof(tokens[1]);
486
0
                msFreeCharArray(tokens, n);
487
0
                xmlFree(value);
488
0
              }
489
0
              if (EQUAL((char *)cornerNode->name, "UpperCorner")) {
490
0
                char *value = (char *)xmlNodeGetContent(cornerNode);
491
0
                tokens = msStringSplit(value, ' ', &n);
492
0
                if (tokens == NULL || n < 2) {
493
0
                  msSetError(MS_WCSERR,
494
0
                             "Wrong number of arguments for UpperCorner",
495
0
                             "msWCSParseRequest()");
496
0
                  msWCSException(map, "InvalidParameterValue", "UpperCorner",
497
0
                                 params->version);
498
0
                  msFreeCharArray(tokens, n);
499
0
                  xmlFree(value);
500
0
                  xmlFreeDoc(doc);
501
0
                  xmlCleanupParser();
502
0
                  return MS_DONE;
503
0
                }
504
0
                params->bbox.maxx = atof(tokens[0]);
505
0
                params->bbox.maxy = atof(tokens[1]);
506
0
                msFreeCharArray(tokens, n);
507
0
                xmlFree(value);
508
0
              }
509
0
            }
510
0
          }
511
0
        }
512
0
      } else if (EQUAL((char *)child->name, "RangeSubset")) {
513
        /* TODO: not implemented in mapserver WCS 1.1? */
514
0
      } else if (EQUAL((char *)child->name, "Output")) {
515
0
        xmlNodePtr tmpNode = NULL;
516
0
        params->format = (char *)xmlGetProp(child, BAD_CAST "format");
517
0
        for (tmpNode = child->children; tmpNode != NULL;
518
0
             tmpNode = tmpNode->next) {
519
0
          if (EQUAL((char *)tmpNode->name, "GridCRS")) {
520
0
            xmlNodePtr crsNode = NULL;
521
0
            for (crsNode = tmpNode->children; crsNode != NULL;
522
0
                 crsNode = crsNode->next) {
523
0
              if (EQUAL((char *)crsNode->name, "GridBaseCRS")) {
524
0
                params->response_crs = (char *)xmlNodeGetContent(crsNode);
525
0
              } else if (EQUAL((char *)crsNode->name, "GridOrigin")) {
526
0
                char *value = (char *)xmlNodeGetContent(crsNode);
527
0
                tokens = msStringSplit(value, ' ', &n);
528
0
                if (tokens == NULL || n < 2) {
529
0
                  msSetError(MS_WCSERR,
530
0
                             "Wrong number of arguments for GridOrigin",
531
0
                             "msWCSParseRequest()");
532
0
                  msWCSException(map, "InvalidParameterValue", "GridOffsets",
533
0
                                 params->version);
534
0
                  msFreeCharArray(tokens, n);
535
0
                  xmlFree(value);
536
0
                  xmlFreeDoc(doc);
537
0
                  xmlCleanupParser();
538
0
                  return MS_DONE;
539
0
                }
540
0
                params->originx = atof(tokens[0]);
541
0
                params->originy = atof(tokens[1]);
542
0
                msFreeCharArray(tokens, n);
543
0
                xmlFree(value);
544
0
              } else if (EQUAL((char *)crsNode->name, "GridOffsets")) {
545
0
                char *value = (char *)xmlNodeGetContent(crsNode);
546
0
                tokens = msStringSplit(value, ' ', &n);
547
0
                if (tokens == NULL || n < 2) {
548
0
                  msSetError(MS_WCSERR,
549
0
                             "Wrong number of arguments for GridOffsets",
550
0
                             "msWCSParseRequest()");
551
0
                  msWCSException(map, "InvalidParameterValue", "GridOffsets",
552
0
                                 params->version);
553
0
                  msFreeCharArray(tokens, n);
554
0
                  xmlFree(value);
555
0
                  xmlFreeDoc(doc);
556
0
                  xmlCleanupParser();
557
0
                  return MS_DONE;
558
0
                }
559
                /* take absolute values to convert to positive RESX/RESY style
560
                WCS 1.0 behavior.  *but* this does break some possibilities! */
561
0
                params->resx = fabs(atof(tokens[0]));
562
0
                params->resy = fabs(atof(tokens[1]));
563
0
                msFreeCharArray(tokens, n);
564
0
                xmlFree(value);
565
0
              }
566
0
            }
567
0
          }
568
0
        }
569
0
      }
570
0
    }
571
0
    xmlFreeDoc(doc);
572
0
    xmlCleanupParser();
573
0
    return MS_SUCCESS;
574
#else  /* defined(USE_LIBXML2) */
575
    msSetError(MS_WCSERR,
576
               "To enable POST requests, MapServer has to "
577
               "be compiled with libxml2.",
578
               "msWCSParseRequest()");
579
    return MS_FAILURE;
580
#endif /* defined(USE_LIBXML2) */
581
0
  }
582
583
  /* -------------------------------------------------------------------- */
584
  /*      Extract WCS KVP Parameters.                                     */
585
  /* -------------------------------------------------------------------- */
586
1.33k
  if (request->NumParams > 0) {
587
9.92k
    for (i = 0; i < request->NumParams; i++) {
588
589
8.61k
      if (msWCSSetParam(&(params->version), request, i, "VERSION")) {
590
7.30k
      } else if (msWCSSetParam(&(params->updatesequence), request, i,
591
7.30k
                               "UPDATESEQUENCE")) {
592
7.16k
      } else if (msWCSSetParam(&(params->request), request, i, "REQUEST")) {
593
5.74k
      } else if (msWCSSetParam(&(params->interpolation), request, i,
594
5.74k
                               "INTERPOLATION")) {
595
5.64k
      } else if (msWCSSetParam(&(params->service), request, i, "SERVICE")) {
596
4.19k
      } else if (msWCSSetParam(&(params->section), request, i,
597
4.19k
                               "SECTION")) { /* 1.0 */
598
        /* TODO: validate value here */
599
4.02k
      } else if (msWCSSetParam(&(params->section), request, i,
600
4.02k
                               "SECTIONS")) { /* 1.1 */
601
        /* TODO: validate value here */
602
120
      }
603
604
      /* GetCoverage parameters. */
605
3.90k
      else if (strcasecmp(request->ParamNames[i], "BBOX") == 0) {
606
84
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
607
84
        if (tokens == NULL || n != 4) {
608
18
          msSetError(MS_WCSERR, "Wrong number of arguments for BBOX.",
609
18
                     "msWCSParseRequest()");
610
18
          msFreeCharArray(tokens, n);
611
18
          return msWCSException(map, "InvalidParameterValue", "bbox",
612
18
                                params->version);
613
18
        }
614
66
        params->bbox.minx = atof(tokens[0]);
615
66
        params->bbox.miny = atof(tokens[1]);
616
66
        params->bbox.maxx = atof(tokens[2]);
617
66
        params->bbox.maxy = atof(tokens[3]);
618
619
66
        msFreeCharArray(tokens, n);
620
3.82k
      } else if (strcasecmp(request->ParamNames[i], "RESX") == 0)
621
67
        params->resx = atof(request->ParamValues[i]);
622
3.75k
      else if (strcasecmp(request->ParamNames[i], "RESY") == 0)
623
70
        params->resy = atof(request->ParamValues[i]);
624
3.68k
      else if (strcasecmp(request->ParamNames[i], "WIDTH") == 0)
625
73
        params->width = atoi(request->ParamValues[i]);
626
3.61k
      else if (strcasecmp(request->ParamNames[i], "HEIGHT") == 0)
627
67
        params->height = atoi(request->ParamValues[i]);
628
3.54k
      else if (strcasecmp(request->ParamNames[i], "COVERAGE") == 0)
629
86
        params->coverages =
630
86
            CSLAddString(params->coverages, request->ParamValues[i]);
631
3.45k
      else if (msWCSSetParam(&(params->time), request, i, "TIME")) {
632
3.38k
      } else if (msWCSSetParam(&(params->format), request, i, "FORMAT")) {
633
3.31k
      } else if (msWCSSetParam(&(params->crs), request, i, "CRS")) {
634
3.22k
      } else if (msWCSSetParam(&(params->response_crs), request, i,
635
3.22k
                               "RESPONSE_CRS")) {
636
66
      }
637
638
      /* WCS 1.1 DescribeCoverage and GetCoverage ... */
639
3.15k
      else if (strcasecmp(request->ParamNames[i], "IDENTIFIER") == 0 ||
640
3.09k
               strcasecmp(request->ParamNames[i], "IDENTIFIERS") == 0) {
641
135
        msDebug("msWCSParseRequest(): Whole String: %s\n",
642
135
                request->ParamValues[i]);
643
135
        params->coverages =
644
135
            CSLAddString(params->coverages, request->ParamValues[i]);
645
135
      }
646
      /* WCS 1.1 style BOUNDINGBOX */
647
3.02k
      else if (strcasecmp(request->ParamNames[i], "BOUNDINGBOX") == 0) {
648
394
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
649
394
        if (tokens == NULL || n < 5) {
650
4
          msSetError(MS_WCSERR, "Wrong number of arguments for BOUNDINGBOX.",
651
4
                     "msWCSParseRequest()");
652
4
          msWCSException(map, "InvalidParameterValue", "boundingbox",
653
4
                         params->version);
654
4
          msFreeCharArray(tokens, n);
655
4
          return MS_DONE;
656
4
        }
657
658
        /* NOTE: WCS 1.1 boundingbox is center of pixel oriented, not edge
659
           like in WCS 1.0.  So bbox semantics are wonky till this is fixed
660
           later in the GetCoverage processing. */
661
390
        params->bbox.minx = atof(tokens[0]);
662
390
        params->bbox.miny = atof(tokens[1]);
663
390
        params->bbox.maxx = atof(tokens[2]);
664
390
        params->bbox.maxy = atof(tokens[3]);
665
666
390
        msFree(params->crs);
667
390
        params->crs = msStrdup(tokens[4]);
668
390
        msFreeCharArray(tokens, n);
669
        /* normalize imageCRS urns to simply "imageCRS" */
670
390
        if (strncasecmp(params->crs, "urn:ogc:def:crs:", 16) == 0 &&
671
155
            strncasecmp(params->crs + strlen(params->crs) - 8, "imageCRS", 8) ==
672
155
                0)
673
69
          strcpy(params->crs, "imageCRS");
674
2.63k
      } else if (strcasecmp(request->ParamNames[i], "GridOffsets") == 0) {
675
81
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
676
81
        if (tokens == NULL || n < 2) {
677
1
          msSetError(MS_WCSERR, "Wrong number of arguments for GridOffsets",
678
1
                     "msWCSParseRequest()");
679
1
          msWCSException(map, "InvalidParameterValue", "GridOffsets",
680
1
                         params->version);
681
1
          msFreeCharArray(tokens, n);
682
1
          return MS_DONE;
683
1
        }
684
        /* take absolute values to convert to positive RESX/RESY style
685
           WCS 1.0 behavior.  *but* this does break some possibilities! */
686
80
        params->resx = fabs(atof(tokens[0]));
687
80
        params->resy = fabs(atof(tokens[1]));
688
80
        msFreeCharArray(tokens, n);
689
2.54k
      } else if (strcasecmp(request->ParamNames[i], "GridOrigin") == 0) {
690
72
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
691
72
        if (tokens == NULL || n < 2) {
692
2
          msSetError(MS_WCSERR, "Wrong number of arguments for GridOrigin",
693
2
                     "msWCSParseRequest()");
694
2
          msWCSException(map, "InvalidParameterValue", "GridOffsets",
695
2
                         params->version);
696
2
          msFreeCharArray(tokens, n);
697
2
          return MS_DONE;
698
2
        }
699
70
        params->originx = atof(tokens[0]);
700
70
        params->originy = atof(tokens[1]);
701
70
        msFreeCharArray(tokens, n);
702
70
      }
703
704
      /* and so on... */
705
8.61k
    }
706
1.33k
  }
707
  /* we are not dealing with an XML encoded request at this point */
708
1.30k
  return MS_SUCCESS;
709
1.33k
}
710
711
/************************************************************************/
712
/*           msWCSGetCapabilities_Service_ResponsibleParty()            */
713
/************************************************************************/
714
715
1.09k
static void msWCSGetCapabilities_Service_ResponsibleParty(mapObj *map) {
716
1.09k
  int bEnableTelephone = MS_FALSE, bEnableAddress = MS_FALSE,
717
1.09k
      bEnableOnlineResource = MS_FALSE;
718
719
  /* the WCS-specific way */
720
1.09k
  if (msOWSLookupMetadata(&(map->web.metadata), "CO",
721
1.09k
                          "responsibleparty_individualname") ||
722
1.09k
      msOWSLookupMetadata(&(map->web.metadata), "CO",
723
1.09k
                          "responsibleparty_organizationname")) {
724
725
0
    msIO_printf("<responsibleParty>\n");
726
0
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
727
0
                             "responsibleparty_individualname", OWS_NOERR,
728
0
                             "    <individualName>%s</individualName>\n", NULL);
729
0
    msOWSPrintEncodeMetadata(
730
0
        stdout, &(map->web.metadata), "CO", "responsibleparty_organizationname",
731
0
        OWS_NOERR, "    <organisationName>%s</organisationName>\n", NULL);
732
0
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
733
0
                             "responsibleparty_positionname", OWS_NOERR,
734
0
                             "    <positionName>%s</positionName>\n", NULL);
735
736
0
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
737
0
                            "responsibleparty_phone_voice") ||
738
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
739
0
                            "responsibleparty_phone_facsimile"))
740
0
      bEnableTelephone = MS_TRUE;
741
742
0
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
743
0
                            "responsibleparty_address_deliverypoint") ||
744
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
745
0
                            "responsibleparty_address_city") ||
746
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
747
0
                            "responsibleparty_address_administrativearea") ||
748
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
749
0
                            "responsibleparty_address_postalcode") ||
750
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
751
0
                            "responsibleparty_address_country") ||
752
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
753
0
                            "responsibleparty_address_electronicmailaddress"))
754
0
      bEnableAddress = MS_TRUE;
755
756
0
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
757
0
                            "responsibleparty_onlineresource"))
758
0
      bEnableOnlineResource = MS_TRUE;
759
760
0
    if (bEnableTelephone || bEnableAddress || bEnableOnlineResource) {
761
0
      msIO_printf("  <contactInfo>\n");
762
0
      if (bEnableTelephone) {
763
0
        msIO_printf("    <phone>\n");
764
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
765
0
                                 "responsibleparty_phone_voice", OWS_NOERR,
766
0
                                 "    <voice>%s</voice>\n", NULL);
767
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
768
0
                                 "responsibleparty_phone_facsimile", OWS_NOERR,
769
0
                                 "    <facsimile>%s</facsimile>\n", NULL);
770
0
        msIO_printf("    </phone>\n");
771
0
      }
772
0
      if (bEnableAddress) {
773
0
        msIO_printf("    <address>\n");
774
0
        msOWSPrintEncodeMetadata(
775
0
            stdout, &(map->web.metadata), "CO",
776
0
            "responsibleparty_address_deliverypoint", OWS_NOERR,
777
0
            "    <deliveryPoint>%s</deliveryPoint>\n", NULL);
778
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
779
0
                                 "responsibleparty_address_city", OWS_NOERR,
780
0
                                 "    <city>%s</city>\n", NULL);
781
0
        msOWSPrintEncodeMetadata(
782
0
            stdout, &(map->web.metadata), "CO",
783
0
            "responsibleparty_address_administrativearea", OWS_NOERR,
784
0
            "    <administrativeArea>%s</administrativeArea>\n", NULL);
785
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
786
0
                                 "responsibleparty_address_postalcode",
787
0
                                 OWS_NOERR, "    <postalCode>%s</postalCode>\n",
788
0
                                 NULL);
789
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
790
0
                                 "responsibleparty_address_country", OWS_NOERR,
791
0
                                 "    <country>%s</country>\n", NULL);
792
0
        msOWSPrintEncodeMetadata(
793
0
            stdout, &(map->web.metadata), "CO",
794
0
            "responsibleparty_address_electronicmailaddress", OWS_NOERR,
795
0
            "    <electronicMailAddress>%s</electronicMailAddress>\n", NULL);
796
0
        msIO_printf("    </address>\n");
797
0
      }
798
0
      msOWSPrintEncodeMetadata(
799
0
          stdout, &(map->web.metadata), "CO", "responsibleparty_onlineresource",
800
0
          OWS_NOERR,
801
0
          "    <onlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>\n",
802
0
          NULL);
803
0
      msIO_printf("  </contactInfo>\n");
804
0
    }
805
806
0
    msIO_printf("</responsibleParty>\n");
807
808
1.09k
  } else if (msOWSLookupMetadata(&(map->web.metadata), "CO", "contactperson") ||
809
1.09k
             msOWSLookupMetadata(
810
1.09k
                 &(map->web.metadata), "CO",
811
1.09k
                 "contactorganization")) { /* leverage WMS contact information
812
                                            */
813
814
0
    msIO_printf("<responsibleParty>\n");
815
0
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
816
0
                             "contactperson", OWS_NOERR,
817
0
                             "    <individualName>%s</individualName>\n", NULL);
818
0
    msOWSPrintEncodeMetadata(
819
0
        stdout, &(map->web.metadata), "CO", "contactorganization", OWS_NOERR,
820
0
        "    <organisationName>%s</organisationName>\n", NULL);
821
0
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
822
0
                             "contactposition", OWS_NOERR,
823
0
                             "    <positionName>%s</positionName>\n", NULL);
824
825
0
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
826
0
                            "contactvoicetelephone") ||
827
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
828
0
                            "contactfacsimiletelephone"))
829
0
      bEnableTelephone = MS_TRUE;
830
831
0
    if (msOWSLookupMetadata(&(map->web.metadata), "CO", "address") ||
832
0
        msOWSLookupMetadata(&(map->web.metadata), "CO", "city") ||
833
0
        msOWSLookupMetadata(&(map->web.metadata), "CO", "stateorprovince") ||
834
0
        msOWSLookupMetadata(&(map->web.metadata), "CO", "postcode") ||
835
0
        msOWSLookupMetadata(&(map->web.metadata), "CO", "country") ||
836
0
        msOWSLookupMetadata(&(map->web.metadata), "CO",
837
0
                            "contactelectronicmailaddress"))
838
0
      bEnableAddress = MS_TRUE;
839
840
0
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
841
0
                            "service_onlineresource"))
842
0
      bEnableOnlineResource = MS_TRUE;
843
844
0
    if (bEnableTelephone || bEnableAddress || bEnableOnlineResource) {
845
0
      msIO_printf("  <contactInfo>\n");
846
0
      if (bEnableTelephone) {
847
0
        msIO_printf("    <phone>\n");
848
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
849
0
                                 "contactvoicetelephone", OWS_NOERR,
850
0
                                 "    <voice>%s</voice>\n", NULL);
851
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
852
0
                                 "contactfacsimiletelephone", OWS_NOERR,
853
0
                                 "    <facsimile>%s</facsimile>\n", NULL);
854
0
        msIO_printf("    </phone>\n");
855
0
      }
856
0
      if (bEnableAddress) {
857
0
        msIO_printf("    <address>\n");
858
0
        msOWSPrintEncodeMetadata(
859
0
            stdout, &(map->web.metadata), "CO", "address", OWS_NOERR,
860
0
            "    <deliveryPoint>%s</deliveryPoint>\n", NULL);
861
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "city",
862
0
                                 OWS_NOERR, "    <city>%s</city>\n", NULL);
863
0
        msOWSPrintEncodeMetadata(
864
0
            stdout, &(map->web.metadata), "CO", "stateorprovince", OWS_NOERR,
865
0
            "    <administrativeArea>%s</administrativeArea>\n", NULL);
866
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "postcode",
867
0
                                 OWS_NOERR, "    <postalCode>%s</postalCode>\n",
868
0
                                 NULL);
869
0
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "country",
870
0
                                 OWS_NOERR, "    <country>%s</country>\n",
871
0
                                 NULL);
872
0
        msOWSPrintEncodeMetadata(
873
0
            stdout, &(map->web.metadata), "CO", "contactelectronicmailaddress",
874
0
            OWS_NOERR,
875
0
            "    <electronicMailAddress>%s</electronicMailAddress>\n", NULL);
876
0
        msIO_printf("    </address>\n");
877
0
      }
878
0
      msOWSPrintEncodeMetadata(
879
0
          stdout, &(map->web.metadata), "CO", "service_onlineresource",
880
0
          OWS_NOERR,
881
0
          "    <onlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>\n",
882
0
          NULL);
883
0
      msIO_printf("  </contactInfo>\n");
884
0
    }
885
0
    msIO_printf("</responsibleParty>\n");
886
0
  }
887
888
1.09k
  return;
889
1.09k
}
890
891
/************************************************************************/
892
/*                    msWCSGetCapabilities_Service()                    */
893
/************************************************************************/
894
895
1.09k
static int msWCSGetCapabilities_Service(mapObj *map, wcsParamsObj *params) {
896
  /* start the Service section, only need the full start tag if this is the only
897
   * section requested */
898
1.09k
  if (!params->section ||
899
12
      (params->section && strcasecmp(params->section, "/") == 0))
900
1.08k
    msIO_printf("<Service>\n");
901
11
  else
902
11
    msIO_printf("<Service\n"
903
11
                "   version=\"%s\" \n"
904
11
                "   updateSequence=\"%s\" \n"
905
11
                "   xmlns=\"http://www.opengis.net/wcs\" \n"
906
11
                "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
907
11
                "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
908
11
                "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
909
11
                "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
910
11
                "%s/wcs/%s/wcsCapabilities.xsd\">\n",
911
11
                params->version, params->updatesequence,
912
11
                msOWSGetSchemasLocation(map), params->version);
913
914
  /* optional metadataLink */
915
1.09k
  msOWSPrintURLType(stdout, &(map->web.metadata), "CO", "metadatalink",
916
1.09k
                    OWS_NOERR,
917
1.09k
                    "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>", NULL,
918
1.09k
                    " metadataType=\"%s\"", NULL, NULL, NULL,
919
1.09k
                    " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE,
920
1.09k
                    MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL);
921
922
1.09k
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "description",
923
1.09k
                           OWS_NOERR, "  <description>%s</description>\n",
924
1.09k
                           NULL);
925
1.09k
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "name",
926
1.09k
                           OWS_NOERR, "  <name>%s</name>\n", "MapServer WCS");
927
1.09k
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "label",
928
1.09k
                           OWS_WARN, "  <label>%s</label>\n", NULL);
929
930
  /* we are not supporting the optional keyword type, at least not yet */
931
1.09k
  msOWSPrintEncodeMetadataList(
932
1.09k
      stdout, &(map->web.metadata), "CO", "keywordlist", "  <keywords>\n",
933
1.09k
      "  </keywords>\n", "    <keyword>%s</keyword>\n", NULL);
934
935
1.09k
  msWCSGetCapabilities_Service_ResponsibleParty(map);
936
937
1.09k
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "fees",
938
1.09k
                           OWS_NOERR, "  <fees>%s</fees>\n", "NONE");
939
1.09k
  msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "CO",
940
1.09k
                               "accessconstraints", "  <accessConstraints>\n",
941
1.09k
                               "  </accessConstraints>\n", "    %s\n", "NONE");
942
943
  /* done */
944
1.09k
  msIO_printf("</Service>\n");
945
946
1.09k
  return MS_SUCCESS;
947
1.09k
}
948
949
/************************************************************************/
950
/*                  msWCSGetCapabilities_Capability()                   */
951
/************************************************************************/
952
953
static int msWCSGetCapabilities_Capability(mapObj *map, wcsParamsObj *params,
954
1.08k
                                           cgiRequestObj *req) {
955
1.08k
  char *script_url = NULL, *script_url_encoded = NULL;
956
957
  /* we need this server's onlineresource for the request section */
958
1.08k
  if ((script_url = msOWSGetOnlineResource(map, "CO", "onlineresource", req)) ==
959
1.08k
          NULL ||
960
1.08k
      (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
961
0
    free(script_url);
962
0
    free(script_url_encoded);
963
0
    return msWCSException(map, NULL, NULL, params->version);
964
0
  }
965
966
  /* start the Capability section, only need the full start tag if this is the
967
   * only section requested */
968
1.08k
  if (!params->section ||
969
3
      (params->section && strcasecmp(params->section, "/") == 0))
970
1.08k
    msIO_printf("<Capability>\n");
971
2
  else
972
2
    msIO_printf("<Capability\n"
973
2
                "   version=\"%s\" \n"
974
2
                "   updateSequence=\"%s\" \n"
975
2
                "   xmlns=\"http://www.opengis.net/wcs\" \n"
976
2
                "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
977
2
                "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
978
2
                "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
979
2
                "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
980
2
                "%s/wcs/%s/wcsCapabilities.xsd\">\n",
981
2
                params->version, params->updatesequence,
982
2
                msOWSGetSchemasLocation(map), params->version);
983
984
  /* describe the types of requests the server can handle */
985
1.08k
  msIO_printf("  <Request>\n");
986
987
1.08k
  msWCSPrintRequestCapability("GetCapabilities", script_url_encoded);
988
1.08k
  if (msOWSRequestIsEnabled(map, NULL, "C", "DescribeCoverage", MS_FALSE))
989
1.08k
    msWCSPrintRequestCapability("DescribeCoverage", script_url_encoded);
990
1.08k
  if (msOWSRequestIsEnabled(map, NULL, "C", "GetCoverage", MS_FALSE))
991
1.08k
    msWCSPrintRequestCapability("GetCoverage", script_url_encoded);
992
993
1.08k
  msIO_printf("  </Request>\n");
994
995
  /* describe the exception formats the server can produce */
996
1.08k
  msIO_printf("  <Exception>\n");
997
1.08k
  msIO_printf("    <Format>application/vnd.ogc.se_xml</Format>\n");
998
1.08k
  msIO_printf("  </Exception>\n");
999
1000
  /* describe any vendor specific capabilities */
1001
  /* msIO_printf("  <VendorSpecificCapabilities />\n"); */ /* none yet */
1002
1003
  /* done */
1004
1.08k
  msIO_printf("</Capability>\n");
1005
1006
1.08k
  free(script_url);
1007
1.08k
  free(script_url_encoded);
1008
1009
1.08k
  return MS_SUCCESS;
1010
1.08k
}
1011
1012
/************************************************************************/
1013
/*                    msWCSPrintMetadataLink()                          */
1014
/************************************************************************/
1015
1016
static void msWCSPrintMetadataLink(layerObj *layer,
1017
0
                                   const char *script_url_encoded) {
1018
0
  const char *list =
1019
0
      msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_list");
1020
0
  if (list) {
1021
0
    int ntokens = 0;
1022
0
    char **tokens = msStringSplit(list, ' ', &ntokens);
1023
0
    for (int i = 0; i < ntokens; i++) {
1024
0
      std::string key("metadatalink_");
1025
0
      key += tokens[i];
1026
0
      msOWSPrintURLType(
1027
0
          stdout, &(layer->metadata), "CO", key.c_str(), OWS_NOERR,
1028
0
          "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>", NULL,
1029
0
          " metadataType=\"%s\"", NULL, NULL, NULL, " xlink:href=\"%s\"",
1030
0
          MS_FALSE, MS_FALSE, MS_FALSE, MS_FALSE, MS_TRUE, "other", NULL, NULL,
1031
0
          NULL, NULL, NULL);
1032
0
    }
1033
0
    msFreeCharArray(tokens, ntokens);
1034
0
    return;
1035
0
  }
1036
1037
  /* optional metadataLink */
1038
0
  if (!msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_href"))
1039
0
    msMetadataSetGetMetadataURL(layer, script_url_encoded);
1040
1041
0
  msOWSPrintURLType(stdout, &(layer->metadata), "CO", "metadatalink", OWS_NOERR,
1042
0
                    "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>", NULL,
1043
0
                    " metadataType=\"%s\"", NULL, NULL, NULL,
1044
0
                    " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE,
1045
0
                    MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL);
1046
0
}
1047
1048
/************************************************************************/
1049
/*             msWCSGetCapabilities_CoverageOfferingBrief()             */
1050
/************************************************************************/
1051
1052
static int
1053
msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer,
1054
0
                                           const char *script_url_encoded) {
1055
0
  coverageMetadataObj cm;
1056
0
  int status;
1057
1058
0
  if ((layer->status == MS_DELETE) || !msWCSIsLayerSupported(layer))
1059
0
    return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */
1060
1061
0
  status = msWCSGetCoverageMetadata(layer, &cm);
1062
0
  if (status != MS_SUCCESS)
1063
0
    return MS_FAILURE;
1064
1065
  /* start the CoverageOfferingBrief section */
1066
0
  msIO_printf(
1067
0
      "  <CoverageOfferingBrief>\n"); /* is this tag right? (I hate schemas
1068
                                         without ANY examples) */
1069
1070
0
  msWCSPrintMetadataLink(layer, script_url_encoded);
1071
1072
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "description",
1073
0
                           OWS_NOERR, "    <description>%s</description>\n",
1074
0
                           NULL);
1075
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "name", OWS_NOERR,
1076
0
                           "    <name>%s</name>\n", layer->name);
1077
1078
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "label", OWS_WARN,
1079
0
                           "    <label>%s</label>\n", NULL);
1080
1081
  /* TODO: add elevation ranges to lonLatEnvelope (optional) */
1082
0
  msIO_printf(
1083
0
      "    <lonLatEnvelope srsName=\"urn:ogc:def:crs:OGC:1.3:CRS84\">\n");
1084
0
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx,
1085
0
              cm.llextent.miny); /* TODO: don't know if this is right */
1086
0
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx,
1087
0
              cm.llextent.maxy);
1088
1089
0
  msOWSPrintEncodeMetadataList(
1090
0
      stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL,
1091
0
      "      <gml:timePosition>%s</gml:timePosition>\n", NULL);
1092
1093
0
  msIO_printf("    </lonLatEnvelope>\n");
1094
1095
  /* we are not supporting the optional keyword type, at least not yet */
1096
0
  msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "keywordlist",
1097
0
                               "  <keywords>\n", "  </keywords>\n",
1098
0
                               "    <keyword>%s</keyword>\n", NULL);
1099
1100
  /* done */
1101
0
  msIO_printf("  </CoverageOfferingBrief>\n");
1102
1103
0
  msWCSFreeCoverageMetadata(&cm);
1104
1105
0
  return MS_SUCCESS;
1106
0
}
1107
1108
/************************************************************************/
1109
/*                msWCSGetCapabilities_ContentMetadata()                */
1110
/************************************************************************/
1111
1112
static int msWCSGetCapabilities_ContentMetadata(mapObj *map,
1113
                                                wcsParamsObj *params,
1114
                                                owsRequestObj *ows_request,
1115
1.09k
                                                cgiRequestObj *req) {
1116
1.09k
  int i;
1117
1.09k
  char *script_url_encoded = NULL;
1118
1119
1.09k
  {
1120
1.09k
    char *pszTmp = msOWSGetOnlineResource(map, "CO", "onlineresource", req);
1121
1.09k
    script_url_encoded = msEncodeHTMLEntities(pszTmp);
1122
1.09k
    msFree(pszTmp);
1123
1.09k
  }
1124
1125
  /* start the ContentMetadata section, only need the full start tag if this is
1126
   * the only section requested */
1127
  /* TODO: add Xlink attributes for other sources of this information  */
1128
1.09k
  if (!params->section ||
1129
11
      (params->section && strcasecmp(params->section, "/") == 0))
1130
1.08k
    msIO_printf("<ContentMetadata>\n");
1131
10
  else
1132
10
    msIO_printf("<ContentMetadata\n"
1133
10
                "   version=\"%s\" \n"
1134
10
                "   updateSequence=\"%s\" \n"
1135
10
                "   xmlns=\"http://www.opengis.net/wcs\" \n"
1136
10
                "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1137
10
                "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1138
10
                "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1139
10
                "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
1140
10
                "%s/wcs/%s/wcsCapabilities.xsd\">\n",
1141
10
                params->version, params->updatesequence,
1142
10
                msOWSGetSchemasLocation(map), params->version);
1143
1144
1.09k
  if (ows_request->numlayers == 0) {
1145
1.09k
    msIO_printf("  <!-- WARNING: No WCS layers are enabled. Check "
1146
1.09k
                "wcs/ows_enable_request settings. -->\n");
1147
1.09k
  } else {
1148
0
    for (i = 0; i < map->numlayers; i++) {
1149
0
      if (!msIntegerInArray(GET_LAYER(map, i)->index,
1150
0
                            ows_request->enabled_layers,
1151
0
                            ows_request->numlayers))
1152
0
        continue;
1153
1154
0
      if (msWCSGetCapabilities_CoverageOfferingBrief(
1155
0
              (GET_LAYER(map, i)), script_url_encoded) != MS_SUCCESS) {
1156
0
        msIO_printf("  <!-- WARNING: There was a problem with one of layers. "
1157
0
                    "See server log for details. -->\n");
1158
0
      }
1159
0
    }
1160
0
  }
1161
1162
1.09k
  msFree(script_url_encoded);
1163
1164
  /* done */
1165
1.09k
  msIO_printf("</ContentMetadata>\n");
1166
1167
1.09k
  return MS_SUCCESS;
1168
1.09k
}
1169
1170
/************************************************************************/
1171
/*                        msWCSGetCapabilities()                        */
1172
/************************************************************************/
1173
1174
static int msWCSGetCapabilities(mapObj *map, wcsParamsObj *params,
1175
                                cgiRequestObj *req,
1176
1.30k
                                owsRequestObj *ows_request) {
1177
1.30k
  char tmpString[OWS_VERSION_MAXLEN];
1178
1.30k
  int i, tmpInt = 0;
1179
1.30k
  int wcsSupportedVersions[] = {OWS_1_1_2, OWS_1_1_1, OWS_1_1_0, OWS_1_0_0};
1180
1.30k
  int wcsNumSupportedVersions = 4;
1181
1.30k
  const char *updatesequence = NULL;
1182
1183
  /* check version is valid */
1184
1.30k
  tmpInt = msOWSParseVersionString(params->version);
1185
1.30k
  if (tmpInt == OWS_VERSION_BADFORMAT) {
1186
1
    return msWCSException(map, "InvalidParameterValue", "version", "1.0.0 ");
1187
1
  }
1188
1189
  /* negotiate version */
1190
1.30k
  tmpInt = msOWSNegotiateVersion(tmpInt, wcsSupportedVersions,
1191
1.30k
                                 wcsNumSupportedVersions);
1192
1193
  /* set result as string and carry on */
1194
1.30k
  free(params->version);
1195
1.30k
  params->version = msStrdup(msOWSGetVersionString(tmpInt, tmpString));
1196
1197
  /* -------------------------------------------------------------------- */
1198
  /*      1.1.x is sufficiently different we have a whole case for        */
1199
  /*      it.  The remainder of this function is for 1.0.0.               */
1200
  /* -------------------------------------------------------------------- */
1201
1.30k
  if (strncmp(params->version, "1.1", 3) == 0)
1202
92
    return msWCSGetCapabilities11(map, params, req, ows_request);
1203
1204
1.21k
  updatesequence =
1205
1.21k
      msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
1206
1207
1.21k
  if (params->updatesequence != NULL) {
1208
13
    i = msOWSNegotiateUpdateSequence(params->updatesequence, updatesequence);
1209
13
    if (i == 0) { /* current */
1210
0
      msSetError(
1211
0
          MS_WCSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)",
1212
0
          "msWCSGetCapabilities()", params->updatesequence, updatesequence);
1213
0
      return msWCSException(map, "CurrentUpdateSequence", "updatesequence",
1214
0
                            params->version);
1215
0
    }
1216
13
    if (i > 0) { /* invalid */
1217
0
      msSetError(
1218
0
          MS_WCSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)",
1219
0
          "msWCSGetCapabilities()", params->updatesequence, updatesequence);
1220
0
      return msWCSException(map, "InvalidUpdateSequence", "updatesequence",
1221
0
                            params->version);
1222
0
    }
1223
13
  }
1224
1225
1.20k
  else { /* set default updatesequence */
1226
1.20k
    if (!updatesequence)
1227
1.20k
      updatesequence = "0";
1228
1.20k
    params->updatesequence = msStrdup(updatesequence);
1229
1.20k
  }
1230
1231
  /* if a bum section param is passed, throw exception */
1232
1.21k
  if (params->section &&
1233
134
      strcasecmp(params->section, "/WCS_Capabilities/Service") != 0 &&
1234
123
      strcasecmp(params->section, "/WCS_Capabilities/Capability") != 0 &&
1235
121
      strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata") != 0 &&
1236
111
      strcasecmp(params->section, "/") != 0) {
1237
110
    msSetError(MS_WCSERR, "Invalid SECTION parameter \"%s\"",
1238
110
               "msWCSGetCapabilities()", params->section);
1239
110
    return msWCSException(map, "InvalidParameterValue", "section",
1240
110
                          params->version);
1241
110
  }
1242
1243
1.10k
  else {
1244
1.10k
    msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
1245
1.10k
    msIO_sendHeaders();
1246
1247
    /* print common capability elements  */
1248
    /* TODO: DocType? */
1249
1250
1.10k
    if (!updatesequence)
1251
13
      updatesequence = "0";
1252
1253
1.10k
    msIO_printf(
1254
1.10k
        "<?xml version='1.0' encoding=\"UTF-8\" standalone=\"no\" ?>\n");
1255
1256
1.10k
    if (!params->section ||
1257
24
        (params->section && strcasecmp(params->section, "/") == 0))
1258
1.08k
      msIO_printf("<WCS_Capabilities\n"
1259
1.08k
                  "   version=\"%s\" \n"
1260
1.08k
                  "   updateSequence=\"%s\" \n"
1261
1.08k
                  "   xmlns=\"http://www.opengis.net/wcs\" \n"
1262
1.08k
                  "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1263
1.08k
                  "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1264
1.08k
                  "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1265
1.08k
                  "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
1266
1.08k
                  "%s/wcs/%s/wcsCapabilities.xsd\">\n",
1267
1.08k
                  params->version, updatesequence, msOWSGetSchemasLocation(map),
1268
1.08k
                  params->version);
1269
1270
    /* print the various capability sections */
1271
1.10k
    if (!params->section ||
1272
24
        strcasecmp(params->section, "/WCS_Capabilities/Service") == 0)
1273
1.09k
      msWCSGetCapabilities_Service(map, params);
1274
1275
1.10k
    if (!params->section ||
1276
24
        strcasecmp(params->section, "/WCS_Capabilities/Capability") == 0)
1277
1.08k
      msWCSGetCapabilities_Capability(map, params, req);
1278
1279
1.10k
    if (!params->section ||
1280
24
        strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata") == 0)
1281
1.09k
      msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req);
1282
1283
1.10k
    if (params->section && strcasecmp(params->section, "/") == 0) {
1284
1
      msWCSGetCapabilities_Service(map, params);
1285
1
      msWCSGetCapabilities_Capability(map, params, req);
1286
1
      msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req);
1287
1
    }
1288
1289
    /* done */
1290
1.10k
    if (!params->section ||
1291
24
        (params->section && strcasecmp(params->section, "/") == 0))
1292
1.08k
      msIO_printf("</WCS_Capabilities>\n");
1293
1.10k
  }
1294
1295
1.10k
  return MS_SUCCESS;
1296
1.21k
}
1297
1298
/************************************************************************/
1299
/*               msWCSDescribeCoverage_AxisDescription()                */
1300
/************************************************************************/
1301
1302
0
static int msWCSDescribeCoverage_AxisDescription(layerObj *layer, char *name) {
1303
0
  const char *value;
1304
0
  char tag[100]; /* should be plenty of space */
1305
1306
0
  msIO_printf("        <axisDescription>\n");
1307
0
  msIO_printf("          <AxisDescription");
1308
0
  snprintf(tag, sizeof(tag), "%s_semantic",
1309
0
           name); /* optional attributes follow (should escape?) */
1310
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
1311
0
                           " semantic=\"%s\"", NULL);
1312
0
  snprintf(tag, sizeof(tag), "%s_refsys", name);
1313
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
1314
0
                           " refSys=\"%s\"", NULL);
1315
0
  snprintf(tag, sizeof(tag), "%s_refsyslabel", name);
1316
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
1317
0
                           " refSysLabel=\"%s\"", NULL);
1318
0
  msIO_printf(">\n");
1319
1320
  /* TODO: add metadataLink (optional) */
1321
1322
0
  snprintf(tag, sizeof(tag), "%s_description", name);
1323
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
1324
0
                           "            <description>%s</description>\n", NULL);
1325
  /* snprintf(tag, sizeof(tag), "%s_name", name); */
1326
  /* msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_WARN, "
1327
   * <name>%s</name>\n", NULL); */
1328
0
  msIO_printf("            <name>%s</name>\n", name);
1329
1330
0
  snprintf(tag, sizeof(tag), "%s_label", name);
1331
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_WARN,
1332
0
                           "            <label>%s</label>\n", NULL);
1333
1334
  /* Values */
1335
0
  msIO_printf("            <values");
1336
0
  snprintf(tag, sizeof(tag), "%s_values_semantic",
1337
0
           name); /* optional attributes follow (should escape?) */
1338
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
1339
0
                           " semantic=\"%s\"", NULL);
1340
0
  snprintf(tag, sizeof(tag), "%s_values_type", name);
1341
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
1342
0
                           " type=\"%s\"", NULL);
1343
0
  msIO_printf(">\n");
1344
1345
  /* single values, we do not support optional type and semantic attributes */
1346
0
  snprintf(tag, sizeof(tag), "%s_values", name);
1347
0
  if (msOWSLookupMetadata(&(layer->metadata), "CO", tag))
1348
0
    msOWSPrintEncodeMetadataList(
1349
0
        stdout, &(layer->metadata), "CO", tag, NULL, NULL,
1350
0
        "              <singleValue>%s</singleValue>\n", NULL);
1351
1352
  /* intervals, only one per axis for now, we do not support optional type,
1353
   * atomic and semantic attributes */
1354
0
  snprintf(tag, sizeof(tag), "%s_interval", name);
1355
0
  if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", tag)) != NULL) {
1356
0
    int numtokens = 0;
1357
0
    char **tokens = msStringSplit(value, '/', &numtokens);
1358
0
    if (tokens && numtokens > 0) {
1359
0
      msIO_printf("            <interval>\n");
1360
0
      if (numtokens >= 1)
1361
0
        msIO_printf("            <min>%s</min>\n",
1362
0
                    tokens[0]); /* TODO: handle closure */
1363
0
      if (numtokens >= 2)
1364
0
        msIO_printf("            <max>%s</max>\n", tokens[1]);
1365
0
      if (numtokens >= 3)
1366
0
        msIO_printf("            <res>%s</res>\n", tokens[2]);
1367
0
      msIO_printf("            </interval>\n");
1368
0
    }
1369
0
    msFreeCharArray(tokens, numtokens);
1370
0
  }
1371
1372
  /* TODO: add default (optional) */
1373
1374
0
  msIO_printf("            </values>\n");
1375
1376
0
  msIO_printf("          </AxisDescription>\n");
1377
0
  msIO_printf("        </axisDescription>\n");
1378
1379
0
  return MS_SUCCESS;
1380
0
}
1381
1382
/************************************************************************/
1383
/*               msWCSDescribeCoverage_CoverageOffering()               */
1384
/************************************************************************/
1385
1386
static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer,
1387
                                                  wcsParamsObj *params,
1388
0
                                                  char *script_url_encoded) {
1389
0
  const char *value;
1390
0
  char *epsg_buf, *encoded_format;
1391
0
  coverageMetadataObj cm;
1392
0
  int i, status;
1393
1394
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
1395
0
    return MS_FAILURE;
1396
1397
0
  if (!msWCSIsLayerSupported(layer))
1398
0
    return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */
1399
1400
0
  status = msWCSGetCoverageMetadata(layer, &cm);
1401
0
  if (status != MS_SUCCESS)
1402
0
    return MS_FAILURE;
1403
1404
  /* fill in bands rangeset info, if required.  */
1405
0
  msWCSSetDefaultBandsRangeSetInfo(params, &cm, layer);
1406
1407
  /* start the Coverage section */
1408
0
  msIO_printf("  <CoverageOffering>\n");
1409
1410
0
  msWCSPrintMetadataLink(layer, script_url_encoded);
1411
1412
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "description",
1413
0
                           OWS_NOERR, "  <description>%s</description>\n",
1414
0
                           NULL);
1415
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "name", OWS_NOERR,
1416
0
                           "  <name>%s</name>\n", layer->name);
1417
1418
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "label", OWS_WARN,
1419
0
                           "  <label>%s</label>\n", NULL);
1420
1421
  /* TODO: add elevation ranges to lonLatEnvelope (optional) */
1422
0
  msIO_printf(
1423
0
      "    <lonLatEnvelope srsName=\"urn:ogc:def:crs:OGC:1.3:CRS84\">\n");
1424
0
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx,
1425
0
              cm.llextent.miny);
1426
0
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx,
1427
0
              cm.llextent.maxy);
1428
1429
0
  msOWSPrintEncodeMetadataList(
1430
0
      stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL,
1431
0
      "      <gml:timePosition>%s</gml:timePosition>\n", NULL);
1432
1433
0
  msIO_printf("    </lonLatEnvelope>\n");
1434
1435
  /* we are not supporting the optional keyword type, at least not yet */
1436
0
  msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "keywordlist",
1437
0
                               "  <keywords>\n", "  </keywords>\n",
1438
0
                               "    <keyword>%s</keyword>\n", NULL);
1439
1440
  /* DomainSet: starting simple, just a spatial domain (gml:envelope) and
1441
   * optionally a temporal domain */
1442
0
  msIO_printf("    <domainSet>\n");
1443
1444
  /* SpatialDomain */
1445
0
  msIO_printf("      <spatialDomain>\n");
1446
1447
  /* envelope in lat/lon */
1448
0
  msIO_printf("        <gml:Envelope srsName=\"EPSG:4326\">\n");
1449
0
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx,
1450
0
              cm.llextent.miny);
1451
0
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx,
1452
0
              cm.llextent.maxy);
1453
0
  msIO_printf("        </gml:Envelope>\n");
1454
1455
  /* envelope in the native srs */
1456
0
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE,
1457
0
                   &epsg_buf);
1458
0
  if (!epsg_buf) {
1459
0
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
1460
0
                     "CO", MS_TRUE, &epsg_buf);
1461
0
  }
1462
0
  if (epsg_buf) {
1463
0
    msIO_printf("        <gml:Envelope srsName=\"%s\">\n", epsg_buf);
1464
0
    msFree(epsg_buf);
1465
0
  } else {
1466
0
    msIO_printf("        <!-- NativeCRSs ERROR: missing required information, "
1467
0
                "no SRSs defined -->\n");
1468
0
  }
1469
0
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.extent.minx,
1470
0
              cm.extent.miny);
1471
0
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.extent.maxx,
1472
0
              cm.extent.maxy);
1473
0
  msIO_printf("        </gml:Envelope>\n");
1474
1475
  /* gml:rectifiedGrid */
1476
0
  msIO_printf("        <gml:RectifiedGrid dimension=\"2\">\n");
1477
0
  msIO_printf("          <gml:limits>\n");
1478
0
  msIO_printf("            <gml:GridEnvelope>\n");
1479
0
  msIO_printf("              <gml:low>0 0</gml:low>\n");
1480
0
  msIO_printf("              <gml:high>%d %d</gml:high>\n", cm.xsize - 1,
1481
0
              cm.ysize - 1);
1482
0
  msIO_printf("            </gml:GridEnvelope>\n");
1483
0
  msIO_printf("          </gml:limits>\n");
1484
0
  msIO_printf("          <gml:axisName>x</gml:axisName>\n");
1485
0
  msIO_printf("          <gml:axisName>y</gml:axisName>\n");
1486
0
  msIO_printf("          <gml:origin>\n");
1487
0
  msIO_printf("            <gml:pos>%.15g %.15g</gml:pos>\n",
1488
0
              cm.geotransform[0], cm.geotransform[3]);
1489
0
  msIO_printf("          </gml:origin>\n");
1490
0
  msIO_printf("          <gml:offsetVector>%.15g %.15g</gml:offsetVector>\n",
1491
0
              cm.geotransform[1],
1492
0
              cm.geotransform[2]); /* offset vector in X direction */
1493
0
  msIO_printf("          <gml:offsetVector>%.15g %.15g</gml:offsetVector>\n",
1494
0
              cm.geotransform[4],
1495
0
              cm.geotransform[5]); /* offset vector in Y direction */
1496
0
  msIO_printf("        </gml:RectifiedGrid>\n");
1497
1498
0
  msIO_printf("      </spatialDomain>\n");
1499
1500
0
  msWCSFreeCoverageMetadata(&cm);
1501
1502
  /* TemporalDomain */
1503
1504
  /* TODO: figure out when a temporal domain is valid, for example only tiled
1505
   * rasters support time as a domain, plus we need a timeitem */
1506
0
  if (msOWSLookupMetadata(&(layer->metadata), "CO", "timeposition") ||
1507
0
      msOWSLookupMetadata(&(layer->metadata), "CO", "timeperiod")) {
1508
0
    msIO_printf("      <temporalDomain>\n");
1509
1510
    /* TimePosition (should support a value AUTO, then we could mine positions
1511
     * from the timeitem) */
1512
0
    msOWSPrintEncodeMetadataList(
1513
0
        stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL,
1514
0
        "        <gml:timePosition>%s</gml:timePosition>\n", NULL);
1515
1516
    /* TODO:  add TimePeriod (only one per layer)  */
1517
1518
0
    msIO_printf("      </temporalDomain>\n");
1519
0
  }
1520
1521
0
  msIO_printf("    </domainSet>\n");
1522
1523
  /* rangeSet */
1524
0
  msIO_printf("    <rangeSet>\n");
1525
0
  msIO_printf(
1526
0
      "      <RangeSet>\n"); /* TODO: there are some optional attributes */
1527
1528
  /* TODO: add metadataLink (optional) */
1529
1530
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO",
1531
0
                           "rangeset_description", OWS_NOERR,
1532
0
                           "        <description>%s</description>\n", NULL);
1533
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "rangeset_name",
1534
0
                           OWS_WARN, "        <name>%s</name>\n", NULL);
1535
1536
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "rangeset_label",
1537
0
                           OWS_WARN, "        <label>%s</label>\n", NULL);
1538
1539
  /* compound range sets */
1540
0
  if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
1541
0
                                   "rangeset_axes")) != NULL) {
1542
0
    int numtokens = 0;
1543
0
    char **tokens = msStringSplit(value, ',', &numtokens);
1544
0
    if (tokens && numtokens > 0) {
1545
0
      for (i = 0; i < numtokens; i++)
1546
0
        msWCSDescribeCoverage_AxisDescription(layer, tokens[i]);
1547
0
    }
1548
0
    msFreeCharArray(tokens, numtokens);
1549
0
  }
1550
1551
0
  if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
1552
0
                                   "rangeset_nullvalue")) != NULL) {
1553
0
    msIO_printf("        <nullValues>\n");
1554
0
    msIO_printf("          <singleValue>%s</singleValue>\n", value);
1555
0
    msIO_printf("        </nullValues>\n");
1556
0
  }
1557
1558
0
  msIO_printf("      </RangeSet>\n");
1559
0
  msIO_printf("    </rangeSet>\n");
1560
1561
  /* supportedCRSs */
1562
0
  msIO_printf("    <supportedCRSs>\n");
1563
1564
  /* requestResponseCRSs: check the layer metadata/projection, and then the map
1565
   * metadata/projection if necessary (should never get to the error message) */
1566
0
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_FALSE,
1567
0
                   &epsg_buf);
1568
0
  if (!epsg_buf) {
1569
0
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
1570
0
                     "CO", MS_FALSE, &epsg_buf);
1571
0
  }
1572
0
  if (epsg_buf) {
1573
0
    int numtokens = 0;
1574
0
    char **tokens = msStringSplit(epsg_buf, ' ', &numtokens);
1575
0
    if (tokens && numtokens > 0) {
1576
0
      for (i = 0; i < numtokens; i++)
1577
0
        msIO_printf("      <requestResponseCRSs>%s</requestResponseCRSs>\n",
1578
0
                    tokens[i]);
1579
0
    }
1580
0
    msFreeCharArray(tokens, numtokens);
1581
0
    msFree(epsg_buf);
1582
0
  } else {
1583
0
    msIO_printf("      <!-- requestResponseCRSs ERROR: missing required "
1584
0
                "information, no SRSs defined -->\n");
1585
0
  }
1586
1587
  /* nativeCRSs (only one in our case) */
1588
0
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE,
1589
0
                   &epsg_buf);
1590
0
  if (!epsg_buf) {
1591
0
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
1592
0
                     "CO", MS_TRUE, &epsg_buf);
1593
0
  }
1594
0
  if (epsg_buf) {
1595
0
    msIO_printf("      <nativeCRSs>%s</nativeCRSs>\n", epsg_buf);
1596
0
    msFree(epsg_buf);
1597
0
  } else {
1598
0
    msIO_printf("      <!-- nativeCRSs ERROR: missing required information, no "
1599
0
                "SRSs defined -->\n");
1600
0
  }
1601
1602
0
  msIO_printf("    </supportedCRSs>\n");
1603
1604
  /* supportedFormats */
1605
0
  msIO_printf("    <supportedFormats");
1606
0
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "nativeformat",
1607
0
                           OWS_NOERR, " nativeFormat=\"%s\"", NULL);
1608
0
  msIO_printf(">\n");
1609
1610
0
  if ((encoded_format = msOWSGetEncodeMetadata(&(layer->metadata), "CO",
1611
0
                                               "formats", "GTiff")) != NULL) {
1612
0
    int numtokens = 0;
1613
0
    char **tokens = msStringSplit(encoded_format, ' ', &numtokens);
1614
0
    if (tokens && numtokens > 0) {
1615
0
      for (i = 0; i < numtokens; i++)
1616
0
        msIO_printf("      <formats>%s</formats>\n", tokens[i]);
1617
0
    }
1618
0
    msFreeCharArray(tokens, numtokens);
1619
0
    msFree(encoded_format);
1620
0
  }
1621
0
  msIO_printf("    </supportedFormats>\n");
1622
1623
0
  msIO_printf("    <supportedInterpolations default=\"nearest neighbor\">\n");
1624
0
  msIO_printf(
1625
0
      "      <interpolationMethod>nearest neighbor</interpolationMethod>\n");
1626
0
  msIO_printf("      <interpolationMethod>bilinear</interpolationMethod>\n");
1627
  /*  msIO_printf("      <interpolationMethod>bicubic</interpolationMethod>\n"
1628
   * ); */
1629
0
  msIO_printf("    </supportedInterpolations>\n");
1630
1631
  /* done */
1632
0
  msIO_printf("  </CoverageOffering>\n");
1633
1634
0
  return MS_SUCCESS;
1635
0
}
1636
1637
/************************************************************************/
1638
/*                       msWCSDescribeCoverage()                        */
1639
/************************************************************************/
1640
1641
static int msWCSDescribeCoverage(mapObj *map, wcsParamsObj *params,
1642
                                 owsRequestObj *ows_request,
1643
0
                                 cgiRequestObj *req) {
1644
0
  int i = 0, j = 0, k = 0;
1645
0
  const char *updatesequence = NULL;
1646
0
  char **coverages = NULL;
1647
0
  int numcoverages = 0;
1648
1649
0
  char *coverageName = NULL;
1650
0
  char *script_url_encoded = NULL;
1651
1652
  /* -------------------------------------------------------------------- */
1653
  /*      1.1.x is sufficiently different we have a whole case for        */
1654
  /*      it.  The remainder of this function is for 1.0.0.               */
1655
  /* -------------------------------------------------------------------- */
1656
0
  if (strncmp(params->version, "1.1", 3) == 0)
1657
0
    return msWCSDescribeCoverage11(map, params, ows_request);
1658
1659
  /* -------------------------------------------------------------------- */
1660
  /*      Process 1.0.0...                                                */
1661
  /* -------------------------------------------------------------------- */
1662
1663
0
  if (params->coverages) { /* use the list, but validate it first */
1664
0
    for (j = 0; params->coverages[j]; j++) {
1665
0
      coverages = msStringSplit(params->coverages[j], ',', &numcoverages);
1666
0
      for (k = 0; k < numcoverages; k++) {
1667
1668
0
        for (i = 0; i < map->numlayers; i++) {
1669
0
          coverageName =
1670
0
              msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO",
1671
0
                                     "name", GET_LAYER(map, i)->name);
1672
0
          if (coverageName != NULL && EQUAL(coverageName, coverages[k]) &&
1673
0
              (msIntegerInArray(GET_LAYER(map, i)->index,
1674
0
                                ows_request->enabled_layers,
1675
0
                                ows_request->numlayers))) {
1676
0
            msFree(coverageName);
1677
0
            break;
1678
0
          }
1679
0
          msFree(coverageName);
1680
0
        }
1681
1682
        /* i = msGetLayerIndex(map, coverages[k]); */
1683
0
        if (i == map->numlayers) { /* coverage not found */
1684
0
          msSetError(
1685
0
              MS_WCSERR,
1686
0
              "COVERAGE %s cannot be opened / does not exist. A layer might be disabled for \
1687
0
this request. Check wcs/ows_enable_request settings.",
1688
0
              "msWCSDescribeCoverage()", coverages[k]);
1689
0
          return msWCSException(map, "CoverageNotDefined", "coverage",
1690
0
                                params->version);
1691
0
        }
1692
0
      } /* next coverage */
1693
0
      msFreeCharArray(coverages, numcoverages);
1694
0
    }
1695
0
  }
1696
1697
0
  updatesequence =
1698
0
      msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
1699
0
  if (!updatesequence)
1700
0
    updatesequence = "0";
1701
1702
  /* printf("Content-Type: application/vnd.ogc.se_xml%c%c",10,10); */
1703
0
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
1704
0
  msIO_sendHeaders();
1705
1706
  /* print common capability elements  */
1707
0
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
1708
1709
  /* start the DescribeCoverage section */
1710
0
  msIO_printf("<CoverageDescription\n"
1711
0
              "   version=\"%s\" \n"
1712
0
              "   updateSequence=\"%s\" \n"
1713
0
              "   xmlns=\"http://www.opengis.net/wcs\" \n"
1714
0
              "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1715
0
              "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1716
0
              "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1717
0
              "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
1718
0
              "%s/wcs/%s/describeCoverage.xsd\">\n",
1719
0
              params->version, updatesequence, msOWSGetSchemasLocation(map),
1720
0
              params->version);
1721
1722
0
  {
1723
0
    char *pszTmp = msOWSGetOnlineResource(map, "CO", "onlineresource", req);
1724
0
    script_url_encoded = msEncodeHTMLEntities(pszTmp);
1725
0
    msFree(pszTmp);
1726
0
  }
1727
1728
0
  if (params->coverages) { /* use the list */
1729
0
    for (j = 0; params->coverages[j]; j++) {
1730
0
      coverages = msStringSplit(params->coverages[j], ',', &numcoverages);
1731
0
      for (k = 0; k < numcoverages; k++) {
1732
0
        for (i = 0; i < map->numlayers; i++) {
1733
0
          coverageName =
1734
0
              msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO",
1735
0
                                     "name", GET_LAYER(map, i)->name);
1736
0
          if (coverageName != NULL && EQUAL(coverageName, coverages[k])) {
1737
0
            msFree(coverageName);
1738
0
            break;
1739
0
          }
1740
0
          msFree(coverageName);
1741
0
        }
1742
0
        msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params,
1743
0
                                               script_url_encoded);
1744
0
      }
1745
0
      msFreeCharArray(coverages, numcoverages);
1746
0
    }
1747
0
  } else { /* return all layers */
1748
0
    for (i = 0; i < map->numlayers; i++) {
1749
0
      if (!msIntegerInArray(GET_LAYER(map, i)->index,
1750
0
                            ows_request->enabled_layers,
1751
0
                            ows_request->numlayers))
1752
0
        continue;
1753
1754
0
      msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params,
1755
0
                                             script_url_encoded);
1756
0
    }
1757
0
  }
1758
1759
0
  msFree(script_url_encoded);
1760
1761
  /* done */
1762
0
  msIO_printf("</CoverageDescription>\n");
1763
1764
0
  return MS_SUCCESS;
1765
0
}
1766
1767
/************************************************************************/
1768
/*                       msWCSGetCoverageBands10()                      */
1769
/************************************************************************/
1770
1771
static int msWCSGetCoverageBands10(mapObj *map, cgiRequestObj *request,
1772
                                   wcsParamsObj *params, layerObj *lp,
1773
                                   char **p_bandlist)
1774
1775
0
{
1776
0
  const char *value = NULL;
1777
0
  int i;
1778
1779
  /* Are there any non-spatio/temporal ranges to do subsetting on (e.g. bands)
1780
   */
1781
0
  value = msOWSLookupMetadata(
1782
0
      &(lp->metadata), "CO",
1783
0
      "rangeset_axes"); /* this will get all the compound range sets */
1784
0
  if (value) {
1785
0
    char **tokens;
1786
0
    int numtokens;
1787
0
    char tag[100];
1788
0
    const char *rangeitem;
1789
1790
0
    tokens = msStringSplit(value, ',', &numtokens);
1791
1792
0
    for (i = 0; i < numtokens; i++) {
1793
0
      if ((value = msWCSGetRequestParameter(request, tokens[i])) == NULL)
1794
0
        continue; /* next rangeset parameter */
1795
1796
      /* ok, a parameter has been passed which matches a token in
1797
       * wcs_rangeset_axes */
1798
0
      if (msWCSValidateRangeSetParam(lp, tokens[i], value) != MS_SUCCESS) {
1799
0
        int ret;
1800
0
        msSetError(MS_WCSERR, "Error specifying \"%s\" parameter value(s).",
1801
0
                   "msWCSGetCoverage()", tokens[i]);
1802
0
        ret = msWCSException(map, "InvalidParameterValue", tokens[i],
1803
0
                             params->version);
1804
0
        msFreeCharArray(tokens, numtokens);
1805
0
        return ret;
1806
0
      }
1807
1808
      /* xxxxx_rangeitem tells us how to subset */
1809
0
      snprintf(tag, sizeof(tag), "%s_rangeitem", tokens[i]);
1810
0
      if ((rangeitem = msOWSLookupMetadata(&(lp->metadata), "CO", tag)) ==
1811
0
          NULL) {
1812
0
        msSetError(MS_WCSERR,
1813
0
                   "Missing required metadata element \"%s\", unable to "
1814
0
                   "process %s=%s.",
1815
0
                   "msWCSGetCoverage()", tag, tokens[i], value);
1816
0
        msFreeCharArray(tokens, numtokens);
1817
0
        return msWCSException(map, NULL, NULL, params->version);
1818
0
      }
1819
1820
0
      if (strcasecmp(rangeitem, "_bands") ==
1821
0
          0) { /* special case, subset bands */
1822
0
        *p_bandlist = msWCSConvertRangeSetToString(value);
1823
1824
0
        if (!*p_bandlist) {
1825
0
          msSetError(MS_WCSERR, "Error specifying \"%s\" parameter value(s).",
1826
0
                     "msWCSGetCoverage()", tokens[i]);
1827
0
          msFreeCharArray(tokens, numtokens);
1828
0
          return msWCSException(map, NULL, NULL, params->version);
1829
0
        }
1830
0
      } else if (strcasecmp(rangeitem, "_pixels") ==
1831
0
                 0) { /* special case, subset pixels */
1832
0
        msFreeCharArray(tokens, numtokens);
1833
0
        msSetError(
1834
0
            MS_WCSERR,
1835
0
            "Arbitrary range sets based on pixel values are not yet supported.",
1836
0
            "msWCSGetCoverage()");
1837
0
        return msWCSException(map, NULL, NULL, params->version);
1838
0
      } else {
1839
0
        msFreeCharArray(tokens, numtokens);
1840
0
        msSetError(MS_WCSERR,
1841
0
                   "Arbitrary range sets based on tile (i.e. image) attributes "
1842
0
                   "are not yet supported.",
1843
0
                   "msWCSGetCoverage()");
1844
0
        return msWCSException(map, NULL, NULL, params->version);
1845
0
      }
1846
0
    }
1847
    /* clean-up */
1848
0
    msFreeCharArray(tokens, numtokens);
1849
0
  }
1850
1851
0
  return MS_SUCCESS;
1852
0
}
1853
1854
/************************************************************************/
1855
/*                   msWCSGetCoverage_ImageCRSSetup()                   */
1856
/*                                                                      */
1857
/*      The request was in imageCRS - update the map projection to      */
1858
/*      map the native projection of the layer, and reset the           */
1859
/*      bounding box to match the projected bounds corresponding to     */
1860
/*      the imageCRS request.                                           */
1861
/************************************************************************/
1862
1863
static int msWCSGetCoverage_ImageCRSSetup(mapObj *map, wcsParamsObj *params,
1864
                                          coverageMetadataObj *cm,
1865
                                          layerObj *layer)
1866
1867
0
{
1868
  /* -------------------------------------------------------------------- */
1869
  /*      Load map with the layer (coverage) coordinate system.  We       */
1870
  /*      really need a set projectionObj from projectionObj function!    */
1871
  /* -------------------------------------------------------------------- */
1872
0
  char *layer_proj = msGetProjectionString(&(layer->projection));
1873
1874
0
  if (msLoadProjectionString(&(map->projection), layer_proj) != 0) {
1875
0
    msFree(layer_proj);
1876
0
    return msWCSException(map, NULL, NULL, params->version);
1877
0
  }
1878
1879
0
  free(layer_proj);
1880
0
  layer_proj = NULL;
1881
1882
  /* -------------------------------------------------------------------- */
1883
  /*      Reset bounding box.                                             */
1884
  /* -------------------------------------------------------------------- */
1885
0
  if (params->bbox.maxx != params->bbox.minx) {
1886
0
    rectObj orig_bbox = params->bbox;
1887
1888
0
    params->bbox.minx = cm->geotransform[0] +
1889
0
                        orig_bbox.minx * cm->geotransform[1] +
1890
0
                        orig_bbox.miny * cm->geotransform[2];
1891
0
    params->bbox.maxy = cm->geotransform[3] +
1892
0
                        orig_bbox.minx * cm->geotransform[4] +
1893
0
                        orig_bbox.miny * cm->geotransform[5];
1894
0
    params->bbox.maxx = cm->geotransform[0] +
1895
0
                        (orig_bbox.maxx + 1) * cm->geotransform[1] +
1896
0
                        (orig_bbox.maxy + 1) * cm->geotransform[2];
1897
0
    params->bbox.miny = cm->geotransform[3] +
1898
0
                        (orig_bbox.maxx + 1) * cm->geotransform[4] +
1899
0
                        (orig_bbox.maxy + 1) * cm->geotransform[5];
1900
1901
    /* WCS 1.1 boundbox is center of pixel oriented. */
1902
0
    if (strncasecmp(params->version, "1.1", 3) == 0) {
1903
0
      params->bbox.minx += cm->geotransform[1] / 2 + cm->geotransform[2] / 2;
1904
0
      params->bbox.maxx -= cm->geotransform[1] / 2 + cm->geotransform[2] / 2;
1905
0
      params->bbox.maxy += cm->geotransform[4] / 2 + cm->geotransform[5] / 2;
1906
0
      params->bbox.miny -= cm->geotransform[4] / 2 + cm->geotransform[5] / 2;
1907
0
    }
1908
0
  }
1909
1910
  /* -------------------------------------------------------------------- */
1911
  /*      Reset resolution.                                               */
1912
  /* -------------------------------------------------------------------- */
1913
0
  if (params->resx != 0.0) {
1914
0
    params->resx = cm->geotransform[1] * params->resx;
1915
0
    params->resy = fabs(cm->geotransform[5] * params->resy);
1916
0
  }
1917
1918
0
  return MS_SUCCESS;
1919
0
}
1920
1921
/************************************************************************/
1922
/*                    msWCSApplyLayerCreationOptions()                  */
1923
/************************************************************************/
1924
1925
void msWCSApplyLayerCreationOptions(layerObj *lp, outputFormatObj *format,
1926
                                    const char *bandlist)
1927
1928
0
{
1929
0
  const char *pszKey;
1930
0
  char szKeyBeginning[256];
1931
0
  size_t nKeyBeginningLength;
1932
0
  int nBands = 0;
1933
0
  char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
1934
1935
0
  snprintf(szKeyBeginning, sizeof(szKeyBeginning),
1936
0
           "wcs_outputformat_%s_creationoption_", format->name);
1937
0
  nKeyBeginningLength = strlen(szKeyBeginning);
1938
1939
0
  pszKey = msFirstKeyFromHashTable(&(lp->metadata));
1940
0
  for (; pszKey != NULL;
1941
0
       pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
1942
0
    if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
1943
0
      const char *pszValue = msLookupHashTable(&(lp->metadata), pszKey);
1944
0
      const char *pszGDALKey = pszKey + nKeyBeginningLength;
1945
0
      if (EQUALN(pszGDALKey, "BAND_", strlen("BAND_"))) {
1946
        /* Remap BAND specific creation option to the real output
1947
         * band number, given the band subset of the request */
1948
0
        int nKeyOriBandNumber = atoi(pszGDALKey + strlen("BAND_"));
1949
0
        int nTargetBandNumber = -1;
1950
0
        int i;
1951
0
        for (i = 0; i < nBands; i++) {
1952
0
          if (nKeyOriBandNumber == atoi(papszBandNumbers[i])) {
1953
0
            nTargetBandNumber = i + 1;
1954
0
            break;
1955
0
          }
1956
0
        }
1957
0
        if (nTargetBandNumber > 0) {
1958
0
          char szModKey[256];
1959
0
          const char *pszAfterBand = strchr(pszGDALKey + strlen("BAND_"), '_');
1960
0
          if (pszAfterBand != NULL) {
1961
0
            snprintf(szModKey, sizeof(szModKey), "BAND_%d%s", nTargetBandNumber,
1962
0
                     pszAfterBand);
1963
0
            if (lp->debug >= MS_DEBUGLEVEL_VVV) {
1964
0
              msDebug("Setting GDAL %s=%s creation option\n", szModKey,
1965
0
                      pszValue);
1966
0
            }
1967
0
            msSetOutputFormatOption(format, szModKey, pszValue);
1968
0
          }
1969
0
        }
1970
0
      } else {
1971
0
        if (lp->debug >= MS_DEBUGLEVEL_VVV) {
1972
0
          msDebug("Setting GDAL %s=%s creation option\n", pszGDALKey, pszValue);
1973
0
        }
1974
0
        msSetOutputFormatOption(format, pszGDALKey, pszValue);
1975
0
      }
1976
0
    }
1977
0
  }
1978
1979
0
  msFreeCharArray(papszBandNumbers, nBands);
1980
0
}
1981
1982
/************************************************************************/
1983
/*               msWCSApplyDatasetMetadataAsCreationOptions()           */
1984
/************************************************************************/
1985
1986
void msWCSApplyDatasetMetadataAsCreationOptions(layerObj *lp,
1987
                                                outputFormatObj *format,
1988
                                                const char *bandlist,
1989
0
                                                void *hDSIn) {
1990
  /* Requires GDAL 2.3 in practice. */
1991
  /* Automatic forwarding of input dataset metadata if it is GRIB and the */
1992
  /* output is GRIB as well, and wcs_outputformat_GRIB_creationoption* are */
1993
  /* not defined. */
1994
0
  GDALDatasetH hDS = (GDALDatasetH)hDSIn;
1995
0
  if (hDS && GDALGetDatasetDriver(hDS) &&
1996
0
      EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hDS)), "GRIB") &&
1997
0
      EQUAL(format->driver, "GDAL/GRIB")) {
1998
0
    const char *pszKey;
1999
0
    char szKeyBeginning[256];
2000
0
    size_t nKeyBeginningLength;
2001
0
    int bWCSMetadataFound = MS_FALSE;
2002
2003
0
    snprintf(szKeyBeginning, sizeof(szKeyBeginning),
2004
0
             "wcs_outputformat_%s_creationoption_", format->name);
2005
0
    nKeyBeginningLength = strlen(szKeyBeginning);
2006
2007
0
    for (pszKey = msFirstKeyFromHashTable(&(lp->metadata)); pszKey != NULL;
2008
0
         pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
2009
0
      if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
2010
0
        bWCSMetadataFound = MS_TRUE;
2011
0
        break;
2012
0
      }
2013
0
    }
2014
0
    if (!bWCSMetadataFound) {
2015
0
      int nBands = 0;
2016
0
      char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
2017
0
      int i;
2018
0
      for (i = 0; i < nBands; i++) {
2019
0
        int nSrcBand = atoi(papszBandNumbers[i]);
2020
0
        int nDstBand = i + 1;
2021
0
        GDALRasterBandH hBand = GDALGetRasterBand(hDS, nSrcBand);
2022
0
        if (hBand) {
2023
0
          CSLConstList papszMD = GDALGetMetadata(hBand, NULL);
2024
0
          const char *pszMDI = CSLFetchNameValue(papszMD, "GRIB_IDS");
2025
          // Make sure it is a GRIB2 band
2026
0
          if (pszMDI) {
2027
0
            char szKey[256];
2028
0
            snprintf(szKey, sizeof(szKey), "BAND_%d_IDS", nDstBand);
2029
0
            msSetOutputFormatOption(format, szKey, pszMDI);
2030
2031
0
            snprintf(szKey, sizeof(szKey), "BAND_%d_DISCIPLINE", nDstBand);
2032
0
            msSetOutputFormatOption(format, szKey,
2033
0
                                    CSLFetchNameValue(papszMD, "DISCIPLINE"));
2034
2035
0
            snprintf(szKey, sizeof(szKey), "BAND_%d_PDS_PDTN", nDstBand);
2036
0
            msSetOutputFormatOption(
2037
0
                format, szKey, CSLFetchNameValue(papszMD, "GRIB_PDS_PDTN"));
2038
2039
0
            snprintf(szKey, sizeof(szKey), "BAND_%d_PDS_TEMPLATE_NUMBERS",
2040
0
                     nDstBand);
2041
0
            msSetOutputFormatOption(
2042
0
                format, szKey,
2043
0
                CSLFetchNameValue(papszMD, "GRIB_PDS_TEMPLATE_NUMBERS"));
2044
0
          }
2045
0
        }
2046
0
      }
2047
0
      msFreeCharArray(papszBandNumbers, nBands);
2048
0
    }
2049
0
  }
2050
0
}
2051
2052
/************************************************************************/
2053
/*                  msWCSApplyLayerMetadataItemOptions()                */
2054
/************************************************************************/
2055
2056
void msWCSApplyLayerMetadataItemOptions(layerObj *lp, outputFormatObj *format,
2057
                                        const char *bandlist)
2058
2059
0
{
2060
0
  if (!STARTS_WITH(format->driver, "GDAL/"))
2061
0
    return;
2062
2063
0
  const bool bIsNetCDFOutput = EQUAL(format->driver, "GDAL/netCDF");
2064
0
  const char *pszKey;
2065
0
  char szKeyBeginning[256];
2066
0
  size_t nKeyBeginningLength;
2067
0
  int nBands = 0;
2068
0
  char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
2069
2070
0
  snprintf(szKeyBeginning, sizeof(szKeyBeginning), "wcs_outputformat_%s_mdi_",
2071
0
           format->name);
2072
0
  nKeyBeginningLength = strlen(szKeyBeginning);
2073
2074
  // Transform wcs_outputformat_{formatname}_mdi_{key} to mdi_{key}
2075
  // and Transform wcs_outputformat_{formatname}_mdi_BAND_X_{key} to
2076
  // mdi_BAND_Y_{key} MDI stands for MetaDataItem
2077
2078
  // For netCDF 3D output
2079
0
  std::map<int, std::string> oMapExtraDimValues;
2080
0
  std::string osExtraDimName;
2081
0
  bool bExtraDimValid = false;
2082
2083
0
  pszKey = msFirstKeyFromHashTable(&(lp->metadata));
2084
0
  for (; pszKey != NULL;
2085
0
       pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
2086
0
    if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
2087
0
      const char *pszValue = msLookupHashTable(&(lp->metadata), pszKey);
2088
0
      const char *pszGDALKey = pszKey + nKeyBeginningLength;
2089
2090
0
      if (EQUALN(pszGDALKey, "BAND_", strlen("BAND_"))) {
2091
        /* Remap BAND specific creation option to the real output
2092
         * band number, given the band subset of the request */
2093
0
        int nKeyOriBandNumber = atoi(pszGDALKey + strlen("BAND_"));
2094
0
        int nTargetBandNumber = -1;
2095
0
        int i;
2096
0
        for (i = 0; i < nBands; i++) {
2097
0
          if (nKeyOriBandNumber == atoi(papszBandNumbers[i])) {
2098
0
            nTargetBandNumber = i + 1;
2099
0
            break;
2100
0
          }
2101
0
        }
2102
0
        if (nTargetBandNumber > 0) {
2103
0
          char szModKey[256];
2104
0
          const char *pszAfterBand = strchr(pszGDALKey + strlen("BAND_"), '_');
2105
0
          if (pszAfterBand != NULL) {
2106
            // Special case to generate 3D netCDF files
2107
0
            if (bIsNetCDFOutput &&
2108
0
                strncmp(pszAfterBand, "_default_NETCDF_DIM_",
2109
0
                        strlen("_default_NETCDF_DIM_")) == 0) {
2110
0
              const char *pszDimName =
2111
0
                  pszAfterBand + strlen("_default_NETCDF_DIM_");
2112
0
              if (osExtraDimName.empty()) {
2113
0
                bExtraDimValid = true;
2114
0
                osExtraDimName = pszDimName;
2115
0
                oMapExtraDimValues[nTargetBandNumber] = pszValue;
2116
0
              } else if (bExtraDimValid) {
2117
0
                if (osExtraDimName != pszDimName) {
2118
0
                  msDebug("One band has several %sdefault_NETCDF_DIM_ metadata "
2119
0
                          "items, "
2120
0
                          "or different bands have a different value. "
2121
0
                          "Only a single extra dimension is supported.",
2122
0
                          szKeyBeginning);
2123
0
                  bExtraDimValid = false;
2124
0
                } else {
2125
0
                  oMapExtraDimValues[nTargetBandNumber] = pszValue;
2126
0
                }
2127
0
              }
2128
0
            } else {
2129
0
              snprintf(szModKey, sizeof(szModKey), "mdi_BAND_%d%s",
2130
0
                       nTargetBandNumber, pszAfterBand);
2131
0
              if (lp->debug >= MS_DEBUGLEVEL_VVV) {
2132
0
                msDebug("Setting GDAL %s=%s metadata item option\n", szModKey,
2133
0
                        pszValue);
2134
0
              }
2135
0
              msSetOutputFormatOption(format, szModKey, pszValue);
2136
0
            }
2137
0
          }
2138
0
        }
2139
0
      } else {
2140
0
        char szModKey[256];
2141
0
        snprintf(szModKey, sizeof(szModKey), "mdi_%s", pszGDALKey);
2142
0
        if (lp->debug >= MS_DEBUGLEVEL_VVV) {
2143
0
          msDebug("Setting GDAL %s=%s metadata item option\n", szModKey,
2144
0
                  pszValue);
2145
0
        }
2146
0
        msSetOutputFormatOption(format, szModKey, pszValue);
2147
0
      }
2148
0
    }
2149
0
  }
2150
2151
  // netCDF 3D output
2152
  // Tested in msautotest/wxs/wcs_netcdf_3d_output.map
2153
0
  std::string osExtraDimDataType;
2154
0
  if (bExtraDimValid && static_cast<int>(oMapExtraDimValues.size()) != nBands) {
2155
0
    msDebug("One of the band lack a NETCDF_DIM_%s metadata item.",
2156
0
            osExtraDimName.c_str());
2157
0
    bExtraDimValid = false;
2158
0
  }
2159
0
  if (bExtraDimValid) {
2160
0
    for (const auto &keyValue : oMapExtraDimValues) {
2161
0
      const auto &osVal = keyValue.second;
2162
0
      const CPLValueType eType = CPLGetValueType(osVal.c_str());
2163
0
      if (eType == CPL_VALUE_STRING) {
2164
0
        osExtraDimDataType = "string";
2165
0
        break;
2166
0
      } else if (eType == CPL_VALUE_INTEGER) {
2167
0
        if (osExtraDimDataType.empty() || osExtraDimDataType == "integer") {
2168
0
          osExtraDimDataType = "integer";
2169
0
          const GIntBig nVal = CPLAtoGIntBig(osVal.c_str());
2170
0
          if (nVal > INT_MAX || nVal < INT_MIN) {
2171
0
            osExtraDimDataType = "integer64";
2172
0
          }
2173
0
        }
2174
0
      } else {
2175
0
        osExtraDimDataType = "double";
2176
0
      }
2177
0
    }
2178
0
    if (osExtraDimDataType == "string") {
2179
0
      msDebug("One of the value for the NETCDF_DIM_%s metadata is a string. "
2180
0
              "This is not supported",
2181
0
              osExtraDimName.c_str());
2182
0
      bExtraDimValid = false;
2183
0
    }
2184
0
  }
2185
0
  if (bExtraDimValid) {
2186
    // Cf
2187
    // https://gdal.org/drivers/raster/netcdf.html#creation-of-multidimensional-files-with-createcopy-2d-raster-api
2188
2189
    // Define the name of the extra dimensions
2190
0
    {
2191
0
      std::string osValue;
2192
0
      osValue = '{';
2193
0
      osValue += osExtraDimName;
2194
0
      osValue += '}';
2195
2196
0
      msSetOutputFormatOption(format, "mdi_default_NETCDF_DIM_EXTRA",
2197
0
                              osValue.c_str());
2198
0
    }
2199
2200
    // Define the size (in number of samples) and type of the extra dimension
2201
0
    {
2202
0
      std::string osKey;
2203
0
      osKey = "mdi_default_NETCDF_DIM_";
2204
0
      osKey += osExtraDimName;
2205
0
      osKey += "_DEF";
2206
2207
0
      std::string osValue;
2208
0
      osValue = '{';
2209
0
      osValue += CPLSPrintf("%d", nBands);
2210
0
      osValue += ',';
2211
0
      if (osExtraDimDataType == "integer")
2212
0
        osValue += '4';
2213
0
      else if (osExtraDimDataType == "integer64")
2214
0
        osValue += "10";
2215
0
      else /* if ( osExtraDimDataType == "double" ) */
2216
0
        osValue += '6';
2217
0
      osValue += '}';
2218
2219
0
      msSetOutputFormatOption(format, osKey.c_str(), osValue.c_str());
2220
0
    }
2221
2222
    // Define the values along the extra dimension
2223
0
    {
2224
0
      std::string osKey;
2225
0
      osKey = "mdi_default_NETCDF_DIM_";
2226
0
      osKey += osExtraDimName;
2227
0
      osKey += "_VALUES";
2228
0
      std::string osValue;
2229
0
      osValue = '{';
2230
0
      bool bFirstVal = true;
2231
0
      for (const auto &keyValue : oMapExtraDimValues) {
2232
0
        const auto &osVal = keyValue.second;
2233
0
        if (!bFirstVal)
2234
0
          osValue += ',';
2235
0
        else
2236
0
          bFirstVal = false;
2237
0
        osValue += osVal;
2238
0
      }
2239
0
      osValue += '}';
2240
2241
0
      msSetOutputFormatOption(format, osKey.c_str(), osValue.c_str());
2242
0
    }
2243
0
  }
2244
2245
0
  msFreeCharArray(papszBandNumbers, nBands);
2246
0
}
2247
2248
/************************************************************************/
2249
/*               msWCSApplySourceDatasetMetadata()                      */
2250
/************************************************************************/
2251
2252
void msWCSApplySourceDatasetMetadata(layerObj *lp, outputFormatObj *format,
2253
0
                                     const char *bandlist, void *hDSIn) {
2254
  /* Automatic forwarding of input dataset metadata if it is netCDF and the */
2255
  /* output is netCDF as well, and wcs_outputformat_netCDF_mdi* are */
2256
  /* not defined. */
2257
0
  GDALDatasetH hDS = (GDALDatasetH)hDSIn;
2258
0
  if (hDS && GDALGetDatasetDriver(hDS) &&
2259
0
      EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hDS)), "netCDF") &&
2260
0
      EQUAL(format->driver, "GDAL/netCDF")) {
2261
0
    const char *pszKey;
2262
0
    char szKeyBeginning[256];
2263
0
    size_t nKeyBeginningLength;
2264
0
    int bWCSMetadataFound = MS_FALSE;
2265
2266
0
    snprintf(szKeyBeginning, sizeof(szKeyBeginning), "wcs_outputformat_%s_mdi_",
2267
0
             format->name);
2268
0
    nKeyBeginningLength = strlen(szKeyBeginning);
2269
2270
0
    for (pszKey = msFirstKeyFromHashTable(&(lp->metadata)); pszKey != NULL;
2271
0
         pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
2272
0
      if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
2273
0
        bWCSMetadataFound = MS_TRUE;
2274
0
        break;
2275
0
      }
2276
0
    }
2277
0
    if (!bWCSMetadataFound) {
2278
0
      int nBands = 0;
2279
0
      char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
2280
2281
0
      std::string osExtraDimName;
2282
      // Special processing if the input dataset is a 3D one
2283
0
      {
2284
        // Check if extra dimensions are declared on the source dataset,
2285
        // and if so, if there's just a single one.
2286
0
        const char *pszDimExtraWithCurl =
2287
0
            GDALGetMetadataItem(hDS, "NETCDF_DIM_EXTRA", nullptr);
2288
0
        if (pszDimExtraWithCurl &&
2289
0
            strchr(pszDimExtraWithCurl, ',') == nullptr &&
2290
0
            pszDimExtraWithCurl[0] == '{' &&
2291
0
            pszDimExtraWithCurl[strlen(pszDimExtraWithCurl) - 1] == '}') {
2292
0
          osExtraDimName.append(pszDimExtraWithCurl + 1,
2293
0
                                strlen(pszDimExtraWithCurl) - 2);
2294
2295
          // Declare the extra dimension name
2296
0
          msSetOutputFormatOption(format, "mdi_default_NETCDF_DIM_EXTRA",
2297
0
                                  pszDimExtraWithCurl);
2298
2299
          // Declare the extra dimension definition: size + data type
2300
0
          const char *pszDimExtraDef = GDALGetMetadataItem(
2301
0
              hDS, ("NETCDF_DIM_" + osExtraDimName + "_DEF").c_str(), nullptr);
2302
0
          if (pszDimExtraDef && pszDimExtraDef[0] == '{' &&
2303
0
              pszDimExtraDef[strlen(pszDimExtraDef) - 1] == '}') {
2304
0
            const auto tokens = msStringSplit(
2305
0
                std::string(pszDimExtraDef + 1, strlen(pszDimExtraDef) - 2)
2306
0
                    .c_str(),
2307
0
                ',');
2308
0
            if (tokens.size() == 2) {
2309
0
              const auto &varType = tokens[1];
2310
0
              msSetOutputFormatOption(
2311
0
                  format,
2312
0
                  ("mdi_default_NETCDF_DIM_" + osExtraDimName + "_DEF").c_str(),
2313
0
                  (std::string("{") + CPLSPrintf("%d", nBands) + ',' + varType +
2314
0
                   '}')
2315
0
                      .c_str());
2316
0
            }
2317
0
          }
2318
2319
          // Declare the extra dimension values
2320
0
          const char *pszDimExtraValues = GDALGetMetadataItem(
2321
0
              hDS, ("NETCDF_DIM_" + osExtraDimName + "_VALUES").c_str(),
2322
0
              nullptr);
2323
0
          if (pszDimExtraValues && pszDimExtraValues[0] == '{' &&
2324
0
              pszDimExtraValues[strlen(pszDimExtraValues) - 1] == '}') {
2325
0
            const auto tokens =
2326
0
                msStringSplit(std::string(pszDimExtraValues + 1,
2327
0
                                          strlen(pszDimExtraValues) - 2)
2328
0
                                  .c_str(),
2329
0
                              ',');
2330
0
            if (static_cast<int>(tokens.size()) == GDALGetRasterCount(hDS)) {
2331
0
              std::string osValue = "{";
2332
0
              for (int i = 0; i < nBands; i++) {
2333
0
                int nSrcBand = atoi(papszBandNumbers[i]);
2334
0
                assert(nSrcBand >= 1 &&
2335
0
                       nSrcBand <= static_cast<int>(tokens.size()));
2336
0
                if (i > 0)
2337
0
                  osValue += ',';
2338
0
                osValue += tokens[nSrcBand - 1];
2339
0
              }
2340
0
              osValue += '}';
2341
2342
0
              msSetOutputFormatOption(
2343
0
                  format,
2344
0
                  ("mdi_default_NETCDF_DIM_" + osExtraDimName + "_VALUES")
2345
0
                      .c_str(),
2346
0
                  osValue.c_str());
2347
0
            }
2348
0
          } else if (pszDimExtraValues) {
2349
            // If there's a single value
2350
0
            msSetOutputFormatOption(
2351
0
                format,
2352
0
                ("mdi_default_NETCDF_DIM_" + osExtraDimName + "_VALUES")
2353
0
                    .c_str(),
2354
0
                pszDimExtraValues);
2355
0
          }
2356
0
        }
2357
0
      }
2358
2359
0
      {
2360
0
        CSLConstList papszMD = GDALGetMetadata(hDS, NULL);
2361
0
        if (papszMD) {
2362
0
          for (CSLConstList papszIter = papszMD; *papszIter; ++papszIter) {
2363
            // Copy netCDF global attributes, as well as the ones
2364
            // of the extra dimension for 3D netCDF files
2365
0
            if (STARTS_WITH(*papszIter, "NC_GLOBAL#") ||
2366
0
                (!osExtraDimName.empty() &&
2367
0
                 STARTS_WITH(*papszIter, osExtraDimName.c_str()) &&
2368
0
                 (*papszIter)[osExtraDimName.size()] == '#')) {
2369
0
              char *pszKey = nullptr;
2370
0
              const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
2371
0
              if (pszKey && pszValue) {
2372
0
                char szKey[256];
2373
0
                snprintf(szKey, sizeof(szKey), "mdi_default_%s", pszKey);
2374
0
                msSetOutputFormatOption(format, szKey, pszValue);
2375
0
              }
2376
0
              CPLFree(pszKey);
2377
0
            }
2378
0
          }
2379
0
        }
2380
0
      }
2381
2382
0
      for (int i = 0; i < nBands; i++) {
2383
0
        int nSrcBand = atoi(papszBandNumbers[i]);
2384
0
        int nDstBand = i + 1;
2385
0
        GDALRasterBandH hBand = GDALGetRasterBand(hDS, nSrcBand);
2386
0
        if (hBand) {
2387
0
          CSLConstList papszMD = GDALGetMetadata(hBand, NULL);
2388
0
          if (papszMD) {
2389
0
            for (CSLConstList papszIter = papszMD; *papszIter; ++papszIter) {
2390
0
              char *pszKey = nullptr;
2391
0
              const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
2392
0
              if (pszKey && pszValue && !EQUAL(pszKey, "grid_name") &&
2393
0
                  !EQUAL(pszKey, "grid_mapping")) {
2394
0
                char szKey[256];
2395
0
                snprintf(szKey, sizeof(szKey), "mdi_BAND_%d_default_%s",
2396
0
                         nDstBand, pszKey);
2397
0
                msSetOutputFormatOption(format, szKey, pszValue);
2398
0
              }
2399
0
              CPLFree(pszKey);
2400
0
            }
2401
0
          }
2402
0
        }
2403
0
      }
2404
0
      msFreeCharArray(papszBandNumbers, nBands);
2405
0
    }
2406
0
  }
2407
0
}
2408
2409
/************************************************************************/
2410
/*                          msWCSGetCoverage()                          */
2411
/************************************************************************/
2412
2413
static int msWCSGetCoverage(mapObj *map, cgiRequestObj *request,
2414
0
                            wcsParamsObj *params, owsRequestObj *ows_request) {
2415
0
  imageObj *image;
2416
0
  layerObj *lp;
2417
0
  int status, i;
2418
0
  const char *value;
2419
0
  outputFormatObj *format;
2420
0
  char *bandlist = NULL;
2421
0
  size_t bufferSize = 0;
2422
0
  char numbands[12]; /* should be large enough to hold the number of bands in
2423
                        the bandlist */
2424
0
  coverageMetadataObj cm;
2425
0
  rectObj reqextent;
2426
0
  rectObj covextent;
2427
0
  rasterBufferObj rb;
2428
0
  int doDrawRasterLayerDraw = MS_TRUE;
2429
0
  GDALDatasetH hDS = NULL;
2430
2431
0
  char *coverageName;
2432
2433
  /* make sure all required parameters are available (at least the easy ones) */
2434
0
  if (!params->crs) {
2435
0
    msSetError(MS_WCSERR, "Required parameter CRS was not supplied.",
2436
0
               "msWCSGetCoverage()");
2437
0
    return msWCSException(map, "MissingParameterValue", "crs", params->version);
2438
0
  }
2439
2440
0
  if (!params->time && !params->bbox.minx && !params->bbox.miny &&
2441
0
      !params->bbox.maxx && !params->bbox.maxy) {
2442
0
    msSetError(MS_WCSERR, "One of BBOX or TIME is required",
2443
0
               "msWCSGetCoverage()");
2444
0
    return msWCSException(map, "MissingParameterValue", "bbox/time",
2445
0
                          params->version);
2446
0
  }
2447
2448
0
  if (params->coverages == NULL || params->coverages[0] == NULL) {
2449
0
    msSetError(MS_WCSERR, "Required parameter COVERAGE was not supplied.",
2450
0
               "msWCSGetCoverage()");
2451
0
    return msWCSException(map, "MissingParameterValue", "coverage",
2452
0
                          params->version);
2453
0
  }
2454
2455
  /* For WCS 1.1, we need to normalize the axis order of the BBOX and
2456
     resolution values some coordinate systems (eg. EPSG geographic) */
2457
0
  if (strncasecmp(params->version, "1.0", 3) != 0 && params->crs != NULL &&
2458
0
      strncasecmp(params->crs, "urn:", 4) == 0) {
2459
0
    projectionObj proj;
2460
2461
0
    msInitProjection(&proj);
2462
0
    msProjectionInheritContextFrom(&proj, &(map->projection));
2463
0
    if (msLoadProjectionString(&proj, (char *)params->crs) == 0) {
2464
0
      msAxisNormalizePoints(&proj, 1, &(params->bbox.minx),
2465
0
                            &(params->bbox.miny));
2466
0
      msAxisNormalizePoints(&proj, 1, &(params->bbox.maxx),
2467
0
                            &(params->bbox.maxy));
2468
0
      msAxisNormalizePoints(&proj, 1, &(params->resx), &(params->resy));
2469
0
      msAxisNormalizePoints(&proj, 1, &(params->originx), &(params->originy));
2470
0
    } else
2471
0
      msResetErrorList();
2472
0
    msFreeProjection(&proj);
2473
0
  }
2474
2475
  /* find the layer we are working with */
2476
0
  lp = NULL;
2477
0
  for (i = 0; i < map->numlayers; i++) {
2478
0
    coverageName = msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO",
2479
0
                                          "name", GET_LAYER(map, i)->name);
2480
0
    if (coverageName != NULL && EQUAL(coverageName, params->coverages[0]) &&
2481
0
        (msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers,
2482
0
                          ows_request->numlayers))) {
2483
0
      lp = GET_LAYER(map, i);
2484
0
      free(coverageName);
2485
0
      break;
2486
0
    }
2487
0
    free(coverageName);
2488
0
  }
2489
2490
0
  if (lp == NULL) {
2491
0
    msSetError(
2492
0
        MS_WCSERR,
2493
0
        "COVERAGE=%s not found, not in supported layer list. A layer might be disabled for \
2494
0
this request. Check wcs/ows_enable_request settings.",
2495
0
        "msWCSGetCoverage()", params->coverages[0]);
2496
0
    return msWCSException(map, "InvalidParameterValue", "coverage",
2497
0
                          params->version);
2498
0
  }
2499
2500
  /* make sure the layer is on */
2501
0
  lp->status = MS_ON;
2502
2503
  /* If the layer has no projection set, set it to the map's projection (#4079)
2504
   */
2505
0
  if (lp->projection.numargs <= 0) {
2506
0
    char *map_original_srs = msGetProjectionString(&(map->projection));
2507
0
    if (msLoadProjectionString(&(lp->projection), map_original_srs) != 0) {
2508
0
      msSetError(
2509
0
          MS_WCSERR,
2510
0
          "Error when setting map projection to a layer with no projection",
2511
0
          "msWCSGetCoverage()");
2512
0
      free(map_original_srs);
2513
0
      return msWCSException(map, NULL, NULL, params->version);
2514
0
    }
2515
0
    free(map_original_srs);
2516
0
  }
2517
2518
  /* we need the coverage metadata, since things like numbands may not be
2519
   * available otherwise */
2520
0
  status = msWCSGetCoverageMetadata(lp, &cm);
2521
0
  if (status != MS_SUCCESS)
2522
0
    return MS_FAILURE;
2523
2524
  /* fill in bands rangeset info, if required.  */
2525
0
  msWCSSetDefaultBandsRangeSetInfo(params, &cm, lp);
2526
2527
  /* handle the response CRS, that is, set the map object projection */
2528
0
  if (params->response_crs || params->crs) {
2529
0
    int iUnits;
2530
0
    const char *crs_to_use = params->response_crs;
2531
0
    if (crs_to_use == NULL)
2532
0
      crs_to_use = params->crs;
2533
2534
0
    if (strcasecmp(crs_to_use, "imageCRS") == 0) {
2535
      /* use layer native CRS, and rework bounding box accordingly */
2536
0
      if (msWCSGetCoverage_ImageCRSSetup(map, params, &cm, lp) != MS_SUCCESS) {
2537
0
        msWCSFreeCoverageMetadata(&cm);
2538
0
        return MS_FAILURE;
2539
0
      }
2540
0
    } else if (strncasecmp(crs_to_use, "urn:ogc:def:crs:", 16) == 0 ||
2541
0
               strncasecmp(crs_to_use, "http://www.opengis.net/def/crs/", 31) ==
2542
0
                   0 ||
2543
0
               strchr(crs_to_use, ':') != NULL) {
2544
      /* Handles URNs, OGC URIs, and any AUTHORITY:CODE pattern
2545
      ** e.g. EPSG:4326, ESRI:54052, CRS:84, IAU_2015:30100 */
2546
0
      if (msLoadProjectionString(&(map->projection), (char *)crs_to_use) != 0) {
2547
0
        msSetError(MS_WCSERR, "Unsupported or unknown CRS '%s'.",
2548
0
                   "msWCSGetCoverage()", crs_to_use);
2549
0
        return msWCSException(map, "InvalidParameterValue", "srs",
2550
0
                              params->version);
2551
0
      }
2552
0
    } else {
2553
0
      msSetError(
2554
0
          MS_WCSERR,
2555
0
          "Unsupported SRS format '%s' (expected AUTHORITY:CODE, URN, or URI).",
2556
0
          "msWCSGetCoverage()", crs_to_use);
2557
0
      return msWCSException(map, "InvalidParameterValue", "srs",
2558
0
                            params->version);
2559
0
    }
2560
2561
0
    iUnits = GetMapserverUnitUsingProj(&(map->projection));
2562
0
    if (iUnits != -1)
2563
0
      map->units = static_cast<MS_UNITS>(iUnits);
2564
0
  }
2565
2566
  /* did we get a TIME value (support only a single value for now) */
2567
0
  if (params->time) {
2568
0
    int tli;
2569
0
    layerObj *tlp = NULL;
2570
2571
    /* need to handle NOW case */
2572
2573
    /* check format of TIME parameter */
2574
0
    if (strchr(params->time, ',')) {
2575
0
      msWCSFreeCoverageMetadata(&cm);
2576
0
      msSetError(MS_WCSERR,
2577
0
                 "Temporal lists are not supported, only individual values.",
2578
0
                 "msWCSGetCoverage()");
2579
0
      return msWCSException(map, "InvalidParameterValue", "time",
2580
0
                            params->version);
2581
0
    }
2582
0
    if (strchr(params->time, '/')) {
2583
0
      msWCSFreeCoverageMetadata(&cm);
2584
0
      msSetError(MS_WCSERR,
2585
0
                 "Temporal ranges are not supported, only individual values.",
2586
0
                 "msWCSGetCoverage()");
2587
0
      return msWCSException(map, "InvalidParameterValue", "time",
2588
0
                            params->version);
2589
0
    }
2590
2591
    /* TODO: will need to expand this check if a time period is supported */
2592
0
    value = msOWSLookupMetadata(&(lp->metadata), "CO", "timeposition");
2593
0
    if (!value) {
2594
0
      msWCSFreeCoverageMetadata(&cm);
2595
0
      msSetError(MS_WCSERR,
2596
0
                 "The coverage does not support temporal subsetting.",
2597
0
                 "msWCSGetCoverage()");
2598
0
      return msWCSException(map, "InvalidParameterValue", "time",
2599
0
                            params->version);
2600
0
    }
2601
2602
    /* check if timestamp is covered by the wcs_timeposition definition */
2603
0
    if (msValidateTimeValue(params->time, value) == MS_FALSE) {
2604
0
      msWCSFreeCoverageMetadata(&cm);
2605
0
      msSetError(MS_WCSERR, "The coverage does not have a time position of %s.",
2606
0
                 "msWCSGetCoverage()", params->time);
2607
0
      return msWCSException(map, "InvalidParameterValue", "time",
2608
0
                            params->version);
2609
0
    }
2610
2611
    /* make sure layer is tiled appropriately */
2612
0
    if (!lp->tileindex) {
2613
0
      msWCSFreeCoverageMetadata(&cm);
2614
0
      msSetError(
2615
0
          MS_WCSERR,
2616
0
          "Underlying layer is not tiled, unable to do temporal subsetting.",
2617
0
          "msWCSGetCoverage()");
2618
0
      return msWCSException(map, NULL, NULL, params->version);
2619
0
    }
2620
0
    tli = msGetLayerIndex(map, lp->tileindex);
2621
0
    if (tli == -1) {
2622
0
      msWCSFreeCoverageMetadata(&cm);
2623
0
      msSetError(MS_WCSERR,
2624
0
                 "Underlying layer does not use appropriate tiling mechanism.",
2625
0
                 "msWCSGetCoverage()");
2626
0
      return msWCSException(map, NULL, NULL, params->version);
2627
0
    }
2628
2629
0
    tlp = (GET_LAYER(map, tli));
2630
2631
    /* make sure there is enough information to filter */
2632
0
    value = msOWSLookupMetadata(&(lp->metadata), "CO", "timeitem");
2633
0
    if (!tlp->filteritem && !value) {
2634
0
      msWCSFreeCoverageMetadata(&cm);
2635
0
      msSetError(MS_WCSERR, "Not enough information available to filter.",
2636
0
                 "msWCSGetCoverage()");
2637
0
      return msWCSException(map, NULL, NULL, params->version);
2638
0
    }
2639
2640
    /* override filteritem if specified in metadata */
2641
0
    if (value) {
2642
0
      if (tlp->filteritem)
2643
0
        free(tlp->filteritem);
2644
0
      tlp->filteritem = msStrdup(value);
2645
0
    }
2646
2647
    /* finally set the filter */
2648
0
    msLayerSetTimeFilter(tlp, params->time, value);
2649
0
  }
2650
2651
0
  if (strncasecmp(params->version, "1.0", 3) == 0)
2652
0
    status = msWCSGetCoverageBands10(map, request, params, lp, &bandlist);
2653
0
  else
2654
0
    status = msWCSGetCoverageBands11(map, request, params, lp, &bandlist);
2655
0
  if (status != MS_SUCCESS) {
2656
0
    msWCSFreeCoverageMetadata(&cm);
2657
0
    return status;
2658
0
  }
2659
2660
  /* did we get BBOX values? if not use the extent stored in the
2661
   * coverageMetadataObj */
2662
0
  if (fabs((params->bbox.maxx - params->bbox.minx)) < 0.000000000001 ||
2663
0
      fabs(params->bbox.maxy - params->bbox.miny) < 0.000000000001) {
2664
0
    params->bbox = cm.extent;
2665
2666
    /* WCS 1.1 boundbox is center of pixel oriented. */
2667
0
    if (strncasecmp(params->version, "1.1", 3) == 0) {
2668
0
      params->bbox.minx += cm.geotransform[1] / 2 + cm.geotransform[2] / 2;
2669
0
      params->bbox.maxx -= cm.geotransform[1] / 2 + cm.geotransform[2] / 2;
2670
0
      params->bbox.maxy += cm.geotransform[4] / 2 + cm.geotransform[5] / 2;
2671
0
      params->bbox.miny -= cm.geotransform[4] / 2 + cm.geotransform[5] / 2;
2672
0
    }
2673
0
  }
2674
2675
  /* WCS 1.1+ GridOrigin is effectively resetting the minx/maxy
2676
     BOUNDINGBOX values, so apply that here */
2677
0
  if (params->originx != 0.0 || params->originy != 0.0) {
2678
0
    assert(strncasecmp(params->version, "1.0", 3) !=
2679
0
           0); /* should always be 1.0 in this logic. */
2680
0
    params->bbox.minx = params->originx;
2681
0
    params->bbox.maxy = params->originy;
2682
0
  }
2683
2684
  /* if necessary, project the BBOX to the map->projection */
2685
0
  if (params->response_crs && params->crs) {
2686
0
    projectionObj tmp_proj;
2687
2688
0
    msInitProjection(&tmp_proj);
2689
0
    msProjectionInheritContextFrom(&tmp_proj, &(map->projection));
2690
0
    if (msLoadProjectionString(&tmp_proj, (char *)params->crs) != 0) {
2691
0
      msFreeProjection(&tmp_proj);
2692
0
      msWCSFreeCoverageMetadata(&cm);
2693
0
      return msWCSException(map, NULL, NULL, params->version);
2694
0
    }
2695
0
    msProjectRect(&tmp_proj, &map->projection, &(params->bbox));
2696
0
    msFreeProjection(&tmp_proj);
2697
0
  }
2698
2699
  /* in WCS 1.1 the default is full resolution */
2700
0
  if (strncasecmp(params->version, "1.1", 3) == 0 && params->resx == 0.0 &&
2701
0
      params->resy == 0.0) {
2702
0
    params->resx = cm.geotransform[1];
2703
0
    params->resy = fabs(cm.geotransform[5]);
2704
0
  }
2705
2706
  /* compute width/height from BBOX and cellsize.  */
2707
0
  if ((params->resx == 0.0 || params->resy == 0.0) && params->width != 0 &&
2708
0
      params->height != 0) {
2709
0
    assert(strncasecmp(params->version, "1.0", 3) ==
2710
0
           0); /* should always be 1.0 in this logic. */
2711
0
    params->resx = (params->bbox.maxx - params->bbox.minx) / params->width;
2712
0
    params->resy = (params->bbox.maxy - params->bbox.miny) / params->height;
2713
0
  }
2714
2715
  /* compute cellsize/res from bbox and raster size. */
2716
0
  if ((params->width == 0 || params->height == 0) && params->resx != 0 &&
2717
0
      params->resy != 0) {
2718
2719
    /* WCS 1.0 boundbox is edge of pixel oriented. */
2720
0
    if (strncasecmp(params->version, "1.0", 3) == 0) {
2721
0
      params->width =
2722
0
          (int)((params->bbox.maxx - params->bbox.minx) / params->resx + 0.5);
2723
0
      params->height =
2724
0
          (int)((params->bbox.maxy - params->bbox.miny) / params->resy + 0.5);
2725
0
    } else {
2726
0
      params->width =
2727
0
          (int)((params->bbox.maxx - params->bbox.minx) / params->resx +
2728
0
                1.000001);
2729
0
      params->height =
2730
0
          (int)((params->bbox.maxy - params->bbox.miny) / params->resy +
2731
0
                1.000001);
2732
2733
      /* recompute bounding box so we get exactly the origin and
2734
         resolution requested. */
2735
0
      params->bbox.maxx =
2736
0
          params->bbox.minx + (params->width - 1) * params->resx;
2737
0
      params->bbox.miny =
2738
0
          params->bbox.maxy - (params->height - 1) * params->resy;
2739
0
    }
2740
0
  }
2741
2742
  /* are we still underspecified?  */
2743
0
  if ((params->width == 0 || params->height == 0) &&
2744
0
      (params->resx == 0.0 || params->resy == 0.0)) {
2745
0
    msWCSFreeCoverageMetadata(&cm);
2746
0
    msSetError(MS_WCSERR,
2747
0
               "A non-zero RESX/RESY or WIDTH/HEIGHT is required but neither "
2748
0
               "was provided.",
2749
0
               "msWCSGetCoverage()");
2750
0
    return msWCSException(map, "MissingParameterValue",
2751
0
                          "width/height/resx/resy", params->version);
2752
0
  }
2753
2754
0
  map->cellsize = params->resx;
2755
2756
  /* Do we need to force special handling?  */
2757
0
  if (fabs(params->resx / params->resy - 1.0) > 0.001) {
2758
0
    map->gt.need_geotransform = MS_TRUE;
2759
0
    if (map->debug)
2760
0
      msDebug("RESX and RESY don't match.  Using geotransform/resample.\n");
2761
0
  }
2762
2763
  /* Do we have a specified interpolation method */
2764
0
  if (params->interpolation != NULL) {
2765
0
    if (strncasecmp(params->interpolation, "NEAREST", 7) == 0)
2766
0
      msLayerSetProcessingKey(lp, "RESAMPLE", "NEAREST");
2767
0
    else if (strcasecmp(params->interpolation, "BILINEAR") == 0)
2768
0
      msLayerSetProcessingKey(lp, "RESAMPLE", "BILINEAR");
2769
0
    else if (strcasecmp(params->interpolation, "AVERAGE") == 0)
2770
0
      msLayerSetProcessingKey(lp, "RESAMPLE", "AVERAGE");
2771
0
    else {
2772
0
      msWCSFreeCoverageMetadata(&cm);
2773
0
      msSetError(
2774
0
          MS_WCSERR,
2775
0
          "INTERPOLATION=%s specifies an unsupported interpolation method.",
2776
0
          "msWCSGetCoverage()", params->interpolation);
2777
0
      return msWCSException(map, "InvalidParameterValue", "interpolation",
2778
0
                            params->version);
2779
0
    }
2780
0
  }
2781
2782
  /* apply region and size to map object.  */
2783
0
  map->width = params->width;
2784
0
  map->height = params->height;
2785
2786
  /* Are we exceeding the MAXSIZE limit on result size? */
2787
0
  if (map->width > map->maxsize || map->height > map->maxsize) {
2788
0
    msWCSFreeCoverageMetadata(&cm);
2789
0
    msSetError(MS_WCSERR,
2790
0
               "Raster size out of range, width and height of resulting "
2791
0
               "coverage must be no more than MAXSIZE=%d.",
2792
0
               "msWCSGetCoverage()", map->maxsize);
2793
2794
0
    return msWCSException(map, "InvalidParameterValue", "width/height",
2795
0
                          params->version);
2796
0
  }
2797
2798
  /* adjust OWS BBOX to MapServer's pixel model */
2799
0
  if (strncasecmp(params->version, "1.0", 3) == 0) {
2800
0
    params->bbox.minx += params->resx * 0.5;
2801
0
    params->bbox.miny += params->resy * 0.5;
2802
0
    params->bbox.maxx -= params->resx * 0.5;
2803
0
    params->bbox.maxy -= params->resy * 0.5;
2804
0
  }
2805
2806
0
  map->extent = params->bbox;
2807
2808
0
  map->cellsize = params->resx; /* pick one, MapServer only supports square
2809
                                   cells (what about msAdjustExtent here!) */
2810
2811
0
  if (params->width == 1 || params->height == 1)
2812
0
    msMapComputeGeotransformEx(map, params->resx, params->resy);
2813
0
  else
2814
0
    msMapComputeGeotransform(map);
2815
2816
  /* Do we need to fake out stuff for rotated support? */
2817
0
  if (map->gt.need_geotransform)
2818
0
    msMapSetFakedExtent(map);
2819
2820
0
  map->projection.gt = map->gt;
2821
2822
  /* check for overlap */
2823
2824
  /* get extent of bbox passed, and reproject */
2825
0
  reqextent.minx = map->extent.minx;
2826
0
  reqextent.miny = map->extent.miny;
2827
0
  reqextent.maxx = map->extent.maxx;
2828
0
  reqextent.maxy = map->extent.maxy;
2829
2830
  /* reproject incoming bbox */
2831
0
  msProjectRect(&map->projection, &lp->projection, &(reqextent));
2832
2833
  /* get extent of layer */
2834
0
  covextent.minx = cm.extent.minx;
2835
0
  covextent.miny = cm.extent.miny;
2836
0
  covextent.maxx = cm.extent.maxx;
2837
0
  covextent.maxy = cm.extent.maxy;
2838
2839
0
  if (msRectOverlap(&reqextent, &covextent) == MS_FALSE) {
2840
0
    msWCSFreeCoverageMetadata(&cm);
2841
0
    msSetError(MS_WCSERR,
2842
0
               "Requested BBOX (%.15g,%.15g,%.15g,%.15g) is outside requested "
2843
0
               "coverage BBOX (%.15g,%.15g,%.15g,%.15g)",
2844
0
               "msWCSGetCoverage()", reqextent.minx, reqextent.miny,
2845
0
               reqextent.maxx, reqextent.maxy, covextent.minx, covextent.miny,
2846
0
               covextent.maxx, covextent.maxy);
2847
0
    return msWCSException(map, "NoApplicableCode", "bbox", params->version);
2848
0
  }
2849
2850
  /* check and make sure there is a format, and that it's valid (TODO: make sure
2851
   * in the layer metadata) */
2852
0
  if (!params->format) {
2853
0
    msWCSFreeCoverageMetadata(&cm);
2854
0
    msSetError(MS_WCSERR, "Missing required FORMAT parameter.",
2855
0
               "msWCSGetCoverage()");
2856
0
    return msWCSException(map, "MissingParameterValue", "format",
2857
0
                          params->version);
2858
0
  }
2859
0
  msApplyDefaultOutputFormats(map);
2860
0
  if (msGetOutputFormatIndex(map, params->format) == -1) {
2861
0
    msWCSFreeCoverageMetadata(&cm);
2862
0
    msSetError(MS_WCSERR, "Unrecognized value for the FORMAT parameter.",
2863
0
               "msWCSGetCoverage()");
2864
0
    return msWCSException(map, "InvalidParameterValue", "format",
2865
0
                          params->version);
2866
0
  }
2867
2868
  /* create a temporary outputformat (we likely will need to tweak parts) */
2869
0
  format = msCloneOutputFormat(msSelectOutputFormat(map, params->format));
2870
0
  msApplyOutputFormat(&(map->outputformat), format, MS_NOOVERRIDE);
2871
2872
0
  if (!bandlist) { /* build a bandlist (default is ALL bands) */
2873
0
    bufferSize = cm.bandcount * 30 + 30;
2874
0
    bandlist = (char *)msSmallMalloc(bufferSize);
2875
0
    strcpy(bandlist, "1");
2876
0
    for (i = 1; i < cm.bandcount; i++)
2877
0
      snprintf(bandlist + strlen(bandlist), bufferSize - strlen(bandlist),
2878
0
               ",%d", i + 1);
2879
0
  }
2880
2881
  /* apply nullvalue to the output format object if we have it */
2882
0
  if ((value = msOWSLookupMetadata(&(lp->metadata), "CO",
2883
0
                                   "rangeset_nullvalue")) != NULL) {
2884
0
    msSetOutputFormatOption(map->outputformat, "NULLVALUE", value);
2885
0
  }
2886
2887
0
  msLayerSetProcessingKey(lp, "BANDS", bandlist);
2888
0
  snprintf(numbands, sizeof(numbands), "%d", msCountChars(bandlist, ',') + 1);
2889
0
  msSetOutputFormatOption(map->outputformat, "BAND_COUNT", numbands);
2890
2891
0
  msWCSApplyLayerCreationOptions(lp, map->outputformat, bandlist);
2892
0
  msWCSApplyLayerMetadataItemOptions(lp, map->outputformat, bandlist);
2893
2894
0
  if (lp->tileindex == NULL && lp->data != NULL && strlen(lp->data) > 0 &&
2895
0
      lp->connectiontype != MS_KERNELDENSITY) {
2896
0
    if (msDrawRasterLayerLowCheckIfMustDraw(map, lp)) {
2897
0
      char *decrypted_path = NULL;
2898
0
      char szPath[MS_MAXPATHLEN];
2899
0
      hDS = (GDALDatasetH)msDrawRasterLayerLowOpenDataset(
2900
0
          map, lp, lp->data, szPath, &decrypted_path);
2901
0
      msFree(decrypted_path);
2902
0
      if (hDS) {
2903
0
        msWCSApplyDatasetMetadataAsCreationOptions(lp, map->outputformat,
2904
0
                                                   bandlist, hDS);
2905
0
        msWCSApplySourceDatasetMetadata(lp, map->outputformat, bandlist, hDS);
2906
0
      }
2907
0
    } else {
2908
0
      doDrawRasterLayerDraw = MS_FALSE;
2909
0
    }
2910
0
  }
2911
2912
0
  free(bandlist);
2913
2914
0
  if (lp->mask) {
2915
0
    int maskLayerIdx = msGetLayerIndex(map, lp->mask);
2916
0
    layerObj *maskLayer;
2917
0
    outputFormatObj *altFormat;
2918
0
    if (maskLayerIdx == -1) {
2919
0
      msWCSFreeCoverageMetadata(&cm);
2920
0
      msSetError(MS_MISCERR, "Layer (%s) references unknown mask layer (%s)",
2921
0
                 "msDrawLayer()", lp->name, lp->mask);
2922
0
      msDrawRasterLayerLowCloseDataset(lp, hDS);
2923
0
      return msWCSException(map, NULL, NULL, params->version);
2924
0
    }
2925
0
    maskLayer = GET_LAYER(map, maskLayerIdx);
2926
0
    if (!maskLayer->maskimage) {
2927
0
      int i, retcode;
2928
0
      int origstatus, origlabelcache;
2929
0
      char *origImageType = msStrdup(map->imagetype);
2930
0
      altFormat = msSelectOutputFormat(map, "png24");
2931
0
      msInitializeRendererVTable(altFormat);
2932
      /* TODO: check the png24 format hasn't been tampered with, i.e. it's agg
2933
       */
2934
0
      maskLayer->maskimage = msImageCreate(
2935
0
          map->width, map->height, altFormat, map->web.imagepath,
2936
0
          map->web.imageurl, map->resolution, map->defresolution, NULL);
2937
0
      if (!maskLayer->maskimage) {
2938
0
        msWCSFreeCoverageMetadata(&cm);
2939
0
        msSetError(MS_MISCERR, "Unable to initialize mask image.",
2940
0
                   "msDrawLayer()");
2941
0
        msFree(origImageType);
2942
0
        msDrawRasterLayerLowCloseDataset(lp, hDS);
2943
0
        return msWCSException(map, NULL, NULL, params->version);
2944
0
      }
2945
2946
      /*
2947
       * force the masked layer to status on, and turn off the labelcache so
2948
       * that eventual labels are added to the temporary image instead of being
2949
       * added to the labelcache
2950
       */
2951
0
      origstatus = maskLayer->status;
2952
0
      origlabelcache = maskLayer->labelcache;
2953
0
      maskLayer->status = MS_ON;
2954
0
      maskLayer->labelcache = MS_OFF;
2955
2956
      /* draw the mask layer in the temporary image */
2957
0
      retcode = msDrawLayer(map, maskLayer, maskLayer->maskimage);
2958
0
      maskLayer->status = origstatus;
2959
0
      maskLayer->labelcache = origlabelcache;
2960
0
      if (retcode != MS_SUCCESS) {
2961
0
        msWCSFreeCoverageMetadata(&cm);
2962
        /* set the imagetype from the original outputformat back (it was removed
2963
         * by msSelectOutputFormat() */
2964
0
        msFree(map->imagetype);
2965
0
        map->imagetype = origImageType;
2966
0
        msDrawRasterLayerLowCloseDataset(lp, hDS);
2967
0
        return msWCSException(map, NULL, NULL, params->version);
2968
0
      }
2969
      /*
2970
       * hack to work around bug #3834: if we have use an alternate renderer,
2971
       * the symbolset may contain symbols that reference it. We want to remove
2972
       * those references before the altFormat is destroyed to avoid a segfault
2973
       * and/or a leak, and so the the main renderer doesn't pick the cache up
2974
       * thinking it's for him.
2975
       */
2976
0
      for (i = 0; i < map->symbolset.numsymbols; i++) {
2977
0
        if (map->symbolset.symbol[i] != NULL) {
2978
0
          symbolObj *s = map->symbolset.symbol[i];
2979
0
          if (s->renderer == MS_IMAGE_RENDERER(maskLayer->maskimage)) {
2980
0
            MS_IMAGE_RENDERER(maskLayer->maskimage)->freeSymbol(s);
2981
0
            s->renderer = NULL;
2982
0
          }
2983
0
        }
2984
0
      }
2985
      /* set the imagetype from the original outputformat back (it was removed
2986
       * by msSelectOutputFormat() */
2987
0
      msFree(map->imagetype);
2988
0
      map->imagetype = origImageType;
2989
0
    }
2990
0
  }
2991
2992
  /* create the image object  */
2993
0
  if (!map->outputformat) {
2994
0
    msWCSFreeCoverageMetadata(&cm);
2995
0
    msSetError(MS_WCSERR, "The map outputformat is missing!",
2996
0
               "msWCSGetCoverage()");
2997
0
    msDrawRasterLayerLowCloseDataset(lp, hDS);
2998
0
    return msWCSException(map, NULL, NULL, params->version);
2999
0
  } else if (MS_RENDERER_RAWDATA(map->outputformat) ||
3000
0
             MS_RENDERER_PLUGIN(map->outputformat)) {
3001
0
    image = msImageCreate(map->width, map->height, map->outputformat,
3002
0
                          map->web.imagepath, map->web.imageurl,
3003
0
                          map->resolution, map->defresolution, NULL);
3004
0
  } else {
3005
0
    msWCSFreeCoverageMetadata(&cm);
3006
0
    msSetError(MS_WCSERR, "Map outputformat not supported for WCS!",
3007
0
               "msWCSGetCoverage()");
3008
0
    msDrawRasterLayerLowCloseDataset(lp, hDS);
3009
0
    return msWCSException(map, NULL, NULL, params->version);
3010
0
  }
3011
3012
0
  if (image == NULL) {
3013
0
    msWCSFreeCoverageMetadata(&cm);
3014
0
    msDrawRasterLayerLowCloseDataset(lp, hDS);
3015
0
    return msWCSException(map, NULL, NULL, params->version);
3016
0
  }
3017
0
  if (MS_RENDERER_RAWDATA(map->outputformat)) {
3018
0
    if (doDrawRasterLayerDraw) {
3019
0
      status = msDrawRasterLayerLowWithDataset(map, lp, image, NULL, hDS);
3020
0
    } else {
3021
0
      status = MS_SUCCESS;
3022
0
    }
3023
0
  } else {
3024
0
    status = MS_IMAGE_RENDERER(image)->getRasterBufferHandle(image, &rb);
3025
0
    if (MS_UNLIKELY(status == MS_FAILURE)) {
3026
0
      msWCSFreeCoverageMetadata(&cm);
3027
0
      msDrawRasterLayerLowCloseDataset(lp, hDS);
3028
0
      return MS_FAILURE;
3029
0
    }
3030
3031
    /* Actually produce the "grid". */
3032
0
    if (doDrawRasterLayerDraw) {
3033
0
      status = msDrawRasterLayerLowWithDataset(map, lp, image, &rb, hDS);
3034
0
    } else {
3035
0
      status = MS_SUCCESS;
3036
0
    }
3037
0
  }
3038
0
  msDrawRasterLayerLowCloseDataset(lp, hDS);
3039
3040
0
  if (status != MS_SUCCESS) {
3041
0
    msWCSFreeCoverageMetadata(&cm);
3042
0
    msFreeImage(image);
3043
0
    return msWCSException(map, NULL, NULL, params->version);
3044
0
  }
3045
3046
0
  if (strncmp(params->version, "1.1", 3) == 0) {
3047
0
    msWCSReturnCoverage11(params, map, image);
3048
0
  } else { /* WCS 1.0.0 - just return the binary data with a content type */
3049
0
    const char *fo_filename;
3050
3051
    /* Do we have a predefined filename? */
3052
0
    fo_filename = msGetOutputFormatOption(format, "FILENAME", NULL);
3053
0
    if (fo_filename)
3054
0
      msIO_setHeader("Content-Disposition", "attachment; filename=%s",
3055
0
                     fo_filename);
3056
3057
    /* Emit back to client. */
3058
0
    msOutputFormatResolveFromImage(map, image);
3059
0
    msIO_setHeader("Content-Type", "%s", MS_IMAGE_MIME_TYPE(map->outputformat));
3060
0
    msIO_sendHeaders();
3061
0
    status = msSaveImage(map, image, NULL);
3062
3063
0
    if (status != MS_SUCCESS) {
3064
      /* unfortunately, the image content type will have already been sent
3065
         but that is hard for us to avoid.  The main error that could happen
3066
         here is a misconfigured tmp directory or running out of space. */
3067
0
      msWCSFreeCoverageMetadata(&cm);
3068
0
      return msWCSException(map, NULL, NULL, params->version);
3069
0
    }
3070
0
  }
3071
3072
  /* Cleanup */
3073
0
  msFreeImage(image);
3074
0
  msApplyOutputFormat(&(map->outputformat), NULL, MS_NOOVERRIDE);
3075
  /* msFreeOutputFormat(format); */
3076
3077
0
  msWCSFreeCoverageMetadata(&cm);
3078
0
  return status;
3079
0
}
3080
#endif /* def USE_WCS_SVR */
3081
3082
/************************************************************************/
3083
/*                           msWCSDispatch()                            */
3084
/*                                                                      */
3085
/*      Entry point for WCS requests                                    */
3086
/************************************************************************/
3087
3088
int msWCSDispatch(mapObj *map, cgiRequestObj *request,
3089
4.90k
                  owsRequestObj *ows_request) {
3090
4.90k
#if defined(USE_WCS_SVR)
3091
4.90k
  wcs20ParamsObj *params20 = NULL;
3092
4.90k
  int status, retVal, operation;
3093
3094
  /* If SERVICE is not set or not WCS exit gracefully. */
3095
4.90k
  if (ows_request->service == NULL || !EQUAL(ows_request->service, "WCS")) {
3096
0
    return MS_DONE;
3097
0
  }
3098
3099
  /* If no REQUEST is set, exit with an error */
3100
4.90k
  if (ows_request->request == NULL) {
3101
    /* The request has to be set. */
3102
136
    msSetError(MS_WCSERR, "Missing REQUEST parameter", "msWCSDispatch()");
3103
136
    return msWCSException(map, "MissingParameterValue", "request",
3104
136
                          ows_request->version);
3105
136
  }
3106
3107
4.76k
  if (EQUAL(ows_request->request, "GetCapabilities")) {
3108
4.38k
    operation = MS_WCS_GET_CAPABILITIES;
3109
4.38k
  } else if (EQUAL(ows_request->request, "DescribeCoverage")) {
3110
1
    operation = MS_WCS_DESCRIBE_COVERAGE;
3111
377
  } else if (EQUAL(ows_request->request, "GetCoverage")) {
3112
1
    operation = MS_WCS_GET_COVERAGE;
3113
376
  } else {
3114
376
    msSetError(MS_WCSERR, "Invalid REQUEST parameter \"%s\"", "msWCSDispatch()",
3115
376
               ows_request->request);
3116
376
    return msWCSException(map, "InvalidParameterValue", "request",
3117
376
                          ows_request->version);
3118
376
  }
3119
3120
  /* Check the number of enabled layers for the REQUEST */
3121
4.39k
  msOWSRequestLayersEnabled(map, "C", ows_request->request, ows_request);
3122
4.39k
  if (ows_request->numlayers == 0) {
3123
4.39k
    int caps_globally_enabled = MS_FALSE, disabled = MS_FALSE;
3124
4.39k
    const char *enable_request;
3125
4.39k
    if (operation == MS_WCS_GET_CAPABILITIES) {
3126
4.38k
      enable_request =
3127
4.38k
          msOWSLookupMetadata(&map->web.metadata, "OC", "enable_request");
3128
4.38k
      caps_globally_enabled = msOWSParseRequestMetadata(
3129
4.38k
          enable_request, "GetCapabilities", &disabled);
3130
4.38k
    }
3131
3132
4.39k
    if (caps_globally_enabled == MS_FALSE) {
3133
2
      msSetError(MS_WCSERR,
3134
2
                 "WCS request not enabled. Check "
3135
2
                 "wcs/ows_enable_request settings.",
3136
2
                 "msWCSDispatch()");
3137
2
      return msWCSException(map, "InvalidParameterValue", "request",
3138
2
                            ows_request->version);
3139
2
    }
3140
4.39k
  }
3141
3142
  /* Check the VERSION parameter */
3143
4.38k
  if (ows_request->version == NULL) {
3144
    /* If the VERSION parameter is not set, it is either */
3145
    /* an error (Describe and GetCoverage), or it has to */
3146
    /* be determined (GetCapabilities). To determine the */
3147
    /* version, the request has to be fully parsed to    */
3148
    /* obtain the ACCEPTVERSIONS parameter. If this is   */
3149
    /* present also, set version to "2.0.1".             */
3150
3151
3.06k
    if (operation == MS_WCS_GET_CAPABILITIES) {
3152
      /* Parse it as if it was a WCS 2.0 request */
3153
3.06k
      wcs20ParamsObjPtr params_tmp = msWCSCreateParamsObj20();
3154
3.06k
      status = msWCSParseRequest20(map, request, ows_request, params_tmp);
3155
3.06k
      if (status == MS_FAILURE) {
3156
651
        msWCSFreeParamsObj20(params_tmp);
3157
651
        return msWCSException(map, "InvalidParameterValue", "request", "2.0.1");
3158
651
      }
3159
3160
      /* VERSION negotiation */
3161
2.41k
      if (params_tmp->accept_versions != NULL) {
3162
        /* choose highest acceptable */
3163
384
        int i, highest_version = 0;
3164
384
        char version_string[OWS_VERSION_MAXLEN];
3165
2.92k
        for (i = 0; params_tmp->accept_versions[i] != NULL; ++i) {
3166
2.55k
          int version = msOWSParseVersionString(params_tmp->accept_versions[i]);
3167
2.55k
          if (version == OWS_VERSION_BADFORMAT) {
3168
19
            msWCSFreeParamsObj20(params_tmp);
3169
19
            return msWCSException(map, "InvalidParameterValue", "version",
3170
19
                                  NULL);
3171
19
          }
3172
2.53k
          if (version > highest_version) {
3173
377
            highest_version = version;
3174
377
          }
3175
2.53k
        }
3176
365
        msOWSGetVersionString(highest_version, version_string);
3177
365
        params_tmp->version = msStrdup(version_string);
3178
365
        ows_request->version = msStrdup(version_string);
3179
2.02k
      } else {
3180
        /* set to highest acceptable */
3181
2.02k
        params_tmp->version = msStrdup("2.0.1");
3182
2.02k
        ows_request->version = msStrdup("2.0.1");
3183
2.02k
      }
3184
3185
      /* check if we can keep the params object */
3186
2.39k
      if (EQUAL(params_tmp->version, "2.0.1")) {
3187
2.02k
        params20 = params_tmp;
3188
2.02k
      } else {
3189
365
        msWCSFreeParamsObj20(params_tmp);
3190
365
      }
3191
2.39k
    } else { /* operation != GetCapabilities */
3192
      /* VERSION is mandatory in other requests */
3193
0
      msSetError(MS_WCSERR, "VERSION parameter not set.", "msWCSDispatch()");
3194
0
      return msWCSException(map, "InvalidParameterValue", "version", NULL);
3195
0
    }
3196
3.06k
  } else {
3197
    /* Parse the VERSION parameter */
3198
1.32k
    int requested_version = msOWSParseVersionString(ows_request->version);
3199
1.32k
    if (requested_version == OWS_VERSION_BADFORMAT) {
3200
      /* Return an error if the VERSION is */
3201
      /* in an unsupported format.         */
3202
1
      return msWCSException(map, "InvalidParameterValue", "version", NULL);
3203
1
    }
3204
3205
1.32k
    if (operation == MS_WCS_GET_CAPABILITIES) {
3206
      /* In case of GetCapabilities, make  */
3207
1.32k
      char version_string[OWS_VERSION_MAXLEN];
3208
1.32k
      int version, supported_versions[] = {OWS_2_0_1, OWS_2_0_0, OWS_1_1_2,
3209
1.32k
                                           OWS_1_1_1, OWS_1_1_0, OWS_1_0_0};
3210
1.32k
      version = msOWSNegotiateVersion(requested_version, supported_versions,
3211
1.32k
                                      sizeof(supported_versions) / sizeof(int));
3212
1.32k
      msOWSGetVersionString(version, version_string);
3213
1.32k
      msFree(ows_request->version);
3214
1.32k
      ows_request->version = msStrdup(version_string);
3215
1.32k
    }
3216
1.32k
  }
3217
3218
  /* VERSION specific request handler */
3219
3.71k
  if (strcmp(ows_request->version, "1.0.0") == 0 ||
3220
2.47k
      strcmp(ows_request->version, "1.1.0") == 0 ||
3221
2.43k
      strcmp(ows_request->version, "1.1.1") == 0 ||
3222
2.43k
      strcmp(ows_request->version, "1.1.2") == 0) {
3223
1.33k
    auto paramsTmp = msWCSCreateParams();
3224
1.33k
    status = msWCSParseRequest(request, paramsTmp, map);
3225
1.33k
    if (status == MS_DONE) {
3226
7
      msWCSFreeParams(paramsTmp);
3227
7
      free(paramsTmp);
3228
7
      return MS_FAILURE;
3229
7
    }
3230
1.32k
    if (status == MS_FAILURE) {
3231
18
      msWCSFreeParams(paramsTmp);
3232
18
      free(paramsTmp);
3233
18
      return msWCSException(map, "InvalidParameterValue", "request", "2.0");
3234
18
    }
3235
3236
1.30k
    retVal = MS_FAILURE;
3237
1.30k
    if (operation == MS_WCS_GET_CAPABILITIES) {
3238
1.30k
      retVal = msWCSGetCapabilities(map, paramsTmp, request, ows_request);
3239
1.30k
    } else if (operation == MS_WCS_DESCRIBE_COVERAGE) {
3240
0
      retVal = msWCSDescribeCoverage(map, paramsTmp, ows_request, request);
3241
0
    } else if (operation == MS_WCS_GET_COVERAGE) {
3242
0
      retVal = msWCSGetCoverage(map, request, paramsTmp, ows_request);
3243
0
    }
3244
1.30k
    msWCSFreeParams(paramsTmp);
3245
1.30k
    free(paramsTmp);
3246
1.30k
    return retVal;
3247
2.38k
  } else if (strcmp(ows_request->version, "2.0.0") == 0 ||
3248
2.30k
             strcmp(ows_request->version, "2.0.1") == 0) {
3249
2.30k
#if defined(USE_LIBXML2)
3250
2.30k
    int i;
3251
3252
2.30k
    if (params20 == NULL) {
3253
278
      params20 = msWCSCreateParamsObj20();
3254
278
      status = msWCSParseRequest20(map, request, ows_request, params20);
3255
278
      if (status == MS_FAILURE) {
3256
1
        msWCSFreeParamsObj20(params20);
3257
1
        return msWCSException(map, "InvalidParameterValue", "request", "2.0.1");
3258
277
      } else if (status == MS_DONE) {
3259
        /* MS_DONE means, that the exception has already been written to the IO
3260
          buffer.
3261
        */
3262
32
        msWCSFreeParamsObj20(params20);
3263
32
        return MS_FAILURE;
3264
32
      }
3265
278
    }
3266
3267
    /* check if all layer names are valid NCNames */
3268
2.27k
    for (i = 0; i < map->numlayers; ++i) {
3269
0
      if (!msWCSIsLayerSupported(map->layers[i]))
3270
0
        continue;
3271
3272
      /* Check if each layers name is a valid NCName. */
3273
0
      if (msEvalRegex("^[a-zA-z_][a-zA-Z0-9_.-]*$", map->layers[i]->name) ==
3274
0
          MS_FALSE) {
3275
0
        msSetError(MS_WCSERR, "Layer name '%s' is not a valid NCName.",
3276
0
                   "msWCSDispatch()", map->layers[i]->name);
3277
0
        msWCSFreeParamsObj20(params20);
3278
0
        return msWCSException(map, "mapserv", "Internal", "2.0.1");
3279
0
      }
3280
0
    }
3281
3282
    /* Call operation specific functions */
3283
2.27k
    if (operation == MS_WCS_GET_CAPABILITIES) {
3284
2.27k
      retVal = msWCSGetCapabilities20(map, request, params20, ows_request);
3285
2.27k
    } else if (operation == MS_WCS_DESCRIBE_COVERAGE) {
3286
0
      retVal = msWCSDescribeCoverage20(map, params20, ows_request);
3287
0
    } else if (operation == MS_WCS_GET_COVERAGE) {
3288
0
      retVal = msWCSGetCoverage20(map, request, params20, ows_request);
3289
0
    } else {
3290
0
      msSetError(MS_WCSERR, "Invalid request '%s'.", "msWCSDispatch20()",
3291
0
                 ows_request->request);
3292
0
      retVal =
3293
0
          msWCSException20(map, "InvalidParameterValue", "request", "2.0.1");
3294
0
    }
3295
    /* clean up */
3296
2.27k
    msWCSFreeParamsObj20(params20);
3297
2.27k
    return retVal;
3298
#else      /* def USE_LIBXML2 */
3299
    msSetError(MS_WCSERR,
3300
               "WCS 2.0 needs mapserver to be compiled with libxml2.",
3301
               "msWCSDispatch()");
3302
    return msWCSException(map, "mapserv", "NoApplicableCode", "2.0.1");
3303
#endif     /* def USE_LIBXML2 */
3304
2.27k
  } else { /* unsupported version */
3305
79
    msSetError(MS_WCSERR, "WCS Server does not support VERSION %s.",
3306
79
               "msWCSDispatch()", ows_request->version);
3307
79
    return msWCSException(map, "InvalidParameterValue", "version",
3308
79
                          ows_request->version);
3309
79
  }
3310
3311
#else
3312
  msSetError(MS_WCSERR, "WCS server support is not available.",
3313
             "msWCSDispatch()");
3314
  return MS_FAILURE;
3315
#endif
3316
3.71k
}
3317
3318
/************************************************************************/
3319
/*                      msWCSGetCoverageMetadata()                      */
3320
/************************************************************************/
3321
3322
#ifdef USE_WCS_SVR
3323
3324
0
void msWCSFreeCoverageMetadata(coverageMetadataObj *cm) {
3325
0
  msFree(cm->srs_epsg);
3326
0
}
3327
3328
0
int msWCSGetCoverageMetadata(layerObj *layer, coverageMetadataObj *cm) {
3329
0
  char *srs_urn = NULL;
3330
0
  int i = 0;
3331
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
3332
0
    return MS_FAILURE;
3333
3334
  /* -------------------------------------------------------------------- */
3335
  /*      Get the SRS in WCS 1.0 format (eg. EPSG:n)                      */
3336
  /* -------------------------------------------------------------------- */
3337
0
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE,
3338
0
                   &(cm->srs_epsg));
3339
0
  if (!cm->srs_epsg) {
3340
0
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
3341
0
                     "CO", MS_TRUE, &(cm->srs_epsg));
3342
0
    if (!cm->srs_epsg) {
3343
0
      msSetError(MS_WCSERR,
3344
0
                 "Unable to determine the SRS for this layer, no projection "
3345
0
                 "defined and no metadata available.",
3346
0
                 "msWCSGetCoverageMetadata()");
3347
0
      return MS_FAILURE;
3348
0
    }
3349
0
  }
3350
3351
  /* -------------------------------------------------------------------- */
3352
  /*      Get the SRS in urn format.                                      */
3353
  /* -------------------------------------------------------------------- */
3354
0
  if ((srs_urn = msOWSGetProjURN(&(layer->projection), &(layer->metadata), "CO",
3355
0
                                 MS_TRUE)) == NULL) {
3356
0
    srs_urn = msOWSGetProjURN(&(layer->map->projection),
3357
0
                              &(layer->map->web.metadata), "CO", MS_TRUE);
3358
0
  }
3359
3360
0
  if (srs_urn != NULL) {
3361
0
    if (strlen(srs_urn) > sizeof(cm->srs_urn) - 1) {
3362
0
      msSetError(MS_WCSERR, "SRS URN too long!", "msWCSGetCoverageMetadata()");
3363
0
      return MS_FAILURE;
3364
0
    }
3365
3366
0
    strcpy(cm->srs_urn, srs_urn);
3367
0
    msFree(srs_urn);
3368
0
  } else
3369
0
    cm->srs_urn[0] = '\0';
3370
3371
  /* -------------------------------------------------------------------- */
3372
  /*      If we have "virtual dataset" metadata on the layer, then use    */
3373
  /*      that in preference to inspecting the file(s).                   */
3374
  /*      We require extent and either size or resolution.                */
3375
  /* -------------------------------------------------------------------- */
3376
0
  if (msOWSLookupMetadata(&(layer->metadata), "CO", "extent") != NULL &&
3377
0
      (msOWSLookupMetadata(&(layer->metadata), "CO", "resolution") != NULL ||
3378
0
       msOWSLookupMetadata(&(layer->metadata), "CO", "size") != NULL)) {
3379
0
    const char *value;
3380
3381
    /* get extent */
3382
0
    cm->extent.minx = 0.0;
3383
0
    cm->extent.maxx = 0.0;
3384
0
    cm->extent.miny = 0.0;
3385
0
    cm->extent.maxy = 0.0;
3386
0
    if (msOWSGetLayerExtent(layer->map, layer, "CO", &cm->extent) == MS_FAILURE)
3387
0
      return MS_FAILURE;
3388
3389
    /* get resolution */
3390
0
    cm->xresolution = 0.0;
3391
0
    cm->yresolution = 0.0;
3392
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "resolution")) !=
3393
0
        NULL) {
3394
0
      char **tokens;
3395
0
      int n;
3396
3397
0
      tokens = msStringSplit(value, ' ', &n);
3398
0
      if (tokens == NULL || n != 2) {
3399
0
        msSetError(MS_WCSERR,
3400
0
                   "Wrong number of arguments for wcs|ows_resolution metadata.",
3401
0
                   "msWCSGetCoverageMetadata()");
3402
0
        msFreeCharArray(tokens, n);
3403
0
        return MS_FAILURE;
3404
0
      }
3405
0
      cm->xresolution = atof(tokens[0]);
3406
0
      cm->yresolution = atof(tokens[1]);
3407
0
      msFreeCharArray(tokens, n);
3408
0
    }
3409
3410
    /* get Size (in pixels and lines) */
3411
0
    cm->xsize = 0;
3412
0
    cm->ysize = 0;
3413
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "size")) !=
3414
0
        NULL) {
3415
0
      char **tokens;
3416
0
      int n;
3417
3418
0
      tokens = msStringSplit(value, ' ', &n);
3419
0
      if (tokens == NULL || n != 2) {
3420
0
        msSetError(MS_WCSERR,
3421
0
                   "Wrong number of arguments for wcs|ows_size metadata.",
3422
0
                   "msWCSGetCoverageDomain()");
3423
0
        msFreeCharArray(tokens, n);
3424
0
        return MS_FAILURE;
3425
0
      }
3426
0
      cm->xsize = atoi(tokens[0]);
3427
0
      cm->ysize = atoi(tokens[1]);
3428
0
      msFreeCharArray(tokens, n);
3429
0
    }
3430
3431
    /* try to compute raster size */
3432
0
    if (cm->xsize == 0 && cm->ysize == 0 && cm->xresolution != 0.0 &&
3433
0
        cm->yresolution != 0.0 && cm->extent.minx != cm->extent.maxx &&
3434
0
        cm->extent.miny != cm->extent.maxy) {
3435
0
      cm->xsize =
3436
0
          (int)((cm->extent.maxx - cm->extent.minx) / cm->xresolution + 0.5);
3437
0
      cm->ysize = (int)fabs(
3438
0
          (cm->extent.maxy - cm->extent.miny) / cm->yresolution + 0.5);
3439
0
    }
3440
3441
    /* try to compute raster resolution */
3442
0
    if ((cm->xresolution == 0.0 || cm->yresolution == 0.0) && cm->xsize != 0 &&
3443
0
        cm->ysize != 0) {
3444
0
      cm->xresolution = (cm->extent.maxx - cm->extent.minx) / cm->xsize;
3445
0
      cm->yresolution = (cm->extent.maxy - cm->extent.miny) / cm->ysize;
3446
0
    }
3447
3448
    /* do we have information to do anything */
3449
0
    if (cm->xresolution == 0.0 || cm->yresolution == 0.0 || cm->xsize == 0 ||
3450
0
        cm->ysize == 0) {
3451
0
      msSetError(MS_WCSERR,
3452
0
                 "Failed to collect extent and resolution for WCS coverage "
3453
0
                 "from metadata for layer '%s'.  Need value wcs|ows_resolution "
3454
0
                 "or wcs|ows_size values.",
3455
0
                 "msWCSGetCoverageMetadata()", layer->name);
3456
0
      return MS_FAILURE;
3457
0
    }
3458
3459
    /* compute geotransform */
3460
0
    cm->geotransform[0] = cm->extent.minx;
3461
0
    cm->geotransform[1] = cm->xresolution;
3462
0
    cm->geotransform[2] = 0.0;
3463
0
    cm->geotransform[3] = cm->extent.maxy;
3464
0
    cm->geotransform[4] = 0.0;
3465
0
    cm->geotransform[5] = -fabs(cm->yresolution);
3466
3467
    /* get bands count, or assume 1 if not found */
3468
0
    cm->bandcount = 1;
3469
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "bandcount")) !=
3470
0
        NULL) {
3471
0
      cm->bandcount = atoi(value);
3472
0
    }
3473
3474
    /* get bands type, or assume float if not found */
3475
0
    cm->imagemode = MS_IMAGEMODE_FLOAT32;
3476
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "imagemode")) !=
3477
0
        NULL) {
3478
0
      if (EQUAL(value, "INT16"))
3479
0
        cm->imagemode = MS_IMAGEMODE_INT16;
3480
0
      else if (EQUAL(value, "FLOAT32"))
3481
0
        cm->imagemode = MS_IMAGEMODE_FLOAT32;
3482
0
      else if (EQUAL(value, "BYTE"))
3483
0
        cm->imagemode = MS_IMAGEMODE_BYTE;
3484
0
      else {
3485
0
        msSetError(MS_WCSERR,
3486
0
                   "Content of wcs|ows_imagemode (%s) not recognised.  Should "
3487
0
                   "be one of BYTE, INT16 or FLOAT32.",
3488
0
                   "msWCSGetCoverageMetadata()", value);
3489
0
        return MS_FAILURE;
3490
0
      }
3491
0
    }
3492
    /* set color interpretation to undefined */
3493
    /* TODO: find better solution */
3494
0
    for (i = 0; i < 10; ++i) {
3495
0
      cm->bandinterpretation[i] = GDALGetColorInterpretationName(GCI_Undefined);
3496
0
    }
3497
0
  } else if (layer->data ==
3498
0
             NULL) { /* no virtual metadata, not ok unless we're talking 1
3499
                        image, hopefully we can fix that */
3500
0
    msSetError(
3501
0
        MS_WCSERR,
3502
0
        "RASTER Layer with no DATA statement and no WCS virtual dataset "
3503
0
        "metadata.  Tileindexed raster layers not supported for WCS without "
3504
0
        "virtual dataset metadata (cm->extent, wcs_res, wcs_size).",
3505
0
        "msWCSGetCoverageDomain()");
3506
0
    return MS_FAILURE;
3507
0
  } else { /* work from the file (e.g. DATA) */
3508
0
    GDALDatasetH hDS;
3509
0
    GDALRasterBandH hBand;
3510
0
    char szPath[MS_MAXPATHLEN];
3511
0
    char *decrypted_path;
3512
3513
0
    msGDALInitialize();
3514
3515
0
    msTryBuildPath3(szPath, layer->map->mappath, layer->map->shapepath,
3516
0
                    layer->data);
3517
0
    decrypted_path = msDecryptStringTokens(layer->map, szPath);
3518
0
    if (!decrypted_path)
3519
0
      return MS_FAILURE;
3520
3521
0
    msAcquireLock(TLOCK_GDAL);
3522
0
    {
3523
0
      char **connectionoptions =
3524
0
          msGetStringListFromHashTable(&(layer->connectionoptions));
3525
0
      hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
3526
0
                       (const char *const *)connectionoptions, NULL);
3527
0
      CSLDestroy(connectionoptions);
3528
0
    }
3529
0
    if (hDS == NULL) {
3530
0
      const char *cpl_error_msg = CPLGetLastErrorMsg();
3531
3532
      /* we wish to avoid reporting decrypted paths */
3533
0
      if (cpl_error_msg != NULL &&
3534
0
          strstr(cpl_error_msg, decrypted_path) != NULL &&
3535
0
          strcmp(decrypted_path, szPath) != 0)
3536
0
        cpl_error_msg = NULL;
3537
3538
0
      if (cpl_error_msg == NULL)
3539
0
        cpl_error_msg = "";
3540
3541
0
      msReleaseLock(TLOCK_GDAL);
3542
3543
0
      msSetError(MS_IOERR, "%s", "msWCSGetCoverageMetadata()", cpl_error_msg);
3544
3545
0
      msFree(decrypted_path);
3546
0
      return MS_FAILURE;
3547
0
    }
3548
0
    msFree(decrypted_path);
3549
3550
0
    msGetGDALGeoTransform(hDS, layer->map, layer, cm->geotransform);
3551
3552
0
    cm->xsize = GDALGetRasterXSize(hDS);
3553
0
    cm->ysize = GDALGetRasterYSize(hDS);
3554
3555
0
    cm->extent.minx = cm->geotransform[0];
3556
0
    cm->extent.maxx = cm->geotransform[0] + cm->geotransform[1] * cm->xsize +
3557
0
                      cm->geotransform[2] * cm->ysize;
3558
0
    cm->extent.miny = cm->geotransform[3] + cm->geotransform[4] * cm->xsize +
3559
0
                      cm->geotransform[5] * cm->ysize;
3560
0
    cm->extent.maxy = cm->geotransform[3];
3561
3562
0
    cm->xresolution = cm->geotransform[1];
3563
0
    cm->yresolution = cm->geotransform[5];
3564
3565
    /* TODO: need to set resolution */
3566
3567
0
    cm->bandcount = GDALGetRasterCount(hDS);
3568
3569
0
    if (cm->bandcount == 0) {
3570
0
      msReleaseLock(TLOCK_GDAL);
3571
0
      msSetError(MS_WCSERR,
3572
0
                 "Raster file %s has no raster bands.  This cannot be used in "
3573
0
                 "a layer.",
3574
0
                 "msWCSGetCoverageMetadata()", layer->data);
3575
0
      return MS_FAILURE;
3576
0
    }
3577
3578
0
    hBand = GDALGetRasterBand(hDS, 1);
3579
0
    switch (GDALGetRasterDataType(hBand)) {
3580
0
    case GDT_Byte:
3581
0
      cm->imagemode = MS_IMAGEMODE_BYTE;
3582
0
      break;
3583
0
    case GDT_Int16:
3584
0
      cm->imagemode = MS_IMAGEMODE_INT16;
3585
0
      break;
3586
0
    default:
3587
0
      cm->imagemode = MS_IMAGEMODE_FLOAT32;
3588
0
      break;
3589
0
    }
3590
3591
    /* color interpretation */
3592
0
    for (i = 1; i <= 10 && i <= cm->bandcount; ++i) {
3593
0
      GDALColorInterp colorInterp;
3594
0
      hBand = GDALGetRasterBand(hDS, i);
3595
0
      colorInterp = GDALGetRasterColorInterpretation(hBand);
3596
0
      cm->bandinterpretation[i - 1] =
3597
0
          GDALGetColorInterpretationName(colorInterp);
3598
0
    }
3599
3600
0
    GDALClose(hDS);
3601
0
    msReleaseLock(TLOCK_GDAL);
3602
0
  }
3603
3604
  /* we must have the bounding box in lat/lon [WGS84(DD)/EPSG:4326] */
3605
0
  cm->llextent = cm->extent;
3606
3607
  /* Already in latlong .. use directly. */
3608
0
  if (layer->projection.proj != NULL &&
3609
0
      msProjIsGeographicCRS(&(layer->projection))) {
3610
    /* no change */
3611
0
  }
3612
3613
0
  else if (layer->projection.numargs > 0 &&
3614
0
           !msProjIsGeographicCRS(
3615
0
               &(layer->projection))) /* check the layer projection */
3616
0
    msProjectRect(&(layer->projection), NULL, &(cm->llextent));
3617
3618
0
  else if (layer->map->projection.numargs > 0 &&
3619
0
           !msProjIsGeographicCRS(
3620
0
               &(layer->map->projection))) /* check the map projection */
3621
0
    msProjectRect(&(layer->map->projection), NULL, &(cm->llextent));
3622
3623
0
  else { /* projection was specified in the metadata only (EPSG:... only at the
3624
            moment)  */
3625
0
    projectionObj proj;
3626
0
    char projstring[32];
3627
3628
0
    msInitProjection(&proj); /* or bad things happen */
3629
0
    msProjectionInheritContextFrom(&proj, &(layer->map->projection));
3630
3631
0
    snprintf(projstring, sizeof(projstring), "init=epsg:%.20s",
3632
0
             cm->srs_epsg + 5);
3633
0
    if (msLoadProjectionString(&proj, projstring) != 0) {
3634
0
      msFreeProjection(&proj);
3635
0
      return MS_FAILURE;
3636
0
    }
3637
0
    msProjectRect(&proj, NULL, &(cm->llextent));
3638
0
  }
3639
3640
0
  return MS_SUCCESS;
3641
0
}
3642
#endif /* def USE_WCS_SVR */