• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MapServer / MapServer / 24101978785

07 Apr 2026 08:07PM UTC coverage: 42.423% (+0.09%) from 42.336%
24101978785

push

github

web-flow
Add support for non-EPSG projections for WCS and WFS protocols (#7457)

* Support AUTH:CODE projections

* Remove duplicate param

* Allow additional authority codes in the wfs_srs list

* Handle custom init files

* Move parameter and make static

* Add back legacy support for urn:EPSG:geographicCRS:CODE

* Tests updated due to new projection handling

* Test updates

* Add new line

* Handle custom projection files and new style proj codes

* EOL fix

* Handle projections in the form urn:ogc:def:crs:ESRI::53009

* Add WCS tests

* Update authorities based on proj.db

* Add projection string tests

* EOL fixes

* Add test data

* Copy custom projection file

* Remove extent

* Add WFS tests

* Update encodings

* Add custom projection tests for WCS

* Remove DATA

* Test fixes

* Switch result to ASCII

* Use backslashes

* Add test for IGNF:ATIGBONNE.BOURD and update IAU_2015

* Appveyor fix

* EOL fix

* Avoid adding + to AUTH:CODE projection strings

* Always assume PROJ_VERSION_MAJOR >= 6

* Use std::string and avoid strlcpy

102 of 120 new or added lines in 5 files covered. (85.0%)

15 existing lines in 5 files now uncovered.

64591 of 152256 relevant lines covered (42.42%)

27322.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.31
/src/mapwcs.cpp
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,
2✔
53
                                      const char *value) {
54
  char **allowed_ri_values;
55
  char **client_ri_values;
56
  int allowed_count, client_count;
57
  int i_client, i, all_match = 1;
58
  char *tmpname = NULL;
59
  const char *ri_values_list;
60

61
  if (name == NULL)
2✔
62
    return MS_FAILURE;
63

64
  /* Fetch the available values list for the rangeset item and tokenize */
65
  const size_t nSize = strlen(name) + strlen("_values") + 1;
2✔
66
  tmpname = (char *)msSmallMalloc(nSize);
2✔
67
  snprintf(tmpname, nSize, "%s_values", name);
68
  ri_values_list = msOWSLookupMetadata(&(lp->metadata), "CO", tmpname);
2✔
69
  msFree(tmpname);
2✔
70

71
  if (ri_values_list == NULL)
2✔
72
    return MS_FAILURE;
73

74
  allowed_ri_values = msStringSplit(ri_values_list, ',', &allowed_count);
2✔
75

76
  /* Parse the client value list into tokens. */
77
  client_ri_values = msStringSplit(value, ',', &client_count);
2✔
78

79
  /* test each client value against the allowed list. */
80

81
  for (i_client = 0; all_match && i_client < client_count; i_client++) {
6✔
82
    for (i = 0; i < allowed_count && strcasecmp(client_ri_values[i_client],
17✔
83
                                                allowed_ri_values[i]) != 0;
16✔
84
         i++) {
85
    }
86

87
    if (i == allowed_count)
4✔
88
      all_match = 0;
89
  }
90

91
  msFreeCharArray(allowed_ri_values, allowed_count);
2✔
92
  msFreeCharArray(client_ri_values, client_count);
2✔
93

94
  if (all_match == 0)
2✔
95
    return MS_FAILURE;
96
  else
97
    return MS_SUCCESS;
98
}
99

100
/************************************************************************/
101
/*                    msWCSConvertRangeSetToString()                    */
102
/************************************************************************/
103
static char *msWCSConvertRangeSetToString(const char *value) {
1✔
104
  char **tokens;
105
  int numtokens;
106
  double min, max, res;
107
  double val;
108
  char buf1[128], *buf2 = NULL;
109

110
  if (strchr(value, '/')) { /* value is min/max/res */
1✔
111
    tokens = msStringSplit(value, '/', &numtokens);
×
112
    if (tokens == NULL || numtokens != 3) {
×
113
      msFreeCharArray(tokens, numtokens);
×
114
      return NULL; /* not a set of equally spaced intervals */
×
115
    }
116

117
    min = atof(tokens[0]);
×
118
    max = atof(tokens[1]);
×
119
    res = atof(tokens[2]);
×
120
    msFreeCharArray(tokens, numtokens);
×
121

122
    for (val = min; val <= max; val += res) {
×
123
      if (val == min)
×
124
        snprintf(buf1, sizeof(buf1), "%g", val);
125
      else
126
        snprintf(buf1, sizeof(buf1), ",%g", val);
127
      buf2 = msStringConcatenate(buf2, buf1);
×
128
    }
129

130
    return buf2;
×
131
  } else
132
    return msStrdup(value);
1✔
133
}
134

135
/************************************************************************/
136
/*                           msWCSException()                           */
137
/************************************************************************/
138
int msWCSException(mapObj *map, const char *code, const char *locator,
52✔
139
                   const char *version) {
140
  char *pszEncodedVal = NULL;
141
  char version_string[OWS_VERSION_MAXLEN];
142

143
  if (version == NULL)
52✔
144
    version = "1.0.0";
145

146
#if defined(USE_LIBXML2)
147
  if (msOWSParseVersionString(version) >= OWS_2_0_0)
52✔
148
    return msWCSException20(
33✔
149
        map, code, locator,
150
        msOWSGetVersionString(msOWSParseVersionString(version),
151
                              version_string));
152
#endif
153

154
  if (msOWSParseVersionString(version) >= OWS_1_1_0)
19✔
155
    return msWCSException11(
7✔
156
        map, code, locator,
157
        msOWSGetVersionString(msOWSParseVersionString(version),
158
                              version_string));
159

160
  msIO_setHeader("Content-Type", "application/vnd.ogc.se_xml; charset=UTF-8");
12✔
161
  msIO_sendHeaders();
12✔
162

163
  /* msIO_printf("Content-Type: text/xml%c%c",10,10); */
164

165
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
12✔
166

167
  msIO_printf("<ServiceExceptionReport version=\"1.2.0\"\n");
12✔
168
  msIO_printf("xmlns=\"http://www.opengis.net/ogc\" ");
12✔
169
  msIO_printf("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
12✔
170
  pszEncodedVal = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
12✔
171
  msIO_printf("xsi:schemaLocation=\"http://www.opengis.net/ogc "
12✔
172
              "%s/wcs/1.0.0/OGC-exception.xsd\">\n",
173
              pszEncodedVal);
174
  msFree(pszEncodedVal);
12✔
175
  msIO_printf("  <ServiceException");
12✔
176
  if (code) {
12✔
177
    msIO_printf(" code=\"%s\"", code);
12✔
178
  }
179
  if (locator) {
12✔
180
    msIO_printf(" locator=\"%s\"", locator);
12✔
181
  }
182
  msIO_printf(">");
12✔
183
  msWriteErrorXML(stdout);
12✔
184
  msIO_printf("  </ServiceException>\n");
12✔
185
  msIO_printf("</ServiceExceptionReport>\n");
12✔
186

187
  msResetErrorList();
12✔
188

189
  return MS_FAILURE;
190
}
191

192
/************************************************************************/
193
/*                    msWCSPrintRequestCapability()                     */
194
/************************************************************************/
195

196
static void msWCSPrintRequestCapability(const char *request_tag,
32✔
197
                                        const char *script_url) {
198
  msIO_printf("    <%s>\n", request_tag);
32✔
199

200
  msIO_printf("      <DCPType>\n");
32✔
201
  msIO_printf("        <HTTP>\n");
32✔
202
  msIO_printf("          <Get><OnlineResource xlink:type=\"simple\" "
32✔
203
              "xlink:href=\"%s\" /></Get>\n",
204
              script_url);
205
  msIO_printf("        </HTTP>\n");
32✔
206
  msIO_printf("      </DCPType>\n");
32✔
207
  msIO_printf("      <DCPType>\n");
32✔
208
  msIO_printf("        <HTTP>\n");
32✔
209
  msIO_printf("          <Post><OnlineResource xlink:type=\"simple\" "
32✔
210
              "xlink:href=\"%s\" /></Post>\n",
211
              script_url);
212
  msIO_printf("        </HTTP>\n");
32✔
213
  msIO_printf("      </DCPType>\n");
32✔
214

215
  msIO_printf("    </%s>\n", request_tag);
32✔
216
}
32✔
217

218
/************************************************************************/
219
/*                         msWCSCreateParams()                          */
220
/************************************************************************/
221
static wcsParamsObj *msWCSCreateParams() {
89✔
222
  wcsParamsObj *params;
223

224
  params = (wcsParamsObj *)calloc(1, sizeof(wcsParamsObj));
89✔
225
  MS_CHECK_ALLOC(params, sizeof(wcsParamsObj), NULL);
89✔
226

227
  return params;
228
}
229

230
/************************************************************************/
231
/*                          msWCSFreeParams()                           */
232
/************************************************************************/
233
void msWCSFreeParams(wcsParamsObj *params) {
89✔
234
  if (params) {
89✔
235
    /* TODO */
236
    if (params->version)
89✔
237
      free(params->version);
89✔
238
    if (params->updatesequence)
89✔
239
      free(params->updatesequence);
20✔
240
    if (params->request)
89✔
241
      free(params->request);
89✔
242
    if (params->service)
89✔
243
      free(params->service);
89✔
244
    if (params->section)
89✔
245
      free(params->section);
7✔
246
    if (params->crs)
89✔
247
      free(params->crs);
47✔
248
    if (params->response_crs)
89✔
249
      free(params->response_crs);
2✔
250
    if (params->format)
89✔
251
      free(params->format);
46✔
252
    if (params->exceptions)
89✔
253
      free(params->exceptions);
×
254
    if (params->time)
89✔
255
      free(params->time);
1✔
256
    if (params->interpolation)
89✔
257
      free(params->interpolation);
2✔
258
    CSLDestroy(params->coverages);
89✔
259
  }
260
}
89✔
261

262
/************************************************************************/
263
/*                       msWCSIsLayerSupported()                        */
264
/************************************************************************/
265

266
int msWCSIsLayerSupported(layerObj *layer) {
395✔
267
  /* only raster layers, are elligible to be served via WCS, WMS rasters are not
268
   * ok */
269
  if ((layer->type == MS_LAYER_RASTER) && layer->connectiontype != MS_WMS &&
395✔
270
      layer->name != NULL)
355✔
271
    return MS_TRUE;
355✔
272

273
  return MS_FALSE;
274
}
275

276
/************************************************************************/
277
/*                      msWCSGetRequestParameter()                      */
278
/*                                                                      */
279
/************************************************************************/
280

281
const char *msWCSGetRequestParameter(cgiRequestObj *request, const char *name) {
40✔
282
  int i;
283

284
  if (!request || !name) /* nothing to do */
40✔
285
    return NULL;
286

287
  if (request->NumParams > 0) {
40✔
288
    for (i = 0; i < request->NumParams; i++) {
376✔
289
      if (strcasecmp(request->ParamNames[i], name) == 0)
345✔
290
        return request->ParamValues[i];
2✔
291
    }
292
  }
293

294
  return NULL;
295
}
296

297
/************************************************************************/
298
/*                  msWCSSetDefaultBandsRangeSetInfo()                  */
299
/************************************************************************/
300

301
void msWCSSetDefaultBandsRangeSetInfo(wcsParamsObj *params,
53✔
302
                                      coverageMetadataObj *cm, layerObj *lp) {
303
  (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
  const char *value;
312
  char *bandlist;
313
  size_t bufferSize = 0;
314
  int i;
315

316
  /* Does this item exist in the axes list?  */
317

318
  value = msOWSLookupMetadata(&(lp->metadata), "CO", "rangeset_axes");
53✔
319
  if (value == NULL)
53✔
320
    return;
321

322
  value = strstr(value, "bands");
323
  if (value == NULL || (value[5] != '\0' && value[5] != ' '))
42✔
324
    return;
325

326
  /* Are there any w*s_bands_ metadata already? If so, skip out. */
327
  if (msOWSLookupMetadata(&(lp->metadata), "CO", "bands_description") != NULL ||
84✔
328
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_name") != NULL ||
84✔
329
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_label") != NULL ||
84✔
330
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values") != NULL ||
84✔
331
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values_semantic") !=
42✔
332
          NULL ||
42✔
333
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values_type") != NULL ||
84✔
334
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_rangeitem") != NULL ||
84✔
335
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_semantic") != NULL ||
84✔
336
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_refsys") != NULL ||
84✔
337
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_refsyslabel") != NULL ||
126✔
338
      msOWSLookupMetadata(&(lp->metadata), "CO", "bands_interval") != NULL)
42✔
339
    return;
×
340

341
  /* OK, we have decided to fill in the information. */
342

343
  msInsertHashTable(&(lp->metadata), "wcs_bands_name", "bands");
42✔
344
  msInsertHashTable(&(lp->metadata), "wcs_bands_label",
42✔
345
                    "Bands/Channels/Samples");
346
  msInsertHashTable(&(lp->metadata), "wcs_bands_rangeitem", "_bands"); /* ? */
42✔
347

348
  bufferSize = cm->bandcount * 30 + 30;
42✔
349
  bandlist = (char *)msSmallMalloc(bufferSize);
42✔
350
  strcpy(bandlist, "1");
351
  for (i = 1; i < cm->bandcount; i++)
50✔
352
    snprintf(bandlist + strlen(bandlist), bufferSize - strlen(bandlist), ",%d",
8✔
353
             i + 1);
354

355
  msInsertHashTable(&(lp->metadata), "wcs_bands_values", bandlist);
42✔
356
  free(bandlist);
42✔
357
}
358

359
/************************************************************************/
360
/*                         msWCSParseRequest()                          */
361
/************************************************************************/
362

363
static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
89✔
364
                             mapObj *map) {
365
  int i, n;
366
  char **tokens;
367

368
  if (!request || !params) /* nothing to do */
89✔
369
    return MS_SUCCESS;
370

371
  /* -------------------------------------------------------------------- */
372
  /*      Check if this appears to be an XML POST WCS request.            */
373
  /* -------------------------------------------------------------------- */
374

375
  msDebug("msWCSParseRequest(): request is %s.\n",
89✔
376
          (request->type == MS_POST_REQUEST) ? "POST" : "KVP");
89✔
377

378
  if (request->type == MS_POST_REQUEST && request->postrequest) {
89✔
379
#if defined(USE_LIBXML2)
380
    xmlDocPtr doc = NULL;
381
    xmlNodePtr root = NULL, child = NULL;
382
    char *tmp = NULL;
383

384
    /* parse to DOM-Structure and get root element */
385
    if ((doc = xmlParseMemory(request->postrequest,
17✔
386
                              strlen(request->postrequest))) == NULL) {
17✔
387
      const xmlError *error = xmlGetLastError();
×
388
      msSetError(MS_WCSERR, "XML parsing error: %s", "msWCSParseRequest()",
×
389
                 error->message);
×
390
      return MS_FAILURE;
×
391
    }
392
    root = xmlDocGetRootElement(doc);
17✔
393

394
    /* Get service, version and request from root */
395
    params->request = msStrdup((char *)root->name);
17✔
396
    if ((tmp = (char *)xmlGetProp(root, BAD_CAST "service")) != NULL)
17✔
397
      params->service = tmp;
17✔
398
    if ((tmp = (char *)xmlGetProp(root, BAD_CAST "version")) != NULL)
17✔
399
      params->version = tmp;
17✔
400

401
    /* search first level children, either CoverageID,  */
402
    for (child = root->children; child != NULL; child = child->next) {
50✔
403
      if (EQUAL((char *)child->name, "AcceptVersions")) {
33✔
404
        /* will be overridden to 1.1.1 anyway */
405
      } else if (EQUAL((char *)child->name, "UpdateSequence")) {
33✔
406
        params->updatesequence = (char *)xmlNodeGetContent(child);
3✔
407
      } else if (EQUAL((char *)child->name, "Sections")) {
30✔
408
        xmlNodePtr sectionNode = NULL;
409
        /* concatenate all sections by ',' */
410
        for (sectionNode = child->children; sectionNode != NULL;
8✔
411
             sectionNode = sectionNode->next) {
5✔
412
          char *content;
413
          if (!EQUAL((char *)sectionNode->name, "Section"))
5✔
414
            continue;
×
415
          content = (char *)xmlNodeGetContent(sectionNode);
5✔
416
          if (!params->section) {
5✔
417
            params->section = content;
3✔
418
          } else {
419
            params->section = msStringConcatenate(params->section, ",");
2✔
420
            params->section = msStringConcatenate(params->section, content);
2✔
421
            xmlFree(content);
2✔
422
          }
423
        }
424
      } else if (EQUAL((char *)child->name, "AcceptFormats")) {
27✔
425
        /* TODO: implement */
426
      } else if (EQUAL((char *)child->name, "Identifier")) {
27✔
427
        char *content = (char *)xmlNodeGetContent(child);
9✔
428
        params->coverages = CSLAddString(params->coverages, content);
9✔
429
        xmlFree(content);
9✔
430
      } else if (EQUAL((char *)child->name, "DomainSubset")) {
18✔
431
        xmlNodePtr tmpNode = NULL;
432
        for (tmpNode = child->children; tmpNode != NULL;
18✔
433
             tmpNode = tmpNode->next) {
9✔
434
          if (EQUAL((char *)tmpNode->name, "BoundingBox")) {
9✔
435
            xmlNodePtr cornerNode = NULL;
436
            params->crs = (char *)xmlGetProp(tmpNode, BAD_CAST "crs");
9✔
437
            if (strncasecmp(params->crs, "urn:ogc:def:crs:", 16) == 0 &&
9✔
438
                strncasecmp(params->crs + strlen(params->crs) - 8, "imageCRS",
9✔
439
                            8) == 0)
440
              strcpy(params->crs, "imageCRS");
441
            for (cornerNode = tmpNode->children; cornerNode != NULL;
27✔
442
                 cornerNode = cornerNode->next) {
18✔
443
              if (EQUAL((char *)cornerNode->name, "LowerCorner")) {
18✔
444
                char *value = (char *)xmlNodeGetContent(cornerNode);
9✔
445
                tokens = msStringSplit(value, ' ', &n);
9✔
446
                if (tokens == NULL || n < 2) {
9✔
447
                  msSetError(MS_WCSERR,
×
448
                             "Wrong number of arguments for LowerCorner",
449
                             "msWCSParseRequest()");
450
                  return msWCSException(map, "InvalidParameterValue",
×
451
                                        "LowerCorner", params->version);
×
452
                }
453
                params->bbox.minx = atof(tokens[0]);
9✔
454
                params->bbox.miny = atof(tokens[1]);
9✔
455
                msFreeCharArray(tokens, n);
9✔
456
                xmlFree(value);
9✔
457
              }
458
              if (EQUAL((char *)cornerNode->name, "UpperCorner")) {
18✔
459
                char *value = (char *)xmlNodeGetContent(cornerNode);
9✔
460
                tokens = msStringSplit(value, ' ', &n);
9✔
461
                if (tokens == NULL || n < 2) {
9✔
462
                  msSetError(MS_WCSERR,
×
463
                             "Wrong number of arguments for UpperCorner",
464
                             "msWCSParseRequest()");
465
                  return msWCSException(map, "InvalidParameterValue",
×
466
                                        "UpperCorner", params->version);
×
467
                }
468
                params->bbox.maxx = atof(tokens[0]);
9✔
469
                params->bbox.maxy = atof(tokens[1]);
9✔
470
                msFreeCharArray(tokens, n);
9✔
471
                xmlFree(value);
9✔
472
              }
473
            }
474
          }
475
        }
476
      } else if (EQUAL((char *)child->name, "RangeSubset")) {
9✔
477
        /* TODO: not implemented in mapserver WCS 1.1? */
478
      } else if (EQUAL((char *)child->name, "Output")) {
9✔
479
        xmlNodePtr tmpNode = NULL;
480
        params->format = (char *)xmlGetProp(child, BAD_CAST "format");
9✔
481
        for (tmpNode = child->children; tmpNode != NULL;
18✔
482
             tmpNode = tmpNode->next) {
9✔
483
          if (EQUAL((char *)tmpNode->name, "GridCRS")) {
9✔
484
            xmlNodePtr crsNode = NULL;
485
            for (crsNode = tmpNode->children; crsNode != NULL;
45✔
486
                 crsNode = crsNode->next) {
36✔
487
              if (EQUAL((char *)crsNode->name, "GridBaseCRS")) {
36✔
488
                params->response_crs = (char *)xmlNodeGetContent(crsNode);
×
489
              } else if (EQUAL((char *)crsNode->name, "GridOrigin")) {
36✔
490
                char *value = (char *)xmlNodeGetContent(crsNode);
9✔
491
                tokens = msStringSplit(value, ' ', &n);
9✔
492
                if (tokens == NULL || n < 2) {
9✔
493
                  msSetError(MS_WCSERR,
×
494
                             "Wrong number of arguments for GridOrigin",
495
                             "msWCSParseRequest()");
496
                  return msWCSException(map, "InvalidParameterValue",
×
497
                                        "GridOffsets", params->version);
×
498
                }
499
                params->originx = atof(tokens[0]);
9✔
500
                params->originy = atof(tokens[1]);
9✔
501
                msFreeCharArray(tokens, n);
9✔
502
                xmlFree(value);
9✔
503
              } else if (EQUAL((char *)crsNode->name, "GridOffsets")) {
27✔
504
                char *value = (char *)xmlNodeGetContent(crsNode);
9✔
505
                tokens = msStringSplit(value, ' ', &n);
9✔
506
                if (tokens == NULL || n < 2) {
9✔
507
                  msSetError(MS_WCSERR,
×
508
                             "Wrong number of arguments for GridOffsets",
509
                             "msWCSParseRequest()");
510
                  return msWCSException(map, "InvalidParameterValue",
×
511
                                        "GridOffsets", params->version);
×
512
                }
513
                /* take absolute values to convert to positive RESX/RESY style
514
                WCS 1.0 behavior.  *but* this does break some possibilities! */
515
                params->resx = fabs(atof(tokens[0]));
9✔
516
                params->resy = fabs(atof(tokens[1]));
9✔
517
                msFreeCharArray(tokens, n);
9✔
518
                xmlFree(value);
9✔
519
              }
520
            }
521
          }
522
        }
523
      }
524
    }
525
    xmlFreeDoc(doc);
17✔
526
    xmlCleanupParser();
17✔
527
    return MS_SUCCESS;
17✔
528
#else  /* defined(USE_LIBXML2) */
529
    msSetError(MS_WCSERR,
530
               "To enable POST requests, MapServer has to "
531
               "be compiled with libxml2.",
532
               "msWCSParseRequest()");
533
    return MS_FAILURE;
534
#endif /* defined(USE_LIBXML2) */
535
  }
536

537
  /* -------------------------------------------------------------------- */
538
  /*      Extract WCS KVP Parameters.                                     */
539
  /* -------------------------------------------------------------------- */
540
  if (request->NumParams > 0) {
72✔
541
    for (i = 0; i < request->NumParams; i++) {
626✔
542

543
      if (strcasecmp(request->ParamNames[i], "VERSION") == 0)
554✔
544
        params->version = msStrdup(request->ParamValues[i]);
72✔
545
      if (strcasecmp(request->ParamNames[i], "UPDATESEQUENCE") == 0)
554✔
546
        params->updatesequence = msStrdup(request->ParamValues[i]);
6✔
547
      else if (strcasecmp(request->ParamNames[i], "REQUEST") == 0)
548✔
548
        params->request = msStrdup(request->ParamValues[i]);
72✔
549
      else if (strcasecmp(request->ParamNames[i], "INTERPOLATION") == 0)
476✔
550
        params->interpolation = msStrdup(request->ParamValues[i]);
2✔
551
      else if (strcasecmp(request->ParamNames[i], "SERVICE") == 0)
474✔
552
        params->service = msStrdup(request->ParamValues[i]);
72✔
553
      else if (strcasecmp(request->ParamNames[i], "SECTION") == 0) /* 1.0 */
402✔
554
        params->section =
1✔
555
            msStrdup(request->ParamValues[i]); /* TODO: validate value here */
1✔
556
      else if (strcasecmp(request->ParamNames[i], "SECTIONS") == 0) /* 1.1 */
401✔
557
        params->section =
3✔
558
            msStrdup(request->ParamValues[i]); /* TODO: validate value here */
3✔
559

560
      /* GetCoverage parameters. */
561
      else if (strcasecmp(request->ParamNames[i], "BBOX") == 0) {
398✔
562
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
24✔
563
        if (tokens == NULL || n != 4) {
24✔
564
          msSetError(MS_WCSERR, "Wrong number of arguments for BBOX.",
×
565
                     "msWCSParseRequest()");
566
          return msWCSException(map, "InvalidParameterValue", "bbox",
×
567
                                params->version);
×
568
        }
569
        params->bbox.minx = atof(tokens[0]);
24✔
570
        params->bbox.miny = atof(tokens[1]);
24✔
571
        params->bbox.maxx = atof(tokens[2]);
24✔
572
        params->bbox.maxy = atof(tokens[3]);
24✔
573

574
        msFreeCharArray(tokens, n);
24✔
575
      } else if (strcasecmp(request->ParamNames[i], "RESX") == 0)
374✔
576
        params->resx = atof(request->ParamValues[i]);
6✔
577
      else if (strcasecmp(request->ParamNames[i], "RESY") == 0)
368✔
578
        params->resy = atof(request->ParamValues[i]);
6✔
579
      else if (strcasecmp(request->ParamNames[i], "WIDTH") == 0)
362✔
580
        params->width = atoi(request->ParamValues[i]);
20✔
581
      else if (strcasecmp(request->ParamNames[i], "HEIGHT") == 0)
342✔
582
        params->height = atoi(request->ParamValues[i]);
20✔
583
      else if (strcasecmp(request->ParamNames[i], "COVERAGE") == 0)
322✔
584
        params->coverages =
29✔
585
            CSLAddString(params->coverages, request->ParamValues[i]);
29✔
586
      else if (strcasecmp(request->ParamNames[i], "TIME") == 0)
293✔
587
        params->time = msStrdup(request->ParamValues[i]);
1✔
588
      else if (strcasecmp(request->ParamNames[i], "FORMAT") == 0)
292✔
589
        params->format = msStrdup(request->ParamValues[i]);
38✔
590
      else if (strcasecmp(request->ParamNames[i], "CRS") == 0)
254✔
591
        params->crs = msStrdup(request->ParamValues[i]);
24✔
592
      else if (strcasecmp(request->ParamNames[i], "RESPONSE_CRS") == 0)
230✔
593
        params->response_crs = msStrdup(request->ParamValues[i]);
2✔
594

595
      /* WCS 1.1 DescribeCoverage and GetCoverage ... */
596
      else if (strcasecmp(request->ParamNames[i], "IDENTIFIER") == 0 ||
228✔
597
               strcasecmp(request->ParamNames[i], "IDENTIFIERS") == 0) {
212✔
598
        msDebug("msWCSParseRequest(): Whole String: %s\n",
16✔
599
                request->ParamValues[i]);
16✔
600
        params->coverages =
16✔
601
            CSLAddString(params->coverages, request->ParamValues[i]);
16✔
602
      }
603
      /* WCS 1.1 style BOUNDINGBOX */
604
      else if (strcasecmp(request->ParamNames[i], "BOUNDINGBOX") == 0) {
212✔
605
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
14✔
606
        if (tokens == NULL || n < 5) {
14✔
607
          msSetError(MS_WCSERR, "Wrong number of arguments for BOUNDINGBOX.",
×
608
                     "msWCSParseRequest()");
609
          return msWCSException(map, "InvalidParameterValue", "boundingbox",
×
610
                                params->version);
×
611
        }
612

613
        /* NOTE: WCS 1.1 boundingbox is center of pixel oriented, not edge
614
           like in WCS 1.0.  So bbox semantics are wonky till this is fixed
615
           later in the GetCoverage processing. */
616
        params->bbox.minx = atof(tokens[0]);
14✔
617
        params->bbox.miny = atof(tokens[1]);
14✔
618
        params->bbox.maxx = atof(tokens[2]);
14✔
619
        params->bbox.maxy = atof(tokens[3]);
14✔
620

621
        params->crs = msStrdup(tokens[4]);
14✔
622
        msFreeCharArray(tokens, n);
14✔
623
        /* normalize imageCRS urns to simply "imageCRS" */
624
        if (strncasecmp(params->crs, "urn:ogc:def:crs:", 16) == 0 &&
14✔
625
            strncasecmp(params->crs + strlen(params->crs) - 8, "imageCRS", 8) ==
14✔
626
                0)
627
          strcpy(params->crs, "imageCRS");
628
      } else if (strcasecmp(request->ParamNames[i], "GridOffsets") == 0) {
198✔
629
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
12✔
630
        if (tokens == NULL || n < 2) {
12✔
631
          msSetError(MS_WCSERR, "Wrong number of arguments for GridOffsets",
×
632
                     "msWCSParseRequest()");
633
          return msWCSException(map, "InvalidParameterValue", "GridOffsets",
×
634
                                params->version);
×
635
        }
636
        /* take absolute values to convert to positive RESX/RESY style
637
           WCS 1.0 behavior.  *but* this does break some possibilities! */
638
        params->resx = fabs(atof(tokens[0]));
12✔
639
        params->resy = fabs(atof(tokens[1]));
12✔
640
        msFreeCharArray(tokens, n);
12✔
641
      } else if (strcasecmp(request->ParamNames[i], "GridOrigin") == 0) {
186✔
642
        tokens = msStringSplit(request->ParamValues[i], ',', &n);
12✔
643
        if (tokens == NULL || n < 2) {
12✔
644
          msSetError(MS_WCSERR, "Wrong number of arguments for GridOrigin",
×
645
                     "msWCSParseRequest()");
646
          return msWCSException(map, "InvalidParameterValue", "GridOffsets",
×
647
                                params->version);
×
648
        }
649
        params->originx = atof(tokens[0]);
12✔
650
        params->originy = atof(tokens[1]);
12✔
651
        msFreeCharArray(tokens, n);
12✔
652
      }
653

654
      /* and so on... */
655
    }
656
  }
657
  /* we are not dealing with an XML encoded request at this point */
658
  return MS_SUCCESS;
659
}
660

661
/************************************************************************/
662
/*           msWCSGetCapabilities_Service_ResponsibleParty()            */
663
/************************************************************************/
664

665
static void msWCSGetCapabilities_Service_ResponsibleParty(mapObj *map) {
11✔
666
  int bEnableTelephone = MS_FALSE, bEnableAddress = MS_FALSE,
667
      bEnableOnlineResource = MS_FALSE;
668

669
  /* the WCS-specific way */
670
  if (msOWSLookupMetadata(&(map->web.metadata), "CO",
11✔
671
                          "responsibleparty_individualname") ||
22✔
672
      msOWSLookupMetadata(&(map->web.metadata), "CO",
11✔
673
                          "responsibleparty_organizationname")) {
674

675
    msIO_printf("<responsibleParty>\n");
×
676
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
×
677
                             "responsibleparty_individualname", OWS_NOERR,
678
                             "    <individualName>%s</individualName>\n", NULL);
679
    msOWSPrintEncodeMetadata(
×
680
        stdout, &(map->web.metadata), "CO", "responsibleparty_organizationname",
681
        OWS_NOERR, "    <organisationName>%s</organisationName>\n", NULL);
682
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
×
683
                             "responsibleparty_positionname", OWS_NOERR,
684
                             "    <positionName>%s</positionName>\n", NULL);
685

686
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
×
687
                            "responsibleparty_phone_voice") ||
×
688
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
689
                            "responsibleparty_phone_facsimile"))
690
      bEnableTelephone = MS_TRUE;
691

692
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
×
693
                            "responsibleparty_address_deliverypoint") ||
×
694
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
695
                            "responsibleparty_address_city") ||
×
696
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
697
                            "responsibleparty_address_administrativearea") ||
×
698
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
699
                            "responsibleparty_address_postalcode") ||
×
700
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
701
                            "responsibleparty_address_country") ||
×
702
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
703
                            "responsibleparty_address_electronicmailaddress"))
704
      bEnableAddress = MS_TRUE;
705

706
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
×
707
                            "responsibleparty_onlineresource"))
708
      bEnableOnlineResource = MS_TRUE;
709

710
    if (bEnableTelephone || bEnableAddress || bEnableOnlineResource) {
×
711
      msIO_printf("  <contactInfo>\n");
×
712
      if (bEnableTelephone) {
×
713
        msIO_printf("    <phone>\n");
×
714
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
×
715
                                 "responsibleparty_phone_voice", OWS_NOERR,
716
                                 "    <voice>%s</voice>\n", NULL);
717
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
×
718
                                 "responsibleparty_phone_facsimile", OWS_NOERR,
719
                                 "    <facsimile>%s</facsimile>\n", NULL);
720
        msIO_printf("    </phone>\n");
×
721
      }
722
      if (bEnableAddress) {
×
723
        msIO_printf("    <address>\n");
×
724
        msOWSPrintEncodeMetadata(
×
725
            stdout, &(map->web.metadata), "CO",
726
            "responsibleparty_address_deliverypoint", OWS_NOERR,
727
            "    <deliveryPoint>%s</deliveryPoint>\n", NULL);
728
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
×
729
                                 "responsibleparty_address_city", OWS_NOERR,
730
                                 "    <city>%s</city>\n", NULL);
731
        msOWSPrintEncodeMetadata(
×
732
            stdout, &(map->web.metadata), "CO",
733
            "responsibleparty_address_administrativearea", OWS_NOERR,
734
            "    <administrativeArea>%s</administrativeArea>\n", NULL);
735
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
×
736
                                 "responsibleparty_address_postalcode",
737
                                 OWS_NOERR, "    <postalCode>%s</postalCode>\n",
738
                                 NULL);
739
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
×
740
                                 "responsibleparty_address_country", OWS_NOERR,
741
                                 "    <country>%s</country>\n", NULL);
742
        msOWSPrintEncodeMetadata(
×
743
            stdout, &(map->web.metadata), "CO",
744
            "responsibleparty_address_electronicmailaddress", OWS_NOERR,
745
            "    <electronicMailAddress>%s</electronicMailAddress>\n", NULL);
746
        msIO_printf("    </address>\n");
×
747
      }
748
      msOWSPrintEncodeMetadata(
×
749
          stdout, &(map->web.metadata), "CO", "responsibleparty_onlineresource",
750
          OWS_NOERR,
751
          "    <onlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>\n",
752
          NULL);
753
      msIO_printf("  </contactInfo>\n");
×
754
    }
755

756
    msIO_printf("</responsibleParty>\n");
×
757

758
  } else if (msOWSLookupMetadata(&(map->web.metadata), "CO", "contactperson") ||
15✔
759
             msOWSLookupMetadata(
4✔
760
                 &(map->web.metadata), "CO",
761
                 "contactorganization")) { /* leverage WMS contact information
762
                                            */
763

764
    msIO_printf("<responsibleParty>\n");
7✔
765
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
7✔
766
                             "contactperson", OWS_NOERR,
767
                             "    <individualName>%s</individualName>\n", NULL);
768
    msOWSPrintEncodeMetadata(
7✔
769
        stdout, &(map->web.metadata), "CO", "contactorganization", OWS_NOERR,
770
        "    <organisationName>%s</organisationName>\n", NULL);
771
    msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
7✔
772
                             "contactposition", OWS_NOERR,
773
                             "    <positionName>%s</positionName>\n", NULL);
774

775
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
7✔
776
                            "contactvoicetelephone") ||
7✔
777
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
778
                            "contactfacsimiletelephone"))
779
      bEnableTelephone = MS_TRUE;
780

781
    if (msOWSLookupMetadata(&(map->web.metadata), "CO", "address") ||
7✔
782
        msOWSLookupMetadata(&(map->web.metadata), "CO", "city") ||
×
783
        msOWSLookupMetadata(&(map->web.metadata), "CO", "stateorprovince") ||
×
784
        msOWSLookupMetadata(&(map->web.metadata), "CO", "postcode") ||
×
785
        msOWSLookupMetadata(&(map->web.metadata), "CO", "country") ||
7✔
786
        msOWSLookupMetadata(&(map->web.metadata), "CO",
×
787
                            "contactelectronicmailaddress"))
788
      bEnableAddress = MS_TRUE;
789

790
    if (msOWSLookupMetadata(&(map->web.metadata), "CO",
7✔
791
                            "service_onlineresource"))
792
      bEnableOnlineResource = MS_TRUE;
793

794
    if (bEnableTelephone || bEnableAddress || bEnableOnlineResource) {
7✔
795
      msIO_printf("  <contactInfo>\n");
7✔
796
      if (bEnableTelephone) {
7✔
797
        msIO_printf("    <phone>\n");
7✔
798
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
7✔
799
                                 "contactvoicetelephone", OWS_NOERR,
800
                                 "    <voice>%s</voice>\n", NULL);
801
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO",
7✔
802
                                 "contactfacsimiletelephone", OWS_NOERR,
803
                                 "    <facsimile>%s</facsimile>\n", NULL);
804
        msIO_printf("    </phone>\n");
7✔
805
      }
806
      if (bEnableAddress) {
7✔
807
        msIO_printf("    <address>\n");
7✔
808
        msOWSPrintEncodeMetadata(
7✔
809
            stdout, &(map->web.metadata), "CO", "address", OWS_NOERR,
810
            "    <deliveryPoint>%s</deliveryPoint>\n", NULL);
811
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "city",
7✔
812
                                 OWS_NOERR, "    <city>%s</city>\n", NULL);
813
        msOWSPrintEncodeMetadata(
7✔
814
            stdout, &(map->web.metadata), "CO", "stateorprovince", OWS_NOERR,
815
            "    <administrativeArea>%s</administrativeArea>\n", NULL);
816
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "postcode",
7✔
817
                                 OWS_NOERR, "    <postalCode>%s</postalCode>\n",
818
                                 NULL);
819
        msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "country",
7✔
820
                                 OWS_NOERR, "    <country>%s</country>\n",
821
                                 NULL);
822
        msOWSPrintEncodeMetadata(
7✔
823
            stdout, &(map->web.metadata), "CO", "contactelectronicmailaddress",
824
            OWS_NOERR,
825
            "    <electronicMailAddress>%s</electronicMailAddress>\n", NULL);
826
        msIO_printf("    </address>\n");
7✔
827
      }
828
      msOWSPrintEncodeMetadata(
7✔
829
          stdout, &(map->web.metadata), "CO", "service_onlineresource",
830
          OWS_NOERR,
831
          "    <onlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>\n",
832
          NULL);
833
      msIO_printf("  </contactInfo>\n");
7✔
834
    }
835
    msIO_printf("</responsibleParty>\n");
7✔
836
  }
837

838
  return;
11✔
839
}
840

841
/************************************************************************/
842
/*                    msWCSGetCapabilities_Service()                    */
843
/************************************************************************/
844

845
static int msWCSGetCapabilities_Service(mapObj *map, wcsParamsObj *params) {
11✔
846
  /* start the Service section, only need the full start tag if this is the only
847
   * section requested */
848
  if (!params->section ||
11✔
849
      (params->section && strcasecmp(params->section, "/") == 0))
×
850
    msIO_printf("<Service>\n");
11✔
851
  else
852
    msIO_printf("<Service\n"
×
853
                "   version=\"%s\" \n"
854
                "   updateSequence=\"%s\" \n"
855
                "   xmlns=\"http://www.opengis.net/wcs\" \n"
856
                "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
857
                "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
858
                "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
859
                "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
860
                "%s/wcs/%s/wcsCapabilities.xsd\">\n",
861
                params->version, params->updatesequence,
862
                msOWSGetSchemasLocation(map), params->version);
863

864
  /* optional metadataLink */
865
  msOWSPrintURLType(stdout, &(map->web.metadata), "CO", "metadatalink",
11✔
866
                    OWS_NOERR,
867
                    "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>", NULL,
868
                    " metadataType=\"%s\"", NULL, NULL, NULL,
869
                    " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE,
870
                    MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL);
871

872
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "description",
11✔
873
                           OWS_NOERR, "  <description>%s</description>\n",
874
                           NULL);
875
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "name",
11✔
876
                           OWS_NOERR, "  <name>%s</name>\n", "MapServer WCS");
877
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "label",
11✔
878
                           OWS_WARN, "  <label>%s</label>\n", NULL);
879

880
  /* we are not supporting the optional keyword type, at least not yet */
881
  msOWSPrintEncodeMetadataList(
11✔
882
      stdout, &(map->web.metadata), "CO", "keywordlist", "  <keywords>\n",
883
      "  </keywords>\n", "    <keyword>%s</keyword>\n", NULL);
884

885
  msWCSGetCapabilities_Service_ResponsibleParty(map);
11✔
886

887
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "fees",
11✔
888
                           OWS_NOERR, "  <fees>%s</fees>\n", "NONE");
889
  msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "CO",
11✔
890
                               "accessconstraints", "  <accessConstraints>\n",
891
                               "  </accessConstraints>\n", "    %s\n", "NONE");
892

893
  /* done */
894
  msIO_printf("</Service>\n");
11✔
895

896
  return MS_SUCCESS;
11✔
897
}
898

899
/************************************************************************/
900
/*                  msWCSGetCapabilities_Capability()                   */
901
/************************************************************************/
902

903
static int msWCSGetCapabilities_Capability(mapObj *map, wcsParamsObj *params,
11✔
904
                                           cgiRequestObj *req) {
905
  char *script_url = NULL, *script_url_encoded = NULL;
906

907
  /* we need this server's onlineresource for the request section */
908
  if ((script_url = msOWSGetOnlineResource(map, "CO", "onlineresource", req)) ==
11✔
909
          NULL ||
22✔
910
      (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
11✔
911
    free(script_url);
×
912
    free(script_url_encoded);
913
    return msWCSException(map, NULL, NULL, params->version);
×
914
  }
915

916
  /* start the Capability section, only need the full start tag if this is the
917
   * only section requested */
918
  if (!params->section ||
11✔
919
      (params->section && strcasecmp(params->section, "/") == 0))
×
920
    msIO_printf("<Capability>\n");
11✔
921
  else
922
    msIO_printf("<Capability\n"
×
923
                "   version=\"%s\" \n"
924
                "   updateSequence=\"%s\" \n"
925
                "   xmlns=\"http://www.opengis.net/wcs\" \n"
926
                "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
927
                "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
928
                "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
929
                "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
930
                "%s/wcs/%s/wcsCapabilities.xsd\">\n",
931
                params->version, params->updatesequence,
932
                msOWSGetSchemasLocation(map), params->version);
933

934
  /* describe the types of requests the server can handle */
935
  msIO_printf("  <Request>\n");
11✔
936

937
  msWCSPrintRequestCapability("GetCapabilities", script_url_encoded);
11✔
938
  if (msOWSRequestIsEnabled(map, NULL, "C", "DescribeCoverage", MS_FALSE))
11✔
939
    msWCSPrintRequestCapability("DescribeCoverage", script_url_encoded);
11✔
940
  if (msOWSRequestIsEnabled(map, NULL, "C", "GetCoverage", MS_FALSE))
11✔
941
    msWCSPrintRequestCapability("GetCoverage", script_url_encoded);
10✔
942

943
  msIO_printf("  </Request>\n");
11✔
944

945
  /* describe the exception formats the server can produce */
946
  msIO_printf("  <Exception>\n");
11✔
947
  msIO_printf("    <Format>application/vnd.ogc.se_xml</Format>\n");
11✔
948
  msIO_printf("  </Exception>\n");
11✔
949

950
  /* describe any vendor specific capabilities */
951
  /* msIO_printf("  <VendorSpecificCapabilities />\n"); */ /* none yet */
952

953
  /* done */
954
  msIO_printf("</Capability>\n");
11✔
955

956
  free(script_url);
11✔
957
  free(script_url_encoded);
11✔
958

959
  return MS_SUCCESS;
11✔
960
}
961

962
/************************************************************************/
963
/*                    msWCSPrintMetadataLink()                          */
964
/************************************************************************/
965

966
static void msWCSPrintMetadataLink(layerObj *layer,
17✔
967
                                   const char *script_url_encoded) {
968
  const char *list =
969
      msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_list");
17✔
970
  if (list) {
17✔
971
    int ntokens = 0;
1✔
972
    char **tokens = msStringSplit(list, ' ', &ntokens);
1✔
973
    for (int i = 0; i < ntokens; i++) {
3✔
974
      std::string key("metadatalink_");
2✔
975
      key += tokens[i];
2✔
976
      msOWSPrintURLType(
2✔
977
          stdout, &(layer->metadata), "CO", key.c_str(), OWS_NOERR,
978
          "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>", NULL,
979
          " metadataType=\"%s\"", NULL, NULL, NULL, " xlink:href=\"%s\"",
980
          MS_FALSE, MS_FALSE, MS_FALSE, MS_FALSE, MS_TRUE, "other", NULL, NULL,
981
          NULL, NULL, NULL);
982
    }
983
    msFreeCharArray(tokens, ntokens);
1✔
984
    return;
985
  }
986

987
  /* optional metadataLink */
988
  if (!msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_href"))
16✔
989
    msMetadataSetGetMetadataURL(layer, script_url_encoded);
9✔
990

991
  msOWSPrintURLType(stdout, &(layer->metadata), "CO", "metadatalink", OWS_NOERR,
16✔
992
                    "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>", NULL,
993
                    " metadataType=\"%s\"", NULL, NULL, NULL,
994
                    " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE,
995
                    MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL);
996
}
997

998
/************************************************************************/
999
/*             msWCSGetCapabilities_CoverageOfferingBrief()             */
1000
/************************************************************************/
1001

1002
static int
1003
msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer,
16✔
1004
                                           const char *script_url_encoded) {
1005
  coverageMetadataObj cm;
1006
  int status;
1007

1008
  if ((layer->status == MS_DELETE) || !msWCSIsLayerSupported(layer))
16✔
1009
    return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */
4✔
1010

1011
  status = msWCSGetCoverageMetadata(layer, &cm);
12✔
1012
  if (status != MS_SUCCESS)
12✔
1013
    return MS_FAILURE;
1014

1015
  /* start the CoverageOfferingBrief section */
1016
  msIO_printf(
12✔
1017
      "  <CoverageOfferingBrief>\n"); /* is this tag right? (I hate schemas
1018
                                         without ANY examples) */
1019

1020
  msWCSPrintMetadataLink(layer, script_url_encoded);
12✔
1021

1022
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "description",
12✔
1023
                           OWS_NOERR, "    <description>%s</description>\n",
1024
                           NULL);
1025
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "name", OWS_NOERR,
12✔
1026
                           "    <name>%s</name>\n", layer->name);
12✔
1027

1028
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "label", OWS_WARN,
12✔
1029
                           "    <label>%s</label>\n", NULL);
1030

1031
  /* TODO: add elevation ranges to lonLatEnvelope (optional) */
1032
  msIO_printf(
12✔
1033
      "    <lonLatEnvelope srsName=\"urn:ogc:def:crs:OGC:1.3:CRS84\">\n");
1034
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx,
12✔
1035
              cm.llextent.miny); /* TODO: don't know if this is right */
1036
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx,
12✔
1037
              cm.llextent.maxy);
1038

1039
  msOWSPrintEncodeMetadataList(
12✔
1040
      stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL,
1041
      "      <gml:timePosition>%s</gml:timePosition>\n", NULL);
1042

1043
  msIO_printf("    </lonLatEnvelope>\n");
12✔
1044

1045
  /* we are not supporting the optional keyword type, at least not yet */
1046
  msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "keywordlist",
12✔
1047
                               "  <keywords>\n", "  </keywords>\n",
1048
                               "    <keyword>%s</keyword>\n", NULL);
1049

1050
  /* done */
1051
  msIO_printf("  </CoverageOfferingBrief>\n");
12✔
1052

1053
  msWCSFreeCoverageMetadata(&cm);
12✔
1054

1055
  return MS_SUCCESS;
1056
}
1057

1058
/************************************************************************/
1059
/*                msWCSGetCapabilities_ContentMetadata()                */
1060
/************************************************************************/
1061

1062
static int msWCSGetCapabilities_ContentMetadata(mapObj *map,
11✔
1063
                                                wcsParamsObj *params,
1064
                                                owsRequestObj *ows_request,
1065
                                                cgiRequestObj *req) {
1066
  int i;
1067
  char *script_url_encoded = NULL;
1068

1069
  {
1070
    char *pszTmp = msOWSGetOnlineResource(map, "CO", "onlineresource", req);
11✔
1071
    script_url_encoded = msEncodeHTMLEntities(pszTmp);
11✔
1072
    msFree(pszTmp);
11✔
1073
  }
1074

1075
  /* start the ContentMetadata section, only need the full start tag if this is
1076
   * the only section requested */
1077
  /* TODO: add Xlink attributes for other sources of this information  */
1078
  if (!params->section ||
11✔
1079
      (params->section && strcasecmp(params->section, "/") == 0))
×
1080
    msIO_printf("<ContentMetadata>\n");
11✔
1081
  else
1082
    msIO_printf("<ContentMetadata\n"
×
1083
                "   version=\"%s\" \n"
1084
                "   updateSequence=\"%s\" \n"
1085
                "   xmlns=\"http://www.opengis.net/wcs\" \n"
1086
                "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1087
                "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1088
                "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1089
                "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
1090
                "%s/wcs/%s/wcsCapabilities.xsd\">\n",
1091
                params->version, params->updatesequence,
1092
                msOWSGetSchemasLocation(map), params->version);
1093

1094
  if (ows_request->numlayers == 0) {
11✔
1095
    msIO_printf("  <!-- WARNING: No WCS layers are enabled. Check "
1✔
1096
                "wcs/ows_enable_request settings. -->\n");
1097
  } else {
1098
    for (i = 0; i < map->numlayers; i++) {
26✔
1099
      if (!msIntegerInArray(GET_LAYER(map, i)->index,
16✔
1100
                            ows_request->enabled_layers,
1101
                            ows_request->numlayers))
1102
        continue;
×
1103

1104
      if (msWCSGetCapabilities_CoverageOfferingBrief(
16✔
1105
              (GET_LAYER(map, i)), script_url_encoded) != MS_SUCCESS) {
16✔
1106
        msIO_printf("  <!-- WARNING: There was a problem with one of layers. "
×
1107
                    "See server log for details. -->\n");
1108
      }
1109
    }
1110
  }
1111

1112
  msFree(script_url_encoded);
11✔
1113

1114
  /* done */
1115
  msIO_printf("</ContentMetadata>\n");
11✔
1116

1117
  return MS_SUCCESS;
11✔
1118
}
1119

1120
/************************************************************************/
1121
/*                        msWCSGetCapabilities()                        */
1122
/************************************************************************/
1123

1124
static int msWCSGetCapabilities(mapObj *map, wcsParamsObj *params,
33✔
1125
                                cgiRequestObj *req,
1126
                                owsRequestObj *ows_request) {
1127
  char tmpString[OWS_VERSION_MAXLEN];
1128
  int i, tmpInt = 0;
1129
  int wcsSupportedVersions[] = {OWS_1_1_2, OWS_1_1_1, OWS_1_1_0, OWS_1_0_0};
33✔
1130
  int wcsNumSupportedVersions = 4;
1131
  const char *updatesequence = NULL;
1132

1133
  /* check version is valid */
1134
  tmpInt = msOWSParseVersionString(params->version);
33✔
1135
  if (tmpInt == OWS_VERSION_BADFORMAT) {
33✔
1136
    return msWCSException(map, "InvalidParameterValue", "version", "1.0.0 ");
×
1137
  }
1138

1139
  /* negotiate version */
1140
  tmpInt = msOWSNegotiateVersion(tmpInt, wcsSupportedVersions,
33✔
1141
                                 wcsNumSupportedVersions);
1142

1143
  /* set result as string and carry on */
1144
  free(params->version);
33✔
1145
  params->version = msStrdup(msOWSGetVersionString(tmpInt, tmpString));
33✔
1146

1147
  /* -------------------------------------------------------------------- */
1148
  /*      1.1.x is sufficiently different we have a whole case for        */
1149
  /*      it.  The remainder of this function is for 1.0.0.               */
1150
  /* -------------------------------------------------------------------- */
1151
  if (strncmp(params->version, "1.1", 3) == 0)
33✔
1152
    return msWCSGetCapabilities11(map, params, req, ows_request);
19✔
1153

1154
  updatesequence =
1155
      msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
14✔
1156

1157
  if (params->updatesequence != NULL) {
14✔
1158
    i = msOWSNegotiateUpdateSequence(params->updatesequence, updatesequence);
3✔
1159
    if (i == 0) { /* current */
3✔
1160
      msSetError(
1✔
1161
          MS_WCSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)",
1162
          "msWCSGetCapabilities()", params->updatesequence, updatesequence);
1163
      return msWCSException(map, "CurrentUpdateSequence", "updatesequence",
1✔
1164
                            params->version);
1✔
1165
    }
1166
    if (i > 0) { /* invalid */
2✔
1167
      msSetError(
1✔
1168
          MS_WCSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)",
1169
          "msWCSGetCapabilities()", params->updatesequence, updatesequence);
1170
      return msWCSException(map, "InvalidUpdateSequence", "updatesequence",
1✔
1171
                            params->version);
1✔
1172
    }
1173
  }
1174

1175
  else { /* set default updatesequence */
1176
    if (!updatesequence)
11✔
1177
      updatesequence = "0";
1178
    params->updatesequence = msStrdup(updatesequence);
11✔
1179
  }
1180

1181
  /* if a bum section param is passed, throw exception */
1182
  if (params->section &&
12✔
1183
      strcasecmp(params->section, "/WCS_Capabilities/Service") != 0 &&
1✔
1184
      strcasecmp(params->section, "/WCS_Capabilities/Capability") != 0 &&
1✔
1185
      strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata") != 0 &&
1✔
1186
      strcasecmp(params->section, "/") != 0) {
1✔
1187
    msSetError(MS_WCSERR, "Invalid SECTION parameter \"%s\"",
1✔
1188
               "msWCSGetCapabilities()", params->section);
1189
    return msWCSException(map, "InvalidParameterValue", "section",
1✔
1190
                          params->version);
1✔
1191
  }
1192

1193
  else {
1194
    msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
11✔
1195
    msIO_sendHeaders();
11✔
1196

1197
    /* print common capability elements  */
1198
    /* TODO: DocType? */
1199

1200
    if (!updatesequence)
11✔
1201
      updatesequence = "0";
1202

1203
    msIO_printf(
11✔
1204
        "<?xml version='1.0' encoding=\"UTF-8\" standalone=\"no\" ?>\n");
1205

1206
    if (!params->section ||
11✔
1207
        (params->section && strcasecmp(params->section, "/") == 0))
×
1208
      msIO_printf("<WCS_Capabilities\n"
11✔
1209
                  "   version=\"%s\" \n"
1210
                  "   updateSequence=\"%s\" \n"
1211
                  "   xmlns=\"http://www.opengis.net/wcs\" \n"
1212
                  "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1213
                  "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1214
                  "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1215
                  "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
1216
                  "%s/wcs/%s/wcsCapabilities.xsd\">\n",
1217
                  params->version, updatesequence, msOWSGetSchemasLocation(map),
1218
                  params->version);
1219

1220
    /* print the various capability sections */
1221
    if (!params->section ||
11✔
1222
        strcasecmp(params->section, "/WCS_Capabilities/Service") == 0)
×
1223
      msWCSGetCapabilities_Service(map, params);
11✔
1224

1225
    if (!params->section ||
11✔
1226
        strcasecmp(params->section, "/WCS_Capabilities/Capability") == 0)
×
1227
      msWCSGetCapabilities_Capability(map, params, req);
11✔
1228

1229
    if (!params->section ||
11✔
1230
        strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata") == 0)
×
1231
      msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req);
11✔
1232

1233
    if (params->section && strcasecmp(params->section, "/") == 0) {
11✔
1234
      msWCSGetCapabilities_Service(map, params);
×
1235
      msWCSGetCapabilities_Capability(map, params, req);
×
1236
      msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req);
×
1237
    }
1238

1239
    /* done */
1240
    if (!params->section ||
11✔
1241
        (params->section && strcasecmp(params->section, "/") == 0))
×
1242
      msIO_printf("</WCS_Capabilities>\n");
11✔
1243
  }
1244

1245
  return MS_SUCCESS;
1246
}
1247

1248
/************************************************************************/
1249
/*               msWCSDescribeCoverage_AxisDescription()                */
1250
/************************************************************************/
1251

1252
static int msWCSDescribeCoverage_AxisDescription(layerObj *layer, char *name) {
2✔
1253
  const char *value;
1254
  char tag[100]; /* should be plenty of space */
1255

1256
  msIO_printf("        <axisDescription>\n");
2✔
1257
  msIO_printf("          <AxisDescription");
2✔
1258
  snprintf(tag, sizeof(tag), "%s_semantic",
1259
           name); /* optional attributes follow (should escape?) */
1260
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
2✔
1261
                           " semantic=\"%s\"", NULL);
1262
  snprintf(tag, sizeof(tag), "%s_refsys", name);
1263
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
2✔
1264
                           " refSys=\"%s\"", NULL);
1265
  snprintf(tag, sizeof(tag), "%s_refsyslabel", name);
1266
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
2✔
1267
                           " refSysLabel=\"%s\"", NULL);
1268
  msIO_printf(">\n");
2✔
1269

1270
  /* TODO: add metadataLink (optional) */
1271

1272
  snprintf(tag, sizeof(tag), "%s_description", name);
1273
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
2✔
1274
                           "            <description>%s</description>\n", NULL);
1275
  /* snprintf(tag, sizeof(tag), "%s_name", name); */
1276
  /* msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_WARN, "
1277
   * <name>%s</name>\n", NULL); */
1278
  msIO_printf("            <name>%s</name>\n", name);
2✔
1279

1280
  snprintf(tag, sizeof(tag), "%s_label", name);
1281
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_WARN,
2✔
1282
                           "            <label>%s</label>\n", NULL);
1283

1284
  /* Values */
1285
  msIO_printf("            <values");
2✔
1286
  snprintf(tag, sizeof(tag), "%s_values_semantic",
1287
           name); /* optional attributes follow (should escape?) */
1288
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
2✔
1289
                           " semantic=\"%s\"", NULL);
1290
  snprintf(tag, sizeof(tag), "%s_values_type", name);
1291
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR,
2✔
1292
                           " type=\"%s\"", NULL);
1293
  msIO_printf(">\n");
2✔
1294

1295
  /* single values, we do not support optional type and semantic attributes */
1296
  snprintf(tag, sizeof(tag), "%s_values", name);
1297
  if (msOWSLookupMetadata(&(layer->metadata), "CO", tag))
2✔
1298
    msOWSPrintEncodeMetadataList(
2✔
1299
        stdout, &(layer->metadata), "CO", tag, NULL, NULL,
1300
        "              <singleValue>%s</singleValue>\n", NULL);
1301

1302
  /* intervals, only one per axis for now, we do not support optional type,
1303
   * atomic and semantic attributes */
1304
  snprintf(tag, sizeof(tag), "%s_interval", name);
1305
  if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", tag)) != NULL) {
2✔
1306
    int numtokens = 0;
×
1307
    char **tokens = msStringSplit(value, '/', &numtokens);
×
1308
    if (tokens && numtokens > 0) {
×
1309
      msIO_printf("            <interval>\n");
×
1310
      if (numtokens >= 1)
×
1311
        msIO_printf("            <min>%s</min>\n",
×
1312
                    tokens[0]); /* TODO: handle closure */
1313
      if (numtokens >= 2)
×
1314
        msIO_printf("            <max>%s</max>\n", tokens[1]);
×
1315
      if (numtokens >= 3)
×
1316
        msIO_printf("            <res>%s</res>\n", tokens[2]);
×
1317
      msIO_printf("            </interval>\n");
×
1318
    }
1319
    msFreeCharArray(tokens, numtokens);
×
1320
  }
1321

1322
  /* TODO: add default (optional) */
1323

1324
  msIO_printf("            </values>\n");
2✔
1325

1326
  msIO_printf("          </AxisDescription>\n");
2✔
1327
  msIO_printf("        </axisDescription>\n");
2✔
1328

1329
  return MS_SUCCESS;
2✔
1330
}
1331

1332
/************************************************************************/
1333
/*               msWCSDescribeCoverage_CoverageOffering()               */
1334
/************************************************************************/
1335

1336
static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer,
5✔
1337
                                                  wcsParamsObj *params,
1338
                                                  char *script_url_encoded) {
1339
  const char *value;
1340
  char *epsg_buf, *encoded_format;
1341
  coverageMetadataObj cm;
1342
  int i, status;
1343

1344
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
5✔
1345
    return MS_FAILURE;
1346

1347
  if (!msWCSIsLayerSupported(layer))
5✔
1348
    return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */
1349

1350
  status = msWCSGetCoverageMetadata(layer, &cm);
5✔
1351
  if (status != MS_SUCCESS)
5✔
1352
    return MS_FAILURE;
1353

1354
  /* fill in bands rangeset info, if required.  */
1355
  msWCSSetDefaultBandsRangeSetInfo(params, &cm, layer);
5✔
1356

1357
  /* start the Coverage section */
1358
  msIO_printf("  <CoverageOffering>\n");
5✔
1359

1360
  msWCSPrintMetadataLink(layer, script_url_encoded);
5✔
1361

1362
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "description",
5✔
1363
                           OWS_NOERR, "  <description>%s</description>\n",
1364
                           NULL);
1365
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "name", OWS_NOERR,
5✔
1366
                           "  <name>%s</name>\n", layer->name);
5✔
1367

1368
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "label", OWS_WARN,
5✔
1369
                           "  <label>%s</label>\n", NULL);
1370

1371
  /* TODO: add elevation ranges to lonLatEnvelope (optional) */
1372
  msIO_printf(
5✔
1373
      "    <lonLatEnvelope srsName=\"urn:ogc:def:crs:OGC:1.3:CRS84\">\n");
1374
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx,
5✔
1375
              cm.llextent.miny);
1376
  msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx,
5✔
1377
              cm.llextent.maxy);
1378

1379
  msOWSPrintEncodeMetadataList(
5✔
1380
      stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL,
1381
      "      <gml:timePosition>%s</gml:timePosition>\n", NULL);
1382

1383
  msIO_printf("    </lonLatEnvelope>\n");
5✔
1384

1385
  /* we are not supporting the optional keyword type, at least not yet */
1386
  msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "keywordlist",
5✔
1387
                               "  <keywords>\n", "  </keywords>\n",
1388
                               "    <keyword>%s</keyword>\n", NULL);
1389

1390
  /* DomainSet: starting simple, just a spatial domain (gml:envelope) and
1391
   * optionally a temporal domain */
1392
  msIO_printf("    <domainSet>\n");
5✔
1393

1394
  /* SpatialDomain */
1395
  msIO_printf("      <spatialDomain>\n");
5✔
1396

1397
  /* envelope in lat/lon */
1398
  msIO_printf("        <gml:Envelope srsName=\"EPSG:4326\">\n");
5✔
1399
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx,
5✔
1400
              cm.llextent.miny);
1401
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx,
5✔
1402
              cm.llextent.maxy);
1403
  msIO_printf("        </gml:Envelope>\n");
5✔
1404

1405
  /* envelope in the native srs */
1406
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE,
5✔
1407
                   &epsg_buf);
1408
  if (!epsg_buf) {
5✔
1409
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
×
1410
                     "CO", MS_TRUE, &epsg_buf);
1411
  }
1412
  if (epsg_buf) {
5✔
1413
    msIO_printf("        <gml:Envelope srsName=\"%s\">\n", epsg_buf);
5✔
1414
    msFree(epsg_buf);
5✔
1415
  } else {
1416
    msIO_printf("        <!-- NativeCRSs ERROR: missing required information, "
×
1417
                "no SRSs defined -->\n");
1418
  }
1419
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.extent.minx,
5✔
1420
              cm.extent.miny);
1421
  msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.extent.maxx,
5✔
1422
              cm.extent.maxy);
1423
  msIO_printf("        </gml:Envelope>\n");
5✔
1424

1425
  /* gml:rectifiedGrid */
1426
  msIO_printf("        <gml:RectifiedGrid dimension=\"2\">\n");
5✔
1427
  msIO_printf("          <gml:limits>\n");
5✔
1428
  msIO_printf("            <gml:GridEnvelope>\n");
5✔
1429
  msIO_printf("              <gml:low>0 0</gml:low>\n");
5✔
1430
  msIO_printf("              <gml:high>%d %d</gml:high>\n", cm.xsize - 1,
5✔
1431
              cm.ysize - 1);
5✔
1432
  msIO_printf("            </gml:GridEnvelope>\n");
5✔
1433
  msIO_printf("          </gml:limits>\n");
5✔
1434
  msIO_printf("          <gml:axisName>x</gml:axisName>\n");
5✔
1435
  msIO_printf("          <gml:axisName>y</gml:axisName>\n");
5✔
1436
  msIO_printf("          <gml:origin>\n");
5✔
1437
  msIO_printf("            <gml:pos>%.15g %.15g</gml:pos>\n",
5✔
1438
              cm.geotransform[0], cm.geotransform[3]);
1439
  msIO_printf("          </gml:origin>\n");
5✔
1440
  msIO_printf("          <gml:offsetVector>%.15g %.15g</gml:offsetVector>\n",
5✔
1441
              cm.geotransform[1],
1442
              cm.geotransform[2]); /* offset vector in X direction */
1443
  msIO_printf("          <gml:offsetVector>%.15g %.15g</gml:offsetVector>\n",
5✔
1444
              cm.geotransform[4],
1445
              cm.geotransform[5]); /* offset vector in Y direction */
1446
  msIO_printf("        </gml:RectifiedGrid>\n");
5✔
1447

1448
  msIO_printf("      </spatialDomain>\n");
5✔
1449

1450
  msWCSFreeCoverageMetadata(&cm);
5✔
1451

1452
  /* TemporalDomain */
1453

1454
  /* TODO: figure out when a temporal domain is valid, for example only tiled
1455
   * rasters support time as a domain, plus we need a timeitem */
1456
  if (msOWSLookupMetadata(&(layer->metadata), "CO", "timeposition") ||
9✔
1457
      msOWSLookupMetadata(&(layer->metadata), "CO", "timeperiod")) {
4✔
1458
    msIO_printf("      <temporalDomain>\n");
1✔
1459

1460
    /* TimePosition (should support a value AUTO, then we could mine positions
1461
     * from the timeitem) */
1462
    msOWSPrintEncodeMetadataList(
1✔
1463
        stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL,
1464
        "        <gml:timePosition>%s</gml:timePosition>\n", NULL);
1465

1466
    /* TODO:  add TimePeriod (only one per layer)  */
1467

1468
    msIO_printf("      </temporalDomain>\n");
1✔
1469
  }
1470

1471
  msIO_printf("    </domainSet>\n");
5✔
1472

1473
  /* rangeSet */
1474
  msIO_printf("    <rangeSet>\n");
5✔
1475
  msIO_printf(
5✔
1476
      "      <RangeSet>\n"); /* TODO: there are some optional attributes */
1477

1478
  /* TODO: add metadataLink (optional) */
1479

1480
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO",
5✔
1481
                           "rangeset_description", OWS_NOERR,
1482
                           "        <description>%s</description>\n", NULL);
1483
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "rangeset_name",
5✔
1484
                           OWS_WARN, "        <name>%s</name>\n", NULL);
1485

1486
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "rangeset_label",
5✔
1487
                           OWS_WARN, "        <label>%s</label>\n", NULL);
1488

1489
  /* compound range sets */
1490
  if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
5✔
1491
                                   "rangeset_axes")) != NULL) {
1492
    int numtokens = 0;
2✔
1493
    char **tokens = msStringSplit(value, ',', &numtokens);
2✔
1494
    if (tokens && numtokens > 0) {
2✔
1495
      for (i = 0; i < numtokens; i++)
4✔
1496
        msWCSDescribeCoverage_AxisDescription(layer, tokens[i]);
2✔
1497
    }
1498
    msFreeCharArray(tokens, numtokens);
2✔
1499
  }
1500

1501
  if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
5✔
1502
                                   "rangeset_nullvalue")) != NULL) {
1503
    msIO_printf("        <nullValues>\n");
3✔
1504
    msIO_printf("          <singleValue>%s</singleValue>\n", value);
3✔
1505
    msIO_printf("        </nullValues>\n");
3✔
1506
  }
1507

1508
  msIO_printf("      </RangeSet>\n");
5✔
1509
  msIO_printf("    </rangeSet>\n");
5✔
1510

1511
  /* supportedCRSs */
1512
  msIO_printf("    <supportedCRSs>\n");
5✔
1513

1514
  /* requestResponseCRSs: check the layer metadata/projection, and then the map
1515
   * metadata/projection if necessary (should never get to the error message) */
1516
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_FALSE,
5✔
1517
                   &epsg_buf);
1518
  if (!epsg_buf) {
5✔
1519
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
×
1520
                     "CO", MS_FALSE, &epsg_buf);
1521
  }
1522
  if (epsg_buf) {
5✔
1523
    int numtokens = 0;
5✔
1524
    char **tokens = msStringSplit(epsg_buf, ' ', &numtokens);
5✔
1525
    if (tokens && numtokens > 0) {
5✔
1526
      for (i = 0; i < numtokens; i++)
15✔
1527
        msIO_printf("      <requestResponseCRSs>%s</requestResponseCRSs>\n",
10✔
1528
                    tokens[i]);
10✔
1529
    }
1530
    msFreeCharArray(tokens, numtokens);
5✔
1531
    msFree(epsg_buf);
5✔
1532
  } else {
1533
    msIO_printf("      <!-- requestResponseCRSs ERROR: missing required "
×
1534
                "information, no SRSs defined -->\n");
1535
  }
1536

1537
  /* nativeCRSs (only one in our case) */
1538
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE,
5✔
1539
                   &epsg_buf);
1540
  if (!epsg_buf) {
5✔
1541
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
×
1542
                     "CO", MS_TRUE, &epsg_buf);
1543
  }
1544
  if (epsg_buf) {
5✔
1545
    msIO_printf("      <nativeCRSs>%s</nativeCRSs>\n", epsg_buf);
5✔
1546
    msFree(epsg_buf);
5✔
1547
  } else {
1548
    msIO_printf("      <!-- nativeCRSs ERROR: missing required information, no "
×
1549
                "SRSs defined -->\n");
1550
  }
1551

1552
  msIO_printf("    </supportedCRSs>\n");
5✔
1553

1554
  /* supportedFormats */
1555
  msIO_printf("    <supportedFormats");
5✔
1556
  msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "nativeformat",
5✔
1557
                           OWS_NOERR, " nativeFormat=\"%s\"", NULL);
1558
  msIO_printf(">\n");
5✔
1559

1560
  if ((encoded_format = msOWSGetEncodeMetadata(&(layer->metadata), "CO",
5✔
1561
                                               "formats", "GTiff")) != NULL) {
1562
    int numtokens = 0;
5✔
1563
    char **tokens = msStringSplit(encoded_format, ' ', &numtokens);
5✔
1564
    if (tokens && numtokens > 0) {
5✔
1565
      for (i = 0; i < numtokens; i++)
14✔
1566
        msIO_printf("      <formats>%s</formats>\n", tokens[i]);
9✔
1567
    }
1568
    msFreeCharArray(tokens, numtokens);
5✔
1569
    msFree(encoded_format);
5✔
1570
  }
1571
  msIO_printf("    </supportedFormats>\n");
5✔
1572

1573
  msIO_printf("    <supportedInterpolations default=\"nearest neighbor\">\n");
5✔
1574
  msIO_printf(
5✔
1575
      "      <interpolationMethod>nearest neighbor</interpolationMethod>\n");
1576
  msIO_printf("      <interpolationMethod>bilinear</interpolationMethod>\n");
5✔
1577
  /*  msIO_printf("      <interpolationMethod>bicubic</interpolationMethod>\n"
1578
   * ); */
1579
  msIO_printf("    </supportedInterpolations>\n");
5✔
1580

1581
  /* done */
1582
  msIO_printf("  </CoverageOffering>\n");
5✔
1583

1584
  return MS_SUCCESS;
1585
}
1586

1587
/************************************************************************/
1588
/*                       msWCSDescribeCoverage()                        */
1589
/************************************************************************/
1590

1591
static int msWCSDescribeCoverage(mapObj *map, wcsParamsObj *params,
8✔
1592
                                 owsRequestObj *ows_request,
1593
                                 cgiRequestObj *req) {
1594
  int i = 0, j = 0, k = 0;
1595
  const char *updatesequence = NULL;
1596
  char **coverages = NULL;
1597
  int numcoverages = 0;
8✔
1598

1599
  char *coverageName = NULL;
1600
  char *script_url_encoded = NULL;
1601

1602
  /* -------------------------------------------------------------------- */
1603
  /*      1.1.x is sufficiently different we have a whole case for        */
1604
  /*      it.  The remainder of this function is for 1.0.0.               */
1605
  /* -------------------------------------------------------------------- */
1606
  if (strncmp(params->version, "1.1", 3) == 0)
8✔
1607
    return msWCSDescribeCoverage11(map, params, ows_request);
3✔
1608

1609
  /* -------------------------------------------------------------------- */
1610
  /*      Process 1.0.0...                                                */
1611
  /* -------------------------------------------------------------------- */
1612

1613
  if (params->coverages) { /* use the list, but validate it first */
5✔
1614
    for (j = 0; params->coverages[j]; j++) {
10✔
1615
      coverages = msStringSplit(params->coverages[j], ',', &numcoverages);
5✔
1616
      for (k = 0; k < numcoverages; k++) {
10✔
1617

1618
        for (i = 0; i < map->numlayers; i++) {
6✔
1619
          coverageName =
1620
              msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO",
6✔
1621
                                     "name", GET_LAYER(map, i)->name);
6✔
1622
          if (coverageName != NULL && EQUAL(coverageName, coverages[k]) &&
11✔
1623
              (msIntegerInArray(GET_LAYER(map, i)->index,
5✔
1624
                                ows_request->enabled_layers,
1625
                                ows_request->numlayers))) {
1626
            msFree(coverageName);
5✔
1627
            break;
5✔
1628
          }
1629
          msFree(coverageName);
1✔
1630
        }
1631

1632
        /* i = msGetLayerIndex(map, coverages[k]); */
1633
        if (i == map->numlayers) { /* coverage not found */
5✔
1634
          msSetError(
×
1635
              MS_WCSERR,
1636
              "COVERAGE %s cannot be opened / does not exist. A layer might be disabled for \
1637
this request. Check wcs/ows_enable_request settings.",
1638
              "msWCSDescribeCoverage()", coverages[k]);
×
1639
          return msWCSException(map, "CoverageNotDefined", "coverage",
×
1640
                                params->version);
×
1641
        }
1642
      } /* next coverage */
1643
      msFreeCharArray(coverages, numcoverages);
5✔
1644
    }
1645
  }
1646

1647
  updatesequence =
1648
      msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
5✔
1649
  if (!updatesequence)
5✔
1650
    updatesequence = "0";
1651

1652
  /* printf("Content-Type: application/vnd.ogc.se_xml%c%c",10,10); */
1653
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
5✔
1654
  msIO_sendHeaders();
5✔
1655

1656
  /* print common capability elements  */
1657
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
5✔
1658

1659
  /* start the DescribeCoverage section */
1660
  msIO_printf("<CoverageDescription\n"
5✔
1661
              "   version=\"%s\" \n"
1662
              "   updateSequence=\"%s\" \n"
1663
              "   xmlns=\"http://www.opengis.net/wcs\" \n"
1664
              "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1665
              "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1666
              "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1667
              "   xsi:schemaLocation=\"http://www.opengis.net/wcs "
1668
              "%s/wcs/%s/describeCoverage.xsd\">\n",
1669
              params->version, updatesequence, msOWSGetSchemasLocation(map),
1670
              params->version);
1671

1672
  {
1673
    char *pszTmp = msOWSGetOnlineResource(map, "CO", "onlineresource", req);
5✔
1674
    script_url_encoded = msEncodeHTMLEntities(pszTmp);
5✔
1675
    msFree(pszTmp);
5✔
1676
  }
1677

1678
  if (params->coverages) { /* use the list */
5✔
1679
    for (j = 0; params->coverages[j]; j++) {
10✔
1680
      coverages = msStringSplit(params->coverages[j], ',', &numcoverages);
5✔
1681
      for (k = 0; k < numcoverages; k++) {
10✔
1682
        for (i = 0; i < map->numlayers; i++) {
6✔
1683
          coverageName =
1684
              msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO",
6✔
1685
                                     "name", GET_LAYER(map, i)->name);
6✔
1686
          if (coverageName != NULL && EQUAL(coverageName, coverages[k])) {
6✔
1687
            msFree(coverageName);
5✔
1688
            break;
5✔
1689
          }
1690
          msFree(coverageName);
1✔
1691
        }
1692
        msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params,
5✔
1693
                                               script_url_encoded);
1694
      }
1695
      msFreeCharArray(coverages, numcoverages);
5✔
1696
    }
1697
  } else { /* return all layers */
1698
    for (i = 0; i < map->numlayers; i++) {
×
1699
      if (!msIntegerInArray(GET_LAYER(map, i)->index,
×
1700
                            ows_request->enabled_layers,
1701
                            ows_request->numlayers))
1702
        continue;
×
1703

1704
      msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params,
×
1705
                                             script_url_encoded);
1706
    }
1707
  }
1708

1709
  msFree(script_url_encoded);
5✔
1710

1711
  /* done */
1712
  msIO_printf("</CoverageDescription>\n");
5✔
1713

1714
  return MS_SUCCESS;
1715
}
1716

1717
/************************************************************************/
1718
/*                       msWCSGetCoverageBands10()                      */
1719
/************************************************************************/
1720

1721
static int msWCSGetCoverageBands10(mapObj *map, cgiRequestObj *request,
24✔
1722
                                   wcsParamsObj *params, layerObj *lp,
1723
                                   char **p_bandlist)
1724

1725
{
1726
  const char *value = NULL;
1727
  int i;
1728

1729
  /* Are there any non-spatio/temporal ranges to do subsetting on (e.g. bands)
1730
   */
1731
  value = msOWSLookupMetadata(
24✔
1732
      &(lp->metadata), "CO",
24✔
1733
      "rangeset_axes"); /* this will get all the compound range sets */
1734
  if (value) {
24✔
1735
    char **tokens;
1736
    int numtokens;
1737
    char tag[100];
1738
    const char *rangeitem;
1739

1740
    tokens = msStringSplit(value, ',', &numtokens);
19✔
1741

1742
    for (i = 0; i < numtokens; i++) {
37✔
1743
      if ((value = msWCSGetRequestParameter(request, tokens[i])) == NULL)
19✔
1744
        continue; /* next rangeset parameter */
17✔
1745

1746
      /* ok, a parameter has been passed which matches a token in
1747
       * wcs_rangeset_axes */
1748
      if (msWCSValidateRangeSetParam(lp, tokens[i], value) != MS_SUCCESS) {
2✔
1749
        int ret;
1750
        msSetError(MS_WCSERR, "Error specifying \"%s\" parameter value(s).",
1✔
1751
                   "msWCSGetCoverage()", tokens[i]);
1752
        ret = msWCSException(map, "InvalidParameterValue", tokens[i],
1✔
1753
                             params->version);
1✔
1754
        msFreeCharArray(tokens, numtokens);
1✔
1755
        return ret;
1✔
1756
      }
1757

1758
      /* xxxxx_rangeitem tells us how to subset */
1759
      snprintf(tag, sizeof(tag), "%s_rangeitem", tokens[i]);
1✔
1760
      if ((rangeitem = msOWSLookupMetadata(&(lp->metadata), "CO", tag)) ==
1✔
1761
          NULL) {
1762
        msSetError(MS_WCSERR,
×
1763
                   "Missing required metadata element \"%s\", unable to "
1764
                   "process %s=%s.",
1765
                   "msWCSGetCoverage()", tag, tokens[i], value);
1766
        msFreeCharArray(tokens, numtokens);
×
1767
        return msWCSException(map, NULL, NULL, params->version);
×
1768
      }
1769

1770
      if (strcasecmp(rangeitem, "_bands") ==
1✔
1771
          0) { /* special case, subset bands */
1772
        *p_bandlist = msWCSConvertRangeSetToString(value);
1✔
1773

1774
        if (!*p_bandlist) {
1✔
1775
          msSetError(MS_WCSERR, "Error specifying \"%s\" parameter value(s).",
×
1776
                     "msWCSGetCoverage()", tokens[i]);
1777
          msFreeCharArray(tokens, numtokens);
×
1778
          return msWCSException(map, NULL, NULL, params->version);
×
1779
        }
1780
      } else if (strcasecmp(rangeitem, "_pixels") ==
×
1781
                 0) { /* special case, subset pixels */
1782
        msFreeCharArray(tokens, numtokens);
×
1783
        msSetError(
×
1784
            MS_WCSERR,
1785
            "Arbitrary range sets based on pixel values are not yet supported.",
1786
            "msWCSGetCoverage()");
1787
        return msWCSException(map, NULL, NULL, params->version);
×
1788
      } else {
1789
        msFreeCharArray(tokens, numtokens);
×
1790
        msSetError(MS_WCSERR,
×
1791
                   "Arbitrary range sets based on tile (i.e. image) attributes "
1792
                   "are not yet supported.",
1793
                   "msWCSGetCoverage()");
1794
        return msWCSException(map, NULL, NULL, params->version);
×
1795
      }
1796
    }
1797
    /* clean-up */
1798
    msFreeCharArray(tokens, numtokens);
18✔
1799
  }
1800

1801
  return MS_SUCCESS;
1802
}
1803

1804
/************************************************************************/
1805
/*                   msWCSGetCoverage_ImageCRSSetup()                   */
1806
/*                                                                      */
1807
/*      The request was in imageCRS - update the map projection to      */
1808
/*      map the native projection of the layer, and reset the           */
1809
/*      bounding box to match the projected bounds corresponding to     */
1810
/*      the imageCRS request.                                           */
1811
/************************************************************************/
1812

1813
static int msWCSGetCoverage_ImageCRSSetup(mapObj *map, wcsParamsObj *params,
3✔
1814
                                          coverageMetadataObj *cm,
1815
                                          layerObj *layer)
1816

1817
{
1818
  /* -------------------------------------------------------------------- */
1819
  /*      Load map with the layer (coverage) coordinate system.  We       */
1820
  /*      really need a set projectionObj from projectionObj function!    */
1821
  /* -------------------------------------------------------------------- */
1822
  char *layer_proj = msGetProjectionString(&(layer->projection));
3✔
1823

1824
  if (msLoadProjectionString(&(map->projection), layer_proj) != 0) {
3✔
1825
    msFree(layer_proj);
×
1826
    return msWCSException(map, NULL, NULL, params->version);
×
1827
  }
1828

1829
  free(layer_proj);
3✔
1830
  layer_proj = NULL;
1831

1832
  /* -------------------------------------------------------------------- */
1833
  /*      Reset bounding box.                                             */
1834
  /* -------------------------------------------------------------------- */
1835
  if (params->bbox.maxx != params->bbox.minx) {
3✔
1836
    rectObj orig_bbox = params->bbox;
3✔
1837

1838
    params->bbox.minx = cm->geotransform[0] +
3✔
1839
                        orig_bbox.minx * cm->geotransform[1] +
3✔
1840
                        orig_bbox.miny * cm->geotransform[2];
3✔
1841
    params->bbox.maxy = cm->geotransform[3] +
3✔
1842
                        orig_bbox.minx * cm->geotransform[4] +
3✔
1843
                        orig_bbox.miny * cm->geotransform[5];
3✔
1844
    params->bbox.maxx = cm->geotransform[0] +
3✔
1845
                        (orig_bbox.maxx + 1) * cm->geotransform[1] +
3✔
1846
                        (orig_bbox.maxy + 1) * cm->geotransform[2];
3✔
1847
    params->bbox.miny = cm->geotransform[3] +
3✔
1848
                        (orig_bbox.maxx + 1) * cm->geotransform[4] +
3✔
1849
                        (orig_bbox.maxy + 1) * cm->geotransform[5];
3✔
1850

1851
    /* WCS 1.1 boundbox is center of pixel oriented. */
1852
    if (strncasecmp(params->version, "1.1", 3) == 0) {
3✔
1853
      params->bbox.minx += cm->geotransform[1] / 2 + cm->geotransform[2] / 2;
3✔
1854
      params->bbox.maxx -= cm->geotransform[1] / 2 + cm->geotransform[2] / 2;
3✔
1855
      params->bbox.maxy += cm->geotransform[4] / 2 + cm->geotransform[5] / 2;
3✔
1856
      params->bbox.miny -= cm->geotransform[4] / 2 + cm->geotransform[5] / 2;
3✔
1857
    }
1858
  }
1859

1860
  /* -------------------------------------------------------------------- */
1861
  /*      Reset resolution.                                               */
1862
  /* -------------------------------------------------------------------- */
1863
  if (params->resx != 0.0) {
3✔
1864
    params->resx = cm->geotransform[1] * params->resx;
3✔
1865
    params->resy = fabs(cm->geotransform[5] * params->resy);
3✔
1866
  }
1867

1868
  return MS_SUCCESS;
1869
}
1870

1871
/************************************************************************/
1872
/*                    msWCSApplyLayerCreationOptions()                  */
1873
/************************************************************************/
1874

1875
void msWCSApplyLayerCreationOptions(layerObj *lp, outputFormatObj *format,
190✔
1876
                                    const char *bandlist)
1877

1878
{
1879
  const char *pszKey;
1880
  char szKeyBeginning[256];
1881
  size_t nKeyBeginningLength;
1882
  int nBands = 0;
190✔
1883
  char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
190✔
1884

1885
  snprintf(szKeyBeginning, sizeof(szKeyBeginning),
190✔
1886
           "wcs_outputformat_%s_creationoption_", format->name);
1887
  nKeyBeginningLength = strlen(szKeyBeginning);
190✔
1888

1889
  pszKey = msFirstKeyFromHashTable(&(lp->metadata));
190✔
1890
  for (; pszKey != NULL;
3,777✔
1891
       pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
3,587✔
1892
    if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
3,587✔
1893
      const char *pszValue = msLookupHashTable(&(lp->metadata), pszKey);
18✔
1894
      const char *pszGDALKey = pszKey + nKeyBeginningLength;
18✔
1895
      if (EQUALN(pszGDALKey, "BAND_", strlen("BAND_"))) {
18✔
1896
        /* Remap BAND specific creation option to the real output
1897
         * band number, given the band subset of the request */
1898
        int nKeyOriBandNumber = atoi(pszGDALKey + strlen("BAND_"));
5✔
1899
        int nTargetBandNumber = -1;
1900
        int i;
1901
        for (i = 0; i < nBands; i++) {
6✔
1902
          if (nKeyOriBandNumber == atoi(papszBandNumbers[i])) {
5✔
1903
            nTargetBandNumber = i + 1;
4✔
1904
            break;
4✔
1905
          }
1906
        }
1907
        if (nTargetBandNumber > 0) {
5✔
1908
          char szModKey[256];
1909
          const char *pszAfterBand = strchr(pszGDALKey + strlen("BAND_"), '_');
1910
          if (pszAfterBand != NULL) {
4✔
1911
            snprintf(szModKey, sizeof(szModKey), "BAND_%d%s", nTargetBandNumber,
1912
                     pszAfterBand);
1913
            if (lp->debug >= MS_DEBUGLEVEL_VVV) {
4✔
1914
              msDebug("Setting GDAL %s=%s creation option\n", szModKey,
×
1915
                      pszValue);
1916
            }
1917
            msSetOutputFormatOption(format, szModKey, pszValue);
4✔
1918
          }
1919
        }
1920
      } else {
1921
        if (lp->debug >= MS_DEBUGLEVEL_VVV) {
13✔
1922
          msDebug("Setting GDAL %s=%s creation option\n", pszGDALKey, pszValue);
×
1923
        }
1924
        msSetOutputFormatOption(format, pszGDALKey, pszValue);
13✔
1925
      }
1926
    }
1927
  }
1928

1929
  msFreeCharArray(papszBandNumbers, nBands);
190✔
1930
}
190✔
1931

1932
/************************************************************************/
1933
/*               msWCSApplyDatasetMetadataAsCreationOptions()           */
1934
/************************************************************************/
1935

1936
void msWCSApplyDatasetMetadataAsCreationOptions(layerObj *lp,
47✔
1937
                                                outputFormatObj *format,
1938
                                                const char *bandlist,
1939
                                                void *hDSIn) {
1940
  /* Requires GDAL 2.3 in practice. */
1941
  /* Automatic forwarding of input dataset metadata if it is GRIB and the */
1942
  /* output is GRIB as well, and wcs_outputformat_GRIB_creationoption* are */
1943
  /* not defined. */
1944
  GDALDatasetH hDS = (GDALDatasetH)hDSIn;
1945
  if (hDS && GDALGetDatasetDriver(hDS) &&
47✔
1946
      EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hDS)), "GRIB") &&
94✔
1947
      EQUAL(format->driver, "GDAL/GRIB")) {
3✔
1948
    const char *pszKey;
1949
    char szKeyBeginning[256];
1950
    size_t nKeyBeginningLength;
1951
    int bWCSMetadataFound = MS_FALSE;
1952

1953
    snprintf(szKeyBeginning, sizeof(szKeyBeginning),
3✔
1954
             "wcs_outputformat_%s_creationoption_", format->name);
1955
    nKeyBeginningLength = strlen(szKeyBeginning);
3✔
1956

1957
    for (pszKey = msFirstKeyFromHashTable(&(lp->metadata)); pszKey != NULL;
83✔
1958
         pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
80✔
1959
      if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
80✔
1960
        bWCSMetadataFound = MS_TRUE;
1961
        break;
1962
      }
1963
    }
1964
    if (!bWCSMetadataFound) {
3✔
1965
      int nBands = 0;
3✔
1966
      char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
3✔
1967
      int i;
1968
      for (i = 0; i < nBands; i++) {
6✔
1969
        int nSrcBand = atoi(papszBandNumbers[i]);
3✔
1970
        int nDstBand = i + 1;
3✔
1971
        GDALRasterBandH hBand = GDALGetRasterBand(hDS, nSrcBand);
3✔
1972
        if (hBand) {
3✔
1973
          CSLConstList papszMD = GDALGetMetadata(hBand, NULL);
3✔
1974
          const char *pszMDI = CSLFetchNameValue(papszMD, "GRIB_IDS");
3✔
1975
          // Make sure it is a GRIB2 band
1976
          if (pszMDI) {
3✔
1977
            char szKey[256];
1978
            snprintf(szKey, sizeof(szKey), "BAND_%d_IDS", nDstBand);
1979
            msSetOutputFormatOption(format, szKey, pszMDI);
3✔
1980

1981
            snprintf(szKey, sizeof(szKey), "BAND_%d_DISCIPLINE", nDstBand);
1982
            msSetOutputFormatOption(format, szKey,
3✔
1983
                                    CSLFetchNameValue(papszMD, "DISCIPLINE"));
1984

1985
            snprintf(szKey, sizeof(szKey), "BAND_%d_PDS_PDTN", nDstBand);
1986
            msSetOutputFormatOption(
3✔
1987
                format, szKey, CSLFetchNameValue(papszMD, "GRIB_PDS_PDTN"));
1988

1989
            snprintf(szKey, sizeof(szKey), "BAND_%d_PDS_TEMPLATE_NUMBERS",
1990
                     nDstBand);
1991
            msSetOutputFormatOption(
3✔
1992
                format, szKey,
1993
                CSLFetchNameValue(papszMD, "GRIB_PDS_TEMPLATE_NUMBERS"));
1994
          }
1995
        }
1996
      }
1997
      msFreeCharArray(papszBandNumbers, nBands);
3✔
1998
    }
1999
  }
2000
}
47✔
2001

2002
/************************************************************************/
2003
/*                  msWCSApplyLayerMetadataItemOptions()                */
2004
/************************************************************************/
2005

2006
void msWCSApplyLayerMetadataItemOptions(layerObj *lp, outputFormatObj *format,
190✔
2007
                                        const char *bandlist)
2008

2009
{
2010
  if (!STARTS_WITH(format->driver, "GDAL/"))
190✔
2011
    return;
16✔
2012

2013
  const bool bIsNetCDFOutput = EQUAL(format->driver, "GDAL/netCDF");
174✔
2014
  const char *pszKey;
2015
  char szKeyBeginning[256];
2016
  size_t nKeyBeginningLength;
2017
  int nBands = 0;
174✔
2018
  char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
174✔
2019

2020
  snprintf(szKeyBeginning, sizeof(szKeyBeginning), "wcs_outputformat_%s_mdi_",
174✔
2021
           format->name);
2022
  nKeyBeginningLength = strlen(szKeyBeginning);
174✔
2023

2024
  // Transform wcs_outputformat_{formatname}_mdi_{key} to mdi_{key}
2025
  // and Transform wcs_outputformat_{formatname}_mdi_BAND_X_{key} to
2026
  // mdi_BAND_Y_{key} MDI stands for MetaDataItem
2027

2028
  // For netCDF 3D output
2029
  std::map<int, std::string> oMapExtraDimValues;
2030
  std::string osExtraDimName;
2031
  bool bExtraDimValid = false;
2032

2033
  pszKey = msFirstKeyFromHashTable(&(lp->metadata));
174✔
2034
  for (; pszKey != NULL;
3,530✔
2035
       pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
3,356✔
2036
    if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
3,356✔
2037
      const char *pszValue = msLookupHashTable(&(lp->metadata), pszKey);
15✔
2038
      const char *pszGDALKey = pszKey + nKeyBeginningLength;
15✔
2039

2040
      if (EQUALN(pszGDALKey, "BAND_", strlen("BAND_"))) {
15✔
2041
        /* Remap BAND specific creation option to the real output
2042
         * band number, given the band subset of the request */
2043
        int nKeyOriBandNumber = atoi(pszGDALKey + strlen("BAND_"));
11✔
2044
        int nTargetBandNumber = -1;
11✔
2045
        int i;
2046
        for (i = 0; i < nBands; i++) {
33✔
2047
          if (nKeyOriBandNumber == atoi(papszBandNumbers[i])) {
26✔
2048
            nTargetBandNumber = i + 1;
4✔
2049
            break;
4✔
2050
          }
2051
        }
2052
        if (nTargetBandNumber > 0) {
11✔
2053
          char szModKey[256];
2054
          const char *pszAfterBand = strchr(pszGDALKey + strlen("BAND_"), '_');
2055
          if (pszAfterBand != NULL) {
4✔
2056
            // Special case to generate 3D netCDF files
2057
            if (bIsNetCDFOutput &&
4✔
2058
                strncmp(pszAfterBand, "_default_NETCDF_DIM_",
3✔
2059
                        strlen("_default_NETCDF_DIM_")) == 0) {
2060
              const char *pszDimName =
3✔
2061
                  pszAfterBand + strlen("_default_NETCDF_DIM_");
2062
              if (osExtraDimName.empty()) {
3✔
2063
                bExtraDimValid = true;
2064
                osExtraDimName = pszDimName;
2065
                oMapExtraDimValues[nTargetBandNumber] = pszValue;
1✔
2066
              } else if (bExtraDimValid) {
2✔
2067
                if (osExtraDimName != pszDimName) {
2✔
2068
                  msDebug("One band has several %sdefault_NETCDF_DIM_ metadata "
×
2069
                          "items, "
2070
                          "or different bands have a different value. "
2071
                          "Only a single extra dimension is supported.",
2072
                          szKeyBeginning);
2073
                  bExtraDimValid = false;
2074
                } else {
2075
                  oMapExtraDimValues[nTargetBandNumber] = pszValue;
2✔
2076
                }
2077
              }
2078
            } else {
2079
              snprintf(szModKey, sizeof(szModKey), "mdi_BAND_%d%s",
2080
                       nTargetBandNumber, pszAfterBand);
2081
              if (lp->debug >= MS_DEBUGLEVEL_VVV) {
1✔
2082
                msDebug("Setting GDAL %s=%s metadata item option\n", szModKey,
×
2083
                        pszValue);
2084
              }
2085
              msSetOutputFormatOption(format, szModKey, pszValue);
1✔
2086
            }
2087
          }
2088
        }
2089
      } else {
2090
        char szModKey[256];
2091
        snprintf(szModKey, sizeof(szModKey), "mdi_%s", pszGDALKey);
2092
        if (lp->debug >= MS_DEBUGLEVEL_VVV) {
4✔
2093
          msDebug("Setting GDAL %s=%s metadata item option\n", szModKey,
×
2094
                  pszValue);
2095
        }
2096
        msSetOutputFormatOption(format, szModKey, pszValue);
4✔
2097
      }
2098
    }
2099
  }
2100

2101
  // netCDF 3D output
2102
  // Tested in msautotest/wxs/wcs_netcdf_3d_output.map
2103
  std::string osExtraDimDataType;
2104
  if (bExtraDimValid && static_cast<int>(oMapExtraDimValues.size()) != nBands) {
174✔
2105
    msDebug("One of the band lack a NETCDF_DIM_%s metadata item.",
×
2106
            osExtraDimName.c_str());
2107
    bExtraDimValid = false;
2108
  }
2109
  if (bExtraDimValid) {
174✔
2110
    for (const auto &keyValue : oMapExtraDimValues) {
4✔
2111
      const auto &osVal = keyValue.second;
2112
      const CPLValueType eType = CPLGetValueType(osVal.c_str());
3✔
2113
      if (eType == CPL_VALUE_STRING) {
3✔
2114
        osExtraDimDataType = "string";
2115
        break;
2116
      } else if (eType == CPL_VALUE_INTEGER) {
3✔
2117
        if (osExtraDimDataType.empty() || osExtraDimDataType == "integer") {
3✔
2118
          osExtraDimDataType = "integer";
2119
          const GIntBig nVal = CPLAtoGIntBig(osVal.c_str());
3✔
2120
          if (nVal > INT_MAX || nVal < INT_MIN) {
3✔
2121
            osExtraDimDataType = "integer64";
2122
          }
2123
        }
2124
      } else {
2125
        osExtraDimDataType = "double";
2126
      }
2127
    }
2128
    if (osExtraDimDataType == "string") {
1✔
2129
      msDebug("One of the value for the NETCDF_DIM_%s metadata is a string. "
×
2130
              "This is not supported",
2131
              osExtraDimName.c_str());
2132
      bExtraDimValid = false;
2133
    }
2134
  }
2135
  if (bExtraDimValid) {
2136
    // Cf
2137
    // https://gdal.org/drivers/raster/netcdf.html#creation-of-multidimensional-files-with-createcopy-2d-raster-api
2138

2139
    // Define the name of the extra dimensions
2140
    {
2141
      std::string osValue;
2142
      osValue = '{';
2143
      osValue += osExtraDimName;
2144
      osValue += '}';
2145

2146
      msSetOutputFormatOption(format, "mdi_default_NETCDF_DIM_EXTRA",
1✔
2147
                              osValue.c_str());
2148
    }
2149

2150
    // Define the size (in number of samples) and type of the extra dimension
2151
    {
2152
      std::string osKey;
2153
      osKey = "mdi_default_NETCDF_DIM_";
2154
      osKey += osExtraDimName;
2155
      osKey += "_DEF";
2156

2157
      std::string osValue;
2158
      osValue = '{';
2159
      osValue += CPLSPrintf("%d", nBands);
1✔
2160
      osValue += ',';
2161
      if (osExtraDimDataType == "integer")
1✔
2162
        osValue += '4';
2163
      else if (osExtraDimDataType == "integer64")
×
2164
        osValue += "10";
2165
      else /* if ( osExtraDimDataType == "double" ) */
2166
        osValue += '6';
2167
      osValue += '}';
2168

2169
      msSetOutputFormatOption(format, osKey.c_str(), osValue.c_str());
1✔
2170
    }
2171

2172
    // Define the values along the extra dimension
2173
    {
2174
      std::string osKey;
2175
      osKey = "mdi_default_NETCDF_DIM_";
2176
      osKey += osExtraDimName;
2177
      osKey += "_VALUES";
2178
      std::string osValue;
2179
      osValue = '{';
2180
      bool bFirstVal = true;
2181
      for (const auto &keyValue : oMapExtraDimValues) {
4✔
2182
        const auto &osVal = keyValue.second;
2183
        if (!bFirstVal)
3✔
2184
          osValue += ',';
2185
        else
2186
          bFirstVal = false;
2187
        osValue += osVal;
2188
      }
2189
      osValue += '}';
2190

2191
      msSetOutputFormatOption(format, osKey.c_str(), osValue.c_str());
1✔
2192
    }
2193
  }
2194

2195
  msFreeCharArray(papszBandNumbers, nBands);
174✔
2196
}
2197

2198
/************************************************************************/
2199
/*               msWCSApplySourceDatasetMetadata()                      */
2200
/************************************************************************/
2201

2202
void msWCSApplySourceDatasetMetadata(layerObj *lp, outputFormatObj *format,
47✔
2203
                                     const char *bandlist, void *hDSIn) {
2204
  /* Automatic forwarding of input dataset metadata if it is netCDF and the */
2205
  /* output is netCDF as well, and wcs_outputformat_netCDF_mdi* are */
2206
  /* not defined. */
2207
  GDALDatasetH hDS = (GDALDatasetH)hDSIn;
2208
  if (hDS && GDALGetDatasetDriver(hDS) &&
47✔
2209
      EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hDS)), "netCDF") &&
94✔
2210
      EQUAL(format->driver, "GDAL/netCDF")) {
3✔
2211
    const char *pszKey;
2212
    char szKeyBeginning[256];
2213
    size_t nKeyBeginningLength;
2214
    int bWCSMetadataFound = MS_FALSE;
2215

2216
    snprintf(szKeyBeginning, sizeof(szKeyBeginning), "wcs_outputformat_%s_mdi_",
3✔
2217
             format->name);
2218
    nKeyBeginningLength = strlen(szKeyBeginning);
3✔
2219

2220
    for (pszKey = msFirstKeyFromHashTable(&(lp->metadata)); pszKey != NULL;
73✔
2221
         pszKey = msNextKeyFromHashTable(&(lp->metadata), pszKey)) {
70✔
2222
      if (strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0) {
70✔
2223
        bWCSMetadataFound = MS_TRUE;
2224
        break;
2225
      }
2226
    }
2227
    if (!bWCSMetadataFound) {
3✔
2228
      int nBands = 0;
3✔
2229
      char **papszBandNumbers = msStringSplit(bandlist, ',', &nBands);
3✔
2230

2231
      std::string osExtraDimName;
2232
      // Special processing if the input dataset is a 3D one
2233
      {
2234
        // Check if extra dimensions are declared on the source dataset,
2235
        // and if so, if there's just a single one.
2236
        const char *pszDimExtraWithCurl =
2237
            GDALGetMetadataItem(hDS, "NETCDF_DIM_EXTRA", nullptr);
3✔
2238
        if (pszDimExtraWithCurl &&
3✔
2239
            strchr(pszDimExtraWithCurl, ',') == nullptr &&
3✔
2240
            pszDimExtraWithCurl[0] == '{' &&
3✔
2241
            pszDimExtraWithCurl[strlen(pszDimExtraWithCurl) - 1] == '}') {
3✔
2242
          osExtraDimName.append(pszDimExtraWithCurl + 1,
3✔
2243
                                strlen(pszDimExtraWithCurl) - 2);
2244

2245
          // Declare the extra dimension name
2246
          msSetOutputFormatOption(format, "mdi_default_NETCDF_DIM_EXTRA",
3✔
2247
                                  pszDimExtraWithCurl);
2248

2249
          // Declare the extra dimension definition: size + data type
2250
          const char *pszDimExtraDef = GDALGetMetadataItem(
3✔
2251
              hDS, ("NETCDF_DIM_" + osExtraDimName + "_DEF").c_str(), nullptr);
3✔
2252
          if (pszDimExtraDef && pszDimExtraDef[0] == '{' &&
3✔
2253
              pszDimExtraDef[strlen(pszDimExtraDef) - 1] == '}') {
3✔
2254
            const auto tokens = msStringSplit(
2255
                std::string(pszDimExtraDef + 1, strlen(pszDimExtraDef) - 2)
6✔
2256
                    .c_str(),
2257
                ',');
3✔
2258
            if (tokens.size() == 2) {
3✔
2259
              const auto &varType = tokens[1];
2260
              msSetOutputFormatOption(
3✔
2261
                  format,
2262
                  ("mdi_default_NETCDF_DIM_" + osExtraDimName + "_DEF").c_str(),
6✔
2263
                  (std::string("{") + CPLSPrintf("%d", nBands) + ',' + varType +
12✔
2264
                   '}')
2265
                      .c_str());
2266
            }
2267
          }
3✔
2268

2269
          // Declare the extra dimension values
2270
          const char *pszDimExtraValues = GDALGetMetadataItem(
3✔
2271
              hDS, ("NETCDF_DIM_" + osExtraDimName + "_VALUES").c_str(),
3✔
2272
              nullptr);
2273
          if (pszDimExtraValues && pszDimExtraValues[0] == '{' &&
3✔
2274
              pszDimExtraValues[strlen(pszDimExtraValues) - 1] == '}') {
1✔
2275
            const auto tokens =
2276
                msStringSplit(std::string(pszDimExtraValues + 1,
2✔
2277
                                          strlen(pszDimExtraValues) - 2)
2278
                                  .c_str(),
2279
                              ',');
1✔
2280
            if (static_cast<int>(tokens.size()) == GDALGetRasterCount(hDS)) {
1✔
2281
              std::string osValue = "{";
1✔
2282
              for (int i = 0; i < nBands; i++) {
3✔
2283
                int nSrcBand = atoi(papszBandNumbers[i]);
2✔
2284
                assert(nSrcBand >= 1 &&
2285
                       nSrcBand <= static_cast<int>(tokens.size()));
2286
                if (i > 0)
2✔
2287
                  osValue += ',';
2288
                osValue += tokens[nSrcBand - 1];
2✔
2289
              }
2290
              osValue += '}';
2291

2292
              msSetOutputFormatOption(
1✔
2293
                  format,
2294
                  ("mdi_default_NETCDF_DIM_" + osExtraDimName + "_VALUES")
2✔
2295
                      .c_str(),
2296
                  osValue.c_str());
2297
            }
2298
          } else if (pszDimExtraValues) {
1✔
2299
            // If there's a single value
2300
            msSetOutputFormatOption(
2✔
2301
                format,
2302
                ("mdi_default_NETCDF_DIM_" + osExtraDimName + "_VALUES")
4✔
2303
                    .c_str(),
2304
                pszDimExtraValues);
2305
          }
2306
        }
2307
      }
2308

2309
      {
2310
        CSLConstList papszMD = GDALGetMetadata(hDS, NULL);
3✔
2311
        if (papszMD) {
3✔
2312
          for (CSLConstList papszIter = papszMD; *papszIter; ++papszIter) {
88✔
2313
            // Copy netCDF global attributes, as well as the ones
2314
            // of the extra dimension for 3D netCDF files
2315
            if (STARTS_WITH(*papszIter, "NC_GLOBAL#") ||
85✔
2316
                (!osExtraDimName.empty() &&
63✔
2317
                 STARTS_WITH(*papszIter, osExtraDimName.c_str()) &&
63✔
2318
                 (*papszIter)[osExtraDimName.size()] == '#')) {
9✔
2319
              char *pszKey = nullptr;
31✔
2320
              const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
31✔
2321
              if (pszKey && pszValue) {
31✔
2322
                char szKey[256];
2323
                snprintf(szKey, sizeof(szKey), "mdi_default_%s", pszKey);
2324
                msSetOutputFormatOption(format, szKey, pszValue);
31✔
2325
              }
2326
              CPLFree(pszKey);
31✔
2327
            }
2328
          }
2329
        }
2330
      }
2331

2332
      for (int i = 0; i < nBands; i++) {
7✔
2333
        int nSrcBand = atoi(papszBandNumbers[i]);
4✔
2334
        int nDstBand = i + 1;
4✔
2335
        GDALRasterBandH hBand = GDALGetRasterBand(hDS, nSrcBand);
4✔
2336
        if (hBand) {
4✔
2337
          CSLConstList papszMD = GDALGetMetadata(hBand, NULL);
4✔
2338
          if (papszMD) {
4✔
2339
            for (CSLConstList papszIter = papszMD; *papszIter; ++papszIter) {
30✔
2340
              char *pszKey = nullptr;
26✔
2341
              const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
26✔
2342
              if (pszKey && pszValue && !EQUAL(pszKey, "grid_name") &&
26✔
2343
                  !EQUAL(pszKey, "grid_mapping")) {
24✔
2344
                char szKey[256];
2345
                snprintf(szKey, sizeof(szKey), "mdi_BAND_%d_default_%s",
2346
                         nDstBand, pszKey);
2347
                msSetOutputFormatOption(format, szKey, pszValue);
22✔
2348
              }
2349
              CPLFree(pszKey);
26✔
2350
            }
2351
          }
2352
        }
2353
      }
2354
      msFreeCharArray(papszBandNumbers, nBands);
3✔
2355
    }
2356
  }
2357
}
47✔
2358

2359
/************************************************************************/
2360
/*                          msWCSGetCoverage()                          */
2361
/************************************************************************/
2362

2363
static int msWCSGetCoverage(mapObj *map, cgiRequestObj *request,
48✔
2364
                            wcsParamsObj *params, owsRequestObj *ows_request) {
2365
  imageObj *image;
2366
  layerObj *lp;
2367
  int status, i;
2368
  const char *value;
2369
  outputFormatObj *format;
2370
  char *bandlist = NULL;
48✔
2371
  size_t bufferSize = 0;
2372
  char numbands[12]; /* should be large enough to hold the number of bands in
2373
                        the bandlist */
2374
  coverageMetadataObj cm;
2375
  rectObj reqextent;
2376
  rectObj covextent;
2377
  rasterBufferObj rb;
2378
  int doDrawRasterLayerDraw = MS_TRUE;
2379
  GDALDatasetH hDS = NULL;
2380

2381
  char *coverageName;
2382

2383
  /* make sure all required parameters are available (at least the easy ones) */
2384
  if (!params->crs) {
48✔
2385
    msSetError(MS_WCSERR, "Required parameter CRS was not supplied.",
1✔
2386
               "msWCSGetCoverage()");
2387
    return msWCSException(map, "MissingParameterValue", "crs", params->version);
1✔
2388
  }
2389

2390
  if (!params->time && !params->bbox.minx && !params->bbox.miny &&
47✔
2391
      !params->bbox.maxx && !params->bbox.maxy) {
13✔
2392
    msSetError(MS_WCSERR, "One of BBOX or TIME is required",
×
2393
               "msWCSGetCoverage()");
2394
    return msWCSException(map, "MissingParameterValue", "bbox/time",
×
2395
                          params->version);
×
2396
  }
2397

2398
  if (params->coverages == NULL || params->coverages[0] == NULL) {
47✔
2399
    msSetError(MS_WCSERR, "Required parameter COVERAGE was not supplied.",
1✔
2400
               "msWCSGetCoverage()");
2401
    return msWCSException(map, "MissingParameterValue", "coverage",
1✔
2402
                          params->version);
1✔
2403
  }
2404

2405
  /* For WCS 1.1, we need to normalize the axis order of the BBOX and
2406
     resolution values some coordinate systems (eg. EPSG geographic) */
2407
  if (strncasecmp(params->version, "1.0", 3) != 0 && params->crs != NULL &&
46✔
2408
      strncasecmp(params->crs, "urn:", 4) == 0) {
22✔
2409
    projectionObj proj;
2410

2411
    msInitProjection(&proj);
19✔
2412
    msProjectionInheritContextFrom(&proj, &(map->projection));
19✔
2413
    if (msLoadProjectionString(&proj, (char *)params->crs) == 0) {
19✔
2414
      msAxisNormalizePoints(&proj, 1, &(params->bbox.minx),
19✔
2415
                            &(params->bbox.miny));
2416
      msAxisNormalizePoints(&proj, 1, &(params->bbox.maxx),
19✔
2417
                            &(params->bbox.maxy));
2418
      msAxisNormalizePoints(&proj, 1, &(params->resx), &(params->resy));
19✔
2419
      msAxisNormalizePoints(&proj, 1, &(params->originx), &(params->originy));
19✔
2420
    } else
2421
      msResetErrorList();
×
2422
    msFreeProjection(&proj);
19✔
2423
  }
2424

2425
  /* find the layer we are working with */
2426
  lp = NULL;
2427
  for (i = 0; i < map->numlayers; i++) {
61✔
2428
    coverageName = msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO",
60✔
2429
                                          "name", GET_LAYER(map, i)->name);
60✔
2430
    if (coverageName != NULL && EQUAL(coverageName, params->coverages[0]) &&
105✔
2431
        (msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers,
45✔
2432
                          ows_request->numlayers))) {
2433
      lp = GET_LAYER(map, i);
45✔
2434
      free(coverageName);
45✔
2435
      break;
45✔
2436
    }
2437
    free(coverageName);
15✔
2438
  }
2439

2440
  if (lp == NULL) {
46✔
2441
    msSetError(
1✔
2442
        MS_WCSERR,
2443
        "COVERAGE=%s not found, not in supported layer list. A layer might be disabled for \
2444
this request. Check wcs/ows_enable_request settings.",
2445
        "msWCSGetCoverage()", params->coverages[0]);
1✔
2446
    return msWCSException(map, "InvalidParameterValue", "coverage",
1✔
2447
                          params->version);
1✔
2448
  }
2449

2450
  /* make sure the layer is on */
2451
  lp->status = MS_ON;
45✔
2452

2453
  /* If the layer has no projection set, set it to the map's projection (#4079)
2454
   */
2455
  if (lp->projection.numargs <= 0) {
45✔
2456
    char *map_original_srs = msGetProjectionString(&(map->projection));
×
2457
    if (msLoadProjectionString(&(lp->projection), map_original_srs) != 0) {
×
2458
      msSetError(
×
2459
          MS_WCSERR,
2460
          "Error when setting map projection to a layer with no projection",
2461
          "msWCSGetCoverage()");
2462
      free(map_original_srs);
×
2463
      return msWCSException(map, NULL, NULL, params->version);
×
2464
    }
2465
    free(map_original_srs);
×
2466
  }
2467

2468
  /* we need the coverage metadata, since things like numbands may not be
2469
   * available otherwise */
2470
  status = msWCSGetCoverageMetadata(lp, &cm);
45✔
2471
  if (status != MS_SUCCESS)
45✔
2472
    return MS_FAILURE;
2473

2474
  /* fill in bands rangeset info, if required.  */
2475
  msWCSSetDefaultBandsRangeSetInfo(params, &cm, lp);
45✔
2476

2477
  /* handle the response CRS, that is, set the map object projection */
2478
  if (params->response_crs || params->crs) {
45✔
2479
    int iUnits;
2480
    const char *crs_to_use = params->response_crs;
2481
    if (crs_to_use == NULL)
45✔
2482
      crs_to_use = params->crs;
43✔
2483

2484
    if (strcasecmp(crs_to_use, "imageCRS") == 0) {
45✔
2485
      /* use layer native CRS, and rework bounding box accordingly */
2486
      if (msWCSGetCoverage_ImageCRSSetup(map, params, &cm, lp) != MS_SUCCESS) {
3✔
2487
        msWCSFreeCoverageMetadata(&cm);
×
2488
        return MS_FAILURE;
×
2489
      }
2490
    } else if (strncasecmp(crs_to_use, "urn:ogc:def:crs:", 16) == 0 ||
42✔
2491
               strncasecmp(crs_to_use, "http://www.opengis.net/def/crs/", 31) ==
24✔
2492
                   0 ||
24✔
2493
               strchr(crs_to_use, ':') != NULL) {
2494
      /* Handles URNs, OGC URIs, and any AUTHORITY:CODE pattern
2495
      ** e.g. EPSG:4326, ESRI:54052, CRS:84, IAU_2015:30100 */
2496
      if (msLoadProjectionString(&(map->projection), (char *)crs_to_use) != 0) {
42✔
NEW
2497
        msSetError(MS_WCSERR, "Unsupported or unknown CRS '%s'.",
×
2498
                   "msWCSGetCoverage()", crs_to_use);
NEW
2499
        return msWCSException(map, "InvalidParameterValue", "srs",
×
NEW
2500
                              params->version);
×
2501
      }
2502
    } else {
NEW
2503
      msSetError(
×
2504
          MS_WCSERR,
2505
          "Unsupported SRS format '%s' (expected AUTHORITY:CODE, URN, or URI).",
2506
          "msWCSGetCoverage()", crs_to_use);
2507
      return msWCSException(map, "InvalidParameterValue", "srs",
×
2508
                            params->version);
×
2509
    }
2510

2511
    iUnits = GetMapserverUnitUsingProj(&(map->projection));
45✔
2512
    if (iUnits != -1)
45✔
2513
      map->units = static_cast<MS_UNITS>(iUnits);
45✔
2514
  }
2515

2516
  /* did we get a TIME value (support only a single value for now) */
2517
  if (params->time) {
45✔
2518
    int tli;
2519
    layerObj *tlp = NULL;
2520

2521
    /* need to handle NOW case */
2522

2523
    /* check format of TIME parameter */
2524
    if (strchr(params->time, ',')) {
1✔
2525
      msWCSFreeCoverageMetadata(&cm);
×
2526
      msSetError(MS_WCSERR,
×
2527
                 "Temporal lists are not supported, only individual values.",
2528
                 "msWCSGetCoverage()");
2529
      return msWCSException(map, "InvalidParameterValue", "time",
×
2530
                            params->version);
×
2531
    }
2532
    if (strchr(params->time, '/')) {
1✔
2533
      msWCSFreeCoverageMetadata(&cm);
×
2534
      msSetError(MS_WCSERR,
×
2535
                 "Temporal ranges are not supported, only individual values.",
2536
                 "msWCSGetCoverage()");
2537
      return msWCSException(map, "InvalidParameterValue", "time",
×
2538
                            params->version);
×
2539
    }
2540

2541
    /* TODO: will need to expand this check if a time period is supported */
2542
    value = msOWSLookupMetadata(&(lp->metadata), "CO", "timeposition");
1✔
2543
    if (!value) {
1✔
2544
      msWCSFreeCoverageMetadata(&cm);
×
2545
      msSetError(MS_WCSERR,
×
2546
                 "The coverage does not support temporal subsetting.",
2547
                 "msWCSGetCoverage()");
2548
      return msWCSException(map, "InvalidParameterValue", "time",
×
2549
                            params->version);
×
2550
    }
2551

2552
    /* check if timestamp is covered by the wcs_timeposition definition */
2553
    if (msValidateTimeValue(params->time, value) == MS_FALSE) {
1✔
2554
      msWCSFreeCoverageMetadata(&cm);
×
2555
      msSetError(MS_WCSERR, "The coverage does not have a time position of %s.",
×
2556
                 "msWCSGetCoverage()", params->time);
2557
      return msWCSException(map, "InvalidParameterValue", "time",
×
2558
                            params->version);
×
2559
    }
2560

2561
    /* make sure layer is tiled appropriately */
2562
    if (!lp->tileindex) {
1✔
2563
      msWCSFreeCoverageMetadata(&cm);
×
2564
      msSetError(
×
2565
          MS_WCSERR,
2566
          "Underlying layer is not tiled, unable to do temporal subsetting.",
2567
          "msWCSGetCoverage()");
2568
      return msWCSException(map, NULL, NULL, params->version);
×
2569
    }
2570
    tli = msGetLayerIndex(map, lp->tileindex);
1✔
2571
    if (tli == -1) {
1✔
2572
      msWCSFreeCoverageMetadata(&cm);
×
2573
      msSetError(MS_WCSERR,
×
2574
                 "Underlying layer does not use appropriate tiling mechanism.",
2575
                 "msWCSGetCoverage()");
2576
      return msWCSException(map, NULL, NULL, params->version);
×
2577
    }
2578

2579
    tlp = (GET_LAYER(map, tli));
1✔
2580

2581
    /* make sure there is enough information to filter */
2582
    value = msOWSLookupMetadata(&(lp->metadata), "CO", "timeitem");
1✔
2583
    if (!tlp->filteritem && !value) {
1✔
2584
      msWCSFreeCoverageMetadata(&cm);
×
2585
      msSetError(MS_WCSERR, "Not enough information available to filter.",
×
2586
                 "msWCSGetCoverage()");
2587
      return msWCSException(map, NULL, NULL, params->version);
×
2588
    }
2589

2590
    /* override filteritem if specified in metadata */
2591
    if (value) {
×
2592
      if (tlp->filteritem)
1✔
2593
        free(tlp->filteritem);
×
2594
      tlp->filteritem = msStrdup(value);
1✔
2595
    }
2596

2597
    /* finally set the filter */
2598
    msLayerSetTimeFilter(tlp, params->time, value);
1✔
2599
  }
2600

2601
  if (strncasecmp(params->version, "1.0", 3) == 0)
45✔
2602
    status = msWCSGetCoverageBands10(map, request, params, lp, &bandlist);
24✔
2603
  else
2604
    status = msWCSGetCoverageBands11(map, request, params, lp, &bandlist);
21✔
2605
  if (status != MS_SUCCESS) {
45✔
2606
    msWCSFreeCoverageMetadata(&cm);
1✔
2607
    return status;
1✔
2608
  }
2609

2610
  /* did we get BBOX values? if not use the extent stored in the
2611
   * coverageMetadataObj */
2612
  if (fabs((params->bbox.maxx - params->bbox.minx)) < 0.000000000001 ||
44✔
2613
      fabs(params->bbox.maxy - params->bbox.miny) < 0.000000000001) {
44✔
2614
    params->bbox = cm.extent;
×
2615

2616
    /* WCS 1.1 boundbox is center of pixel oriented. */
2617
    if (strncasecmp(params->version, "1.1", 3) == 0) {
×
2618
      params->bbox.minx += cm.geotransform[1] / 2 + cm.geotransform[2] / 2;
×
2619
      params->bbox.maxx -= cm.geotransform[1] / 2 + cm.geotransform[2] / 2;
×
2620
      params->bbox.maxy += cm.geotransform[4] / 2 + cm.geotransform[5] / 2;
×
2621
      params->bbox.miny -= cm.geotransform[4] / 2 + cm.geotransform[5] / 2;
×
2622
    }
2623
  }
2624

2625
  /* WCS 1.1+ GridOrigin is effectively resetting the minx/maxy
2626
     BOUNDINGBOX values, so apply that here */
2627
  if (params->originx != 0.0 || params->originy != 0.0) {
44✔
2628
    assert(strncasecmp(params->version, "1.0", 3) !=
2629
           0); /* should always be 1.0 in this logic. */
2630
    params->bbox.minx = params->originx;
16✔
2631
    params->bbox.maxy = params->originy;
16✔
2632
  }
2633

2634
  /* if necessary, project the BBOX to the map->projection */
2635
  if (params->response_crs && params->crs) {
44✔
2636
    projectionObj tmp_proj;
2637

2638
    msInitProjection(&tmp_proj);
2✔
2639
    msProjectionInheritContextFrom(&tmp_proj, &(map->projection));
2✔
2640
    if (msLoadProjectionString(&tmp_proj, (char *)params->crs) != 0) {
2✔
2641
      msWCSFreeCoverageMetadata(&cm);
×
2642
      return msWCSException(map, NULL, NULL, params->version);
×
2643
    }
2644
    msProjectRect(&tmp_proj, &map->projection, &(params->bbox));
2✔
2645
    msFreeProjection(&tmp_proj);
2✔
2646
  }
2647

2648
  /* in WCS 1.1 the default is full resolution */
2649
  if (strncasecmp(params->version, "1.1", 3) == 0 && params->resx == 0.0 &&
44✔
2650
      params->resy == 0.0) {
2✔
2651
    params->resx = cm.geotransform[1];
2✔
2652
    params->resy = fabs(cm.geotransform[5]);
2✔
2653
  }
2654

2655
  /* compute width/height from BBOX and cellsize.  */
2656
  if ((params->resx == 0.0 || params->resy == 0.0) && params->width != 0 &&
44✔
2657
      params->height != 0) {
18✔
2658
    assert(strncasecmp(params->version, "1.0", 3) ==
2659
           0); /* should always be 1.0 in this logic. */
2660
    params->resx = (params->bbox.maxx - params->bbox.minx) / params->width;
18✔
2661
    params->resy = (params->bbox.maxy - params->bbox.miny) / params->height;
18✔
2662
  }
2663

2664
  /* compute cellsize/res from bbox and raster size. */
2665
  if ((params->width == 0 || params->height == 0) && params->resx != 0 &&
44✔
2666
      params->resy != 0) {
24✔
2667

2668
    /* WCS 1.0 boundbox is edge of pixel oriented. */
2669
    if (strncasecmp(params->version, "1.0", 3) == 0) {
24✔
2670
      params->width =
5✔
2671
          (int)((params->bbox.maxx - params->bbox.minx) / params->resx + 0.5);
5✔
2672
      params->height =
5✔
2673
          (int)((params->bbox.maxy - params->bbox.miny) / params->resy + 0.5);
5✔
2674
    } else {
2675
      params->width =
19✔
2676
          (int)((params->bbox.maxx - params->bbox.minx) / params->resx +
19✔
2677
                1.000001);
2678
      params->height =
19✔
2679
          (int)((params->bbox.maxy - params->bbox.miny) / params->resy +
19✔
2680
                1.000001);
2681

2682
      /* recompute bounding box so we get exactly the origin and
2683
         resolution requested. */
2684
      params->bbox.maxx =
19✔
2685
          params->bbox.minx + (params->width - 1) * params->resx;
19✔
2686
      params->bbox.miny =
19✔
2687
          params->bbox.maxy - (params->height - 1) * params->resy;
19✔
2688
    }
2689
  }
2690

2691
  /* are we still underspecified?  */
2692
  if ((params->width == 0 || params->height == 0) &&
44✔
2693
      (params->resx == 0.0 || params->resy == 0.0)) {
×
2694
    msWCSFreeCoverageMetadata(&cm);
×
2695
    msSetError(MS_WCSERR,
×
2696
               "A non-zero RESX/RESY or WIDTH/HEIGHT is required but neither "
2697
               "was provided.",
2698
               "msWCSGetCoverage()");
2699
    return msWCSException(map, "MissingParameterValue",
×
2700
                          "width/height/resx/resy", params->version);
×
2701
  }
2702

2703
  map->cellsize = params->resx;
44✔
2704

2705
  /* Do we need to force special handling?  */
2706
  if (fabs(params->resx / params->resy - 1.0) > 0.001) {
44✔
2707
    map->gt.need_geotransform = MS_TRUE;
6✔
2708
    if (map->debug)
6✔
2709
      msDebug("RESX and RESY don't match.  Using geotransform/resample.\n");
×
2710
  }
2711

2712
  /* Do we have a specified interpolation method */
2713
  if (params->interpolation != NULL) {
44✔
2714
    if (strncasecmp(params->interpolation, "NEAREST", 7) == 0)
2✔
2715
      msLayerSetProcessingKey(lp, "RESAMPLE", "NEAREST");
×
2716
    else if (strcasecmp(params->interpolation, "BILINEAR") == 0)
2✔
2717
      msLayerSetProcessingKey(lp, "RESAMPLE", "BILINEAR");
2✔
2718
    else if (strcasecmp(params->interpolation, "AVERAGE") == 0)
×
2719
      msLayerSetProcessingKey(lp, "RESAMPLE", "AVERAGE");
×
2720
    else {
2721
      msWCSFreeCoverageMetadata(&cm);
×
2722
      msSetError(
×
2723
          MS_WCSERR,
2724
          "INTERPOLATION=%s specifies an unsupported interpolation method.",
2725
          "msWCSGetCoverage()", params->interpolation);
2726
      return msWCSException(map, "InvalidParameterValue", "interpolation",
×
2727
                            params->version);
×
2728
    }
2729
  }
2730

2731
  /* apply region and size to map object.  */
2732
  map->width = params->width;
44✔
2733
  map->height = params->height;
44✔
2734

2735
  /* Are we exceeding the MAXSIZE limit on result size? */
2736
  if (map->width > map->maxsize || map->height > map->maxsize) {
44✔
2737
    msWCSFreeCoverageMetadata(&cm);
1✔
2738
    msSetError(MS_WCSERR,
1✔
2739
               "Raster size out of range, width and height of resulting "
2740
               "coverage must be no more than MAXSIZE=%d.",
2741
               "msWCSGetCoverage()", map->maxsize);
2742

2743
    return msWCSException(map, "InvalidParameterValue", "width/height",
1✔
2744
                          params->version);
1✔
2745
  }
2746

2747
  /* adjust OWS BBOX to MapServer's pixel model */
2748
  if (strncasecmp(params->version, "1.0", 3) == 0) {
43✔
2749
    params->bbox.minx += params->resx * 0.5;
22✔
2750
    params->bbox.miny += params->resy * 0.5;
22✔
2751
    params->bbox.maxx -= params->resx * 0.5;
22✔
2752
    params->bbox.maxy -= params->resy * 0.5;
22✔
2753
  }
2754

2755
  map->extent = params->bbox;
43✔
2756

2757
  map->cellsize = params->resx; /* pick one, MapServer only supports square
43✔
2758
                                   cells (what about msAdjustExtent here!) */
2759

2760
  if (params->width == 1 || params->height == 1)
43✔
2761
    msMapComputeGeotransformEx(map, params->resx, params->resy);
×
2762
  else
2763
    msMapComputeGeotransform(map);
43✔
2764

2765
  /* Do we need to fake out stuff for rotated support? */
2766
  if (map->gt.need_geotransform)
43✔
2767
    msMapSetFakedExtent(map);
6✔
2768

2769
  map->projection.gt = map->gt;
43✔
2770

2771
  /* check for overlap */
2772

2773
  /* get extent of bbox passed, and reproject */
2774
  reqextent.minx = map->extent.minx;
43✔
2775
  reqextent.miny = map->extent.miny;
43✔
2776
  reqextent.maxx = map->extent.maxx;
43✔
2777
  reqextent.maxy = map->extent.maxy;
43✔
2778

2779
  /* reproject incoming bbox */
2780
  msProjectRect(&map->projection, &lp->projection, &(reqextent));
43✔
2781

2782
  /* get extent of layer */
2783
  covextent.minx = cm.extent.minx;
43✔
2784
  covextent.miny = cm.extent.miny;
43✔
2785
  covextent.maxx = cm.extent.maxx;
43✔
2786
  covextent.maxy = cm.extent.maxy;
43✔
2787

2788
  if (msRectOverlap(&reqextent, &covextent) == MS_FALSE) {
43✔
2789
    msWCSFreeCoverageMetadata(&cm);
1✔
2790
    msSetError(MS_WCSERR,
1✔
2791
               "Requested BBOX (%.15g,%.15g,%.15g,%.15g) is outside requested "
2792
               "coverage BBOX (%.15g,%.15g,%.15g,%.15g)",
2793
               "msWCSGetCoverage()", reqextent.minx, reqextent.miny,
2794
               reqextent.maxx, reqextent.maxy, covextent.minx, covextent.miny,
2795
               covextent.maxx, covextent.maxy);
2796
    return msWCSException(map, "NoApplicableCode", "bbox", params->version);
1✔
2797
  }
2798

2799
  /* check and make sure there is a format, and that it's valid (TODO: make sure
2800
   * in the layer metadata) */
2801
  if (!params->format) {
42✔
2802
    msWCSFreeCoverageMetadata(&cm);
1✔
2803
    msSetError(MS_WCSERR, "Missing required FORMAT parameter.",
1✔
2804
               "msWCSGetCoverage()");
2805
    return msWCSException(map, "MissingParameterValue", "format",
1✔
2806
                          params->version);
1✔
2807
  }
2808
  msApplyDefaultOutputFormats(map);
41✔
2809
  if (msGetOutputFormatIndex(map, params->format) == -1) {
41✔
2810
    msWCSFreeCoverageMetadata(&cm);
1✔
2811
    msSetError(MS_WCSERR, "Unrecognized value for the FORMAT parameter.",
1✔
2812
               "msWCSGetCoverage()");
2813
    return msWCSException(map, "InvalidParameterValue", "format",
1✔
2814
                          params->version);
1✔
2815
  }
2816

2817
  /* create a temporary outputformat (we likely will need to tweak parts) */
2818
  format = msCloneOutputFormat(msSelectOutputFormat(map, params->format));
40✔
2819
  msApplyOutputFormat(&(map->outputformat), format, MS_NOOVERRIDE);
40✔
2820

2821
  if (!bandlist) { /* build a bandlist (default is ALL bands) */
40✔
2822
    bufferSize = cm.bandcount * 30 + 30;
39✔
2823
    bandlist = (char *)msSmallMalloc(bufferSize);
39✔
2824
    strcpy(bandlist, "1");
2825
    for (i = 1; i < cm.bandcount; i++)
39✔
2826
      snprintf(bandlist + strlen(bandlist), bufferSize - strlen(bandlist),
×
2827
               ",%d", i + 1);
2828
  }
2829

2830
  /* apply nullvalue to the output format object if we have it */
2831
  if ((value = msOWSLookupMetadata(&(lp->metadata), "CO",
40✔
2832
                                   "rangeset_nullvalue")) != NULL) {
2833
    msSetOutputFormatOption(map->outputformat, "NULLVALUE", value);
30✔
2834
  }
2835

2836
  msLayerSetProcessingKey(lp, "BANDS", bandlist);
40✔
2837
  snprintf(numbands, sizeof(numbands), "%d", msCountChars(bandlist, ',') + 1);
40✔
2838
  msSetOutputFormatOption(map->outputformat, "BAND_COUNT", numbands);
40✔
2839

2840
  msWCSApplyLayerCreationOptions(lp, map->outputformat, bandlist);
40✔
2841
  msWCSApplyLayerMetadataItemOptions(lp, map->outputformat, bandlist);
40✔
2842

2843
  if (lp->tileindex == NULL && lp->data != NULL && strlen(lp->data) > 0 &&
40✔
2844
      lp->connectiontype != MS_KERNELDENSITY) {
10✔
2845
    if (msDrawRasterLayerLowCheckIfMustDraw(map, lp)) {
10✔
2846
      char *decrypted_path = NULL;
10✔
2847
      char szPath[MS_MAXPATHLEN];
2848
      hDS = (GDALDatasetH)msDrawRasterLayerLowOpenDataset(
10✔
2849
          map, lp, lp->data, szPath, &decrypted_path);
10✔
2850
      msFree(decrypted_path);
10✔
2851
      if (hDS) {
10✔
2852
        msWCSApplyDatasetMetadataAsCreationOptions(lp, map->outputformat,
10✔
2853
                                                   bandlist, hDS);
2854
        msWCSApplySourceDatasetMetadata(lp, map->outputformat, bandlist, hDS);
10✔
2855
      }
2856
    } else {
2857
      doDrawRasterLayerDraw = MS_FALSE;
2858
    }
2859
  }
2860

2861
  free(bandlist);
40✔
2862

2863
  if (lp->mask) {
40✔
2864
    int maskLayerIdx = msGetLayerIndex(map, lp->mask);
11✔
2865
    layerObj *maskLayer;
2866
    outputFormatObj *altFormat;
2867
    if (maskLayerIdx == -1) {
11✔
2868
      msWCSFreeCoverageMetadata(&cm);
×
2869
      msSetError(MS_MISCERR, "Layer (%s) references unknown mask layer (%s)",
×
2870
                 "msDrawLayer()", lp->name, lp->mask);
2871
      msDrawRasterLayerLowCloseDataset(lp, hDS);
×
2872
      return msWCSException(map, NULL, NULL, params->version);
×
2873
    }
2874
    maskLayer = GET_LAYER(map, maskLayerIdx);
11✔
2875
    if (!maskLayer->maskimage) {
11✔
2876
      int i, retcode;
2877
      int origstatus, origlabelcache;
2878
      char *origImageType = msStrdup(map->imagetype);
11✔
2879
      altFormat = msSelectOutputFormat(map, "png24");
11✔
2880
      msInitializeRendererVTable(altFormat);
11✔
2881
      /* TODO: check the png24 format hasn't been tampered with, i.e. it's agg
2882
       */
2883
      maskLayer->maskimage = msImageCreate(
11✔
2884
          map->width, map->height, altFormat, map->web.imagepath,
2885
          map->web.imageurl, map->resolution, map->defresolution, NULL);
2886
      if (!maskLayer->maskimage) {
11✔
2887
        msWCSFreeCoverageMetadata(&cm);
×
2888
        msSetError(MS_MISCERR, "Unable to initialize mask image.",
×
2889
                   "msDrawLayer()");
2890
        msFree(origImageType);
×
2891
        msDrawRasterLayerLowCloseDataset(lp, hDS);
×
2892
        return msWCSException(map, NULL, NULL, params->version);
×
2893
      }
2894

2895
      /*
2896
       * force the masked layer to status on, and turn off the labelcache so
2897
       * that eventual labels are added to the temporary image instead of being
2898
       * added to the labelcache
2899
       */
2900
      origstatus = maskLayer->status;
11✔
2901
      origlabelcache = maskLayer->labelcache;
11✔
2902
      maskLayer->status = MS_ON;
11✔
2903
      maskLayer->labelcache = MS_OFF;
11✔
2904

2905
      /* draw the mask layer in the temporary image */
2906
      retcode = msDrawLayer(map, maskLayer, maskLayer->maskimage);
11✔
2907
      maskLayer->status = origstatus;
11✔
2908
      maskLayer->labelcache = origlabelcache;
11✔
2909
      if (retcode != MS_SUCCESS) {
11✔
2910
        msWCSFreeCoverageMetadata(&cm);
×
2911
        /* set the imagetype from the original outputformat back (it was removed
2912
         * by msSelectOutputFormat() */
2913
        msFree(map->imagetype);
×
2914
        map->imagetype = origImageType;
×
2915
        msDrawRasterLayerLowCloseDataset(lp, hDS);
×
2916
        return msWCSException(map, NULL, NULL, params->version);
×
2917
      }
2918
      /*
2919
       * hack to work around bug #3834: if we have use an alternate renderer,
2920
       * the symbolset may contain symbols that reference it. We want to remove
2921
       * those references before the altFormat is destroyed to avoid a segfault
2922
       * and/or a leak, and so the the main renderer doesn't pick the cache up
2923
       * thinking it's for him.
2924
       */
2925
      for (i = 0; i < map->symbolset.numsymbols; i++) {
22✔
2926
        if (map->symbolset.symbol[i] != NULL) {
11✔
2927
          symbolObj *s = map->symbolset.symbol[i];
2928
          if (s->renderer == MS_IMAGE_RENDERER(maskLayer->maskimage)) {
11✔
2929
            MS_IMAGE_RENDERER(maskLayer->maskimage)->freeSymbol(s);
×
2930
            s->renderer = NULL;
×
2931
          }
2932
        }
2933
      }
2934
      /* set the imagetype from the original outputformat back (it was removed
2935
       * by msSelectOutputFormat() */
2936
      msFree(map->imagetype);
11✔
2937
      map->imagetype = origImageType;
11✔
2938
    }
2939
  }
2940

2941
  /* create the image object  */
2942
  if (!map->outputformat) {
40✔
2943
    msWCSFreeCoverageMetadata(&cm);
×
2944
    msSetError(MS_WCSERR, "The map outputformat is missing!",
×
2945
               "msWCSGetCoverage()");
2946
    msDrawRasterLayerLowCloseDataset(lp, hDS);
×
2947
    return msWCSException(map, NULL, NULL, params->version);
×
2948
  } else if (MS_RENDERER_RAWDATA(map->outputformat) ||
40✔
2949
             MS_RENDERER_PLUGIN(map->outputformat)) {
2950
    image = msImageCreate(map->width, map->height, map->outputformat,
40✔
2951
                          map->web.imagepath, map->web.imageurl,
2952
                          map->resolution, map->defresolution, NULL);
2953
  } else {
2954
    msWCSFreeCoverageMetadata(&cm);
×
2955
    msSetError(MS_WCSERR, "Map outputformat not supported for WCS!",
×
2956
               "msWCSGetCoverage()");
2957
    msDrawRasterLayerLowCloseDataset(lp, hDS);
×
2958
    return msWCSException(map, NULL, NULL, params->version);
×
2959
  }
2960

2961
  if (image == NULL) {
40✔
2962
    msWCSFreeCoverageMetadata(&cm);
×
2963
    msDrawRasterLayerLowCloseDataset(lp, hDS);
×
2964
    return msWCSException(map, NULL, NULL, params->version);
×
2965
  }
2966
  if (MS_RENDERER_RAWDATA(map->outputformat)) {
40✔
2967
    if (doDrawRasterLayerDraw) {
29✔
2968
      status = msDrawRasterLayerLowWithDataset(map, lp, image, NULL, hDS);
29✔
2969
    } else {
2970
      status = MS_SUCCESS;
2971
    }
2972
  } else {
2973
    status = MS_IMAGE_RENDERER(image)->getRasterBufferHandle(image, &rb);
11✔
2974
    if (MS_UNLIKELY(status == MS_FAILURE)) {
11✔
2975
      msWCSFreeCoverageMetadata(&cm);
×
2976
      msDrawRasterLayerLowCloseDataset(lp, hDS);
×
2977
      return MS_FAILURE;
×
2978
    }
2979

2980
    /* Actually produce the "grid". */
2981
    if (doDrawRasterLayerDraw) {
11✔
2982
      status = msDrawRasterLayerLowWithDataset(map, lp, image, &rb, hDS);
11✔
2983
    } else {
2984
      status = MS_SUCCESS;
2985
    }
2986
  }
2987
  msDrawRasterLayerLowCloseDataset(lp, hDS);
40✔
2988

2989
  if (status != MS_SUCCESS) {
40✔
2990
    msWCSFreeCoverageMetadata(&cm);
×
2991
    msFreeImage(image);
×
2992
    return msWCSException(map, NULL, NULL, params->version);
×
2993
  }
2994

2995
  if (strncmp(params->version, "1.1", 3) == 0) {
40✔
2996
    msWCSReturnCoverage11(params, map, image);
19✔
2997
  } else { /* WCS 1.0.0 - just return the binary data with a content type */
2998
    const char *fo_filename;
2999

3000
    /* Do we have a predefined filename? */
3001
    fo_filename = msGetOutputFormatOption(format, "FILENAME", NULL);
21✔
3002
    if (fo_filename)
21✔
3003
      msIO_setHeader("Content-Disposition", "attachment; filename=%s",
8✔
3004
                     fo_filename);
3005

3006
    /* Emit back to client. */
3007
    msOutputFormatResolveFromImage(map, image);
21✔
3008
    msIO_setHeader("Content-Type", "%s", MS_IMAGE_MIME_TYPE(map->outputformat));
42✔
3009
    msIO_sendHeaders();
21✔
3010
    status = msSaveImage(map, image, NULL);
21✔
3011

3012
    if (status != MS_SUCCESS) {
21✔
3013
      /* unfortunately, the image content type will have already been sent
3014
         but that is hard for us to avoid.  The main error that could happen
3015
         here is a misconfigured tmp directory or running out of space. */
3016
      msWCSFreeCoverageMetadata(&cm);
×
3017
      return msWCSException(map, NULL, NULL, params->version);
×
3018
    }
3019
  }
3020

3021
  /* Cleanup */
3022
  msFreeImage(image);
40✔
3023
  msApplyOutputFormat(&(map->outputformat), NULL, MS_NOOVERRIDE);
40✔
3024
  /* msFreeOutputFormat(format); */
3025

3026
  msWCSFreeCoverageMetadata(&cm);
40✔
3027
  return status;
3028
}
3029
#endif /* def USE_WCS_SVR */
3030

3031
/************************************************************************/
3032
/*                           msWCSDispatch()                            */
3033
/*                                                                      */
3034
/*      Entry point for WCS requests                                    */
3035
/************************************************************************/
3036

3037
int msWCSDispatch(mapObj *map, cgiRequestObj *request,
323✔
3038
                  owsRequestObj *ows_request) {
3039
#if defined(USE_WCS_SVR)
3040
  wcs20ParamsObj *params20 = NULL;
3041
  int status, retVal, operation;
3042

3043
  /* If SERVICE is not set or not WCS exit gracefully. */
3044
  if (ows_request->service == NULL || !EQUAL(ows_request->service, "WCS")) {
323✔
3045
    return MS_DONE;
3046
  }
3047

3048
  /* If no REQUEST is set, exit with an error */
3049
  if (ows_request->request == NULL) {
323✔
3050
    /* The request has to be set. */
3051
    msSetError(MS_WCSERR, "Missing REQUEST parameter", "msWCSDispatch()");
1✔
3052
    return msWCSException(map, "MissingParameterValue", "request",
1✔
3053
                          ows_request->version);
1✔
3054
  }
3055

3056
  if (EQUAL(ows_request->request, "GetCapabilities")) {
322✔
3057
    operation = MS_WCS_GET_CAPABILITIES;
3058
  } else if (EQUAL(ows_request->request, "DescribeCoverage")) {
244✔
3059
    operation = MS_WCS_DESCRIBE_COVERAGE;
3060
  } else if (EQUAL(ows_request->request, "GetCoverage")) {
231✔
3061
    operation = MS_WCS_GET_COVERAGE;
3062
  } else {
3063
    msSetError(MS_WCSERR, "Invalid REQUEST parameter \"%s\"", "msWCSDispatch()",
5✔
3064
               ows_request->request);
3065
    return msWCSException(map, "InvalidParameterValue", "request",
5✔
3066
                          ows_request->version);
5✔
3067
  }
3068

3069
  /* Check the number of enabled layers for the REQUEST */
3070
  msOWSRequestLayersEnabled(map, "C", ows_request->request, ows_request);
317✔
3071
  if (ows_request->numlayers == 0) {
317✔
3072
    int caps_globally_enabled = MS_FALSE, disabled = MS_FALSE;
7✔
3073
    const char *enable_request;
3074
    if (operation == MS_WCS_GET_CAPABILITIES) {
7✔
3075
      enable_request =
3076
          msOWSLookupMetadata(&map->web.metadata, "OC", "enable_request");
6✔
3077
      caps_globally_enabled = msOWSParseRequestMetadata(
6✔
3078
          enable_request, "GetCapabilities", &disabled);
3079
    }
3080

3081
    if (caps_globally_enabled == MS_FALSE) {
6✔
3082
      msSetError(MS_WCSERR,
3✔
3083
                 "WCS request not enabled. Check "
3084
                 "wcs/ows_enable_request settings.",
3085
                 "msWCSDispatch()");
3086
      return msWCSException(map, "InvalidParameterValue", "request",
3✔
3087
                            ows_request->version);
3✔
3088
    }
3089
  }
3090

3091
  /* Check the VERSION parameter */
3092
  if (ows_request->version == NULL) {
314✔
3093
    /* If the VERSION parameter is not set, it is either */
3094
    /* an error (Describe and GetCoverage), or it has to */
3095
    /* be determined (GetCapabilities). To determine the */
3096
    /* version, the request has to be fully parsed to    */
3097
    /* obtain the ACCEPTVERSIONS parameter. If this is   */
3098
    /* present also, set version to "2.0.1".             */
3099

3100
    if (operation == MS_WCS_GET_CAPABILITIES) {
2✔
3101
      /* Parse it as if it was a WCS 2.0 request */
3102
      wcs20ParamsObjPtr params_tmp = msWCSCreateParamsObj20();
2✔
3103
      status = msWCSParseRequest20(map, request, ows_request, params_tmp);
2✔
3104
      if (status == MS_FAILURE) {
2✔
3105
        msWCSFreeParamsObj20(params_tmp);
×
3106
        return msWCSException(map, "InvalidParameterValue", "request", "2.0.1");
×
3107
      }
3108

3109
      /* VERSION negotiation */
3110
      if (params_tmp->accept_versions != NULL) {
2✔
3111
        /* choose highest acceptable */
3112
        int i, highest_version = 0;
3113
        char version_string[OWS_VERSION_MAXLEN];
3114
        for (i = 0; params_tmp->accept_versions[i] != NULL; ++i) {
×
3115
          int version = msOWSParseVersionString(params_tmp->accept_versions[i]);
×
3116
          if (version == OWS_VERSION_BADFORMAT) {
×
3117
            msWCSFreeParamsObj20(params_tmp);
×
3118
            return msWCSException(map, "InvalidParameterValue", "version",
×
3119
                                  NULL);
×
3120
          }
3121
          if (version > highest_version) {
×
3122
            highest_version = version;
3123
          }
3124
        }
3125
        msOWSGetVersionString(highest_version, version_string);
×
3126
        params_tmp->version = msStrdup(version_string);
×
3127
        ows_request->version = msStrdup(version_string);
×
3128
      } else {
3129
        /* set to highest acceptable */
3130
        params_tmp->version = msStrdup("2.0.1");
2✔
3131
        ows_request->version = msStrdup("2.0.1");
2✔
3132
      }
3133

3134
      /* check if we can keep the params object */
3135
      if (EQUAL(params_tmp->version, "2.0.1")) {
2✔
3136
        params20 = params_tmp;
3137
      } else {
3138
        msWCSFreeParamsObj20(params_tmp);
×
3139
      }
3140
    } else { /* operation != GetCapabilities */
3141
      /* VERSION is mandatory in other requests */
3142
      msSetError(MS_WCSERR, "VERSION parameter not set.", "msWCSDispatch()");
×
3143
      return msWCSException(map, "InvalidParameterValue", "version", NULL);
×
3144
    }
3145
  } else {
3146
    /* Parse the VERSION parameter */
3147
    int requested_version = msOWSParseVersionString(ows_request->version);
312✔
3148
    if (requested_version == OWS_VERSION_BADFORMAT) {
312✔
3149
      /* Return an error if the VERSION is */
3150
      /* in an unsupported format.         */
3151
      return msWCSException(map, "InvalidParameterValue", "version", NULL);
2✔
3152
    }
3153

3154
    if (operation == MS_WCS_GET_CAPABILITIES) {
310✔
3155
      /* In case of GetCapabilities, make  */
3156
      char version_string[OWS_VERSION_MAXLEN];
3157
      int version, supported_versions[] = {OWS_2_0_1, OWS_2_0_0, OWS_1_1_2,
73✔
3158
                                           OWS_1_1_1, OWS_1_1_0, OWS_1_0_0};
3159
      version = msOWSNegotiateVersion(requested_version, supported_versions,
73✔
3160
                                      sizeof(supported_versions) / sizeof(int));
3161
      msOWSGetVersionString(version, version_string);
73✔
3162
      msFree(ows_request->version);
73✔
3163
      ows_request->version = msStrdup(version_string);
73✔
3164
    }
3165
  }
3166

3167
  /* VERSION specific request handler */
3168
  if (strcmp(ows_request->version, "1.0.0") == 0 ||
312✔
3169
      strcmp(ows_request->version, "1.1.0") == 0 ||
268✔
3170
      strcmp(ows_request->version, "1.1.1") == 0 ||
249✔
3171
      strcmp(ows_request->version, "1.1.2") == 0) {
223✔
3172
    auto paramsTmp = msWCSCreateParams();
89✔
3173
    status = msWCSParseRequest(request, paramsTmp, map);
89✔
3174
    if (status == MS_FAILURE) {
89✔
3175
      msWCSFreeParams(paramsTmp);
×
3176
      free(paramsTmp);
×
3177
      return msWCSException(map, "InvalidParameterValue", "request", "2.0");
×
3178
    }
3179

3180
    retVal = MS_FAILURE;
3181
    if (operation == MS_WCS_GET_CAPABILITIES) {
89✔
3182
      retVal = msWCSGetCapabilities(map, paramsTmp, request, ows_request);
33✔
3183
    } else if (operation == MS_WCS_DESCRIBE_COVERAGE) {
56✔
3184
      retVal = msWCSDescribeCoverage(map, paramsTmp, ows_request, request);
8✔
3185
    } else if (operation == MS_WCS_GET_COVERAGE) {
3186
      retVal = msWCSGetCoverage(map, request, paramsTmp, ows_request);
48✔
3187
    }
3188
    msWCSFreeParams(paramsTmp);
89✔
3189
    free(paramsTmp);
89✔
3190
    return retVal;
89✔
3191
  } else if (strcmp(ows_request->version, "2.0.0") == 0 ||
223✔
3192
             strcmp(ows_request->version, "2.0.1") == 0) {
190✔
3193
#if defined(USE_LIBXML2)
3194
    int i;
3195

3196
    if (params20 == NULL) {
217✔
3197
      params20 = msWCSCreateParamsObj20();
215✔
3198
      status = msWCSParseRequest20(map, request, ows_request, params20);
215✔
3199
      if (status == MS_FAILURE) {
215✔
3200
        msWCSFreeParamsObj20(params20);
2✔
3201
        return msWCSException(map, "InvalidParameterValue", "request", "2.0.1");
2✔
3202
      } else if (status == MS_DONE) {
213✔
3203
        /* MS_DONE means, that the exception has already been written to the IO
3204
          buffer.
3205
        */
3206
        msWCSFreeParamsObj20(params20);
2✔
3207
        return MS_FAILURE;
2✔
3208
      }
3209
    }
3210

3211
    /* check if all layer names are valid NCNames */
3212
    for (i = 0; i < map->numlayers; ++i) {
514✔
3213
      if (!msWCSIsLayerSupported(map->layers[i]))
301✔
3214
        continue;
32✔
3215

3216
      /* Check if each layers name is a valid NCName. */
3217
      if (msEvalRegex("^[a-zA-z_][a-zA-Z0-9_.-]*$", map->layers[i]->name) ==
269✔
3218
          MS_FALSE) {
3219
        msSetError(MS_WCSERR, "Layer name '%s' is not a valid NCName.",
×
3220
                   "msWCSDispatch()", map->layers[i]->name);
×
3221
        msWCSFreeParamsObj20(params20);
×
3222
        return msWCSException(map, "mapserv", "Internal", "2.0.1");
×
3223
      }
3224
    }
3225

3226
    /* Call operation specific functions */
3227
    if (operation == MS_WCS_GET_CAPABILITIES) {
213✔
3228
      retVal = msWCSGetCapabilities20(map, request, params20, ows_request);
42✔
3229
    } else if (operation == MS_WCS_DESCRIBE_COVERAGE) {
171✔
3230
      retVal = msWCSDescribeCoverage20(map, params20, ows_request);
5✔
3231
    } else if (operation == MS_WCS_GET_COVERAGE) {
3232
      retVal = msWCSGetCoverage20(map, request, params20, ows_request);
166✔
3233
    } else {
3234
      msSetError(MS_WCSERR, "Invalid request '%s'.", "msWCSDispatch20()",
3235
                 ows_request->request);
3236
      retVal =
3237
          msWCSException20(map, "InvalidParameterValue", "request", "2.0.1");
3238
    }
3239
    /* clean up */
3240
    msWCSFreeParamsObj20(params20);
213✔
3241
    return retVal;
213✔
3242
#else      /* def USE_LIBXML2 */
3243
    msSetError(MS_WCSERR,
3244
               "WCS 2.0 needs mapserver to be compiled with libxml2.",
3245
               "msWCSDispatch()");
3246
    return msWCSException(map, "mapserv", "NoApplicableCode", "2.0.1");
3247
#endif     /* def USE_LIBXML2 */
3248
  } else { /* unsupported version */
3249
    msSetError(MS_WCSERR, "WCS Server does not support VERSION %s.",
6✔
3250
               "msWCSDispatch()", ows_request->version);
3251
    return msWCSException(map, "InvalidParameterValue", "version",
6✔
3252
                          ows_request->version);
6✔
3253
  }
3254

3255
#else
3256
  msSetError(MS_WCSERR, "WCS server support is not available.",
3257
             "msWCSDispatch()");
3258
  return MS_FAILURE;
3259
#endif
3260
}
3261

3262
/************************************************************************/
3263
/*                      msWCSGetCoverageMetadata()                      */
3264
/************************************************************************/
3265

3266
#ifdef USE_WCS_SVR
3267

3268
void msWCSFreeCoverageMetadata(coverageMetadataObj *cm) {
77✔
3269
  msFree(cm->srs_epsg);
77✔
3270
}
77✔
3271

3272
int msWCSGetCoverageMetadata(layerObj *layer, coverageMetadataObj *cm) {
77✔
3273
  char *srs_urn = NULL;
3274
  int i = 0;
3275
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
77✔
3276
    return MS_FAILURE;
3277

3278
  /* -------------------------------------------------------------------- */
3279
  /*      Get the SRS in WCS 1.0 format (eg. EPSG:n)                      */
3280
  /* -------------------------------------------------------------------- */
3281
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE,
77✔
3282
                   &(cm->srs_epsg));
3283
  if (!cm->srs_epsg) {
77✔
3284
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
×
3285
                     "CO", MS_TRUE, &(cm->srs_epsg));
3286
    if (!cm->srs_epsg) {
×
3287
      msSetError(MS_WCSERR,
×
3288
                 "Unable to determine the SRS for this layer, no projection "
3289
                 "defined and no metadata available.",
3290
                 "msWCSGetCoverageMetadata()");
3291
      return MS_FAILURE;
×
3292
    }
3293
  }
3294

3295
  /* -------------------------------------------------------------------- */
3296
  /*      Get the SRS in urn format.                                      */
3297
  /* -------------------------------------------------------------------- */
3298
  if ((srs_urn = msOWSGetProjURN(&(layer->projection), &(layer->metadata), "CO",
77✔
3299
                                 MS_TRUE)) == NULL) {
3300
    srs_urn = msOWSGetProjURN(&(layer->map->projection),
×
3301
                              &(layer->map->web.metadata), "CO", MS_TRUE);
×
3302
  }
3303

3304
  if (srs_urn != NULL) {
×
3305
    if (strlen(srs_urn) > sizeof(cm->srs_urn) - 1) {
77✔
3306
      msSetError(MS_WCSERR, "SRS URN too long!", "msWCSGetCoverageMetadata()");
×
3307
      return MS_FAILURE;
×
3308
    }
3309

3310
    strcpy(cm->srs_urn, srs_urn);
77✔
3311
    msFree(srs_urn);
77✔
3312
  } else
3313
    cm->srs_urn[0] = '\0';
×
3314

3315
  /* -------------------------------------------------------------------- */
3316
  /*      If we have "virtual dataset" metadata on the layer, then use    */
3317
  /*      that in preference to inspecting the file(s).                   */
3318
  /*      We require extent and either size or resolution.                */
3319
  /* -------------------------------------------------------------------- */
3320
  if (msOWSLookupMetadata(&(layer->metadata), "CO", "extent") != NULL &&
144✔
3321
      (msOWSLookupMetadata(&(layer->metadata), "CO", "resolution") != NULL ||
69✔
3322
       msOWSLookupMetadata(&(layer->metadata), "CO", "size") != NULL)) {
2✔
3323
    const char *value;
3324

3325
    /* get extent */
3326
    cm->extent.minx = 0.0;
66✔
3327
    cm->extent.maxx = 0.0;
66✔
3328
    cm->extent.miny = 0.0;
66✔
3329
    cm->extent.maxy = 0.0;
66✔
3330
    if (msOWSGetLayerExtent(layer->map, layer, "CO", &cm->extent) == MS_FAILURE)
66✔
3331
      return MS_FAILURE;
3332

3333
    /* get resolution */
3334
    cm->xresolution = 0.0;
66✔
3335
    cm->yresolution = 0.0;
66✔
3336
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "resolution")) !=
66✔
3337
        NULL) {
3338
      char **tokens;
3339
      int n;
3340

3341
      tokens = msStringSplit(value, ' ', &n);
65✔
3342
      if (tokens == NULL || n != 2) {
65✔
3343
        msSetError(MS_WCSERR,
×
3344
                   "Wrong number of arguments for wcs|ows_resolution metadata.",
3345
                   "msWCSGetCoverageMetadata()");
3346
        msFreeCharArray(tokens, n);
×
3347
        return MS_FAILURE;
×
3348
      }
3349
      cm->xresolution = atof(tokens[0]);
65✔
3350
      cm->yresolution = atof(tokens[1]);
65✔
3351
      msFreeCharArray(tokens, n);
65✔
3352
    }
3353

3354
    /* get Size (in pixels and lines) */
3355
    cm->xsize = 0;
66✔
3356
    cm->ysize = 0;
66✔
3357
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "size")) !=
66✔
3358
        NULL) {
3359
      char **tokens;
3360
      int n;
3361

3362
      tokens = msStringSplit(value, ' ', &n);
9✔
3363
      if (tokens == NULL || n != 2) {
9✔
3364
        msSetError(MS_WCSERR,
×
3365
                   "Wrong number of arguments for wcs|ows_size metadata.",
3366
                   "msWCSGetCoverageDomain()");
3367
        msFreeCharArray(tokens, n);
×
3368
        return MS_FAILURE;
×
3369
      }
3370
      cm->xsize = atoi(tokens[0]);
9✔
3371
      cm->ysize = atoi(tokens[1]);
9✔
3372
      msFreeCharArray(tokens, n);
9✔
3373
    }
3374

3375
    /* try to compute raster size */
3376
    if (cm->xsize == 0 && cm->ysize == 0 && cm->xresolution != 0.0 &&
66✔
3377
        cm->yresolution != 0.0 && cm->extent.minx != cm->extent.maxx &&
57✔
3378
        cm->extent.miny != cm->extent.maxy) {
57✔
3379
      cm->xsize =
57✔
3380
          (int)((cm->extent.maxx - cm->extent.minx) / cm->xresolution + 0.5);
57✔
3381
      cm->ysize = (int)fabs(
57✔
3382
          (cm->extent.maxy - cm->extent.miny) / cm->yresolution + 0.5);
57✔
3383
    }
3384

3385
    /* try to compute raster resolution */
3386
    if ((cm->xresolution == 0.0 || cm->yresolution == 0.0) && cm->xsize != 0 &&
66✔
3387
        cm->ysize != 0) {
1✔
3388
      cm->xresolution = (cm->extent.maxx - cm->extent.minx) / cm->xsize;
1✔
3389
      cm->yresolution = (cm->extent.maxy - cm->extent.miny) / cm->ysize;
1✔
3390
    }
3391

3392
    /* do we have information to do anything */
3393
    if (cm->xresolution == 0.0 || cm->yresolution == 0.0 || cm->xsize == 0 ||
66✔
3394
        cm->ysize == 0) {
66✔
3395
      msSetError(MS_WCSERR,
×
3396
                 "Failed to collect extent and resolution for WCS coverage "
3397
                 "from metadata for layer '%s'.  Need value wcs|ows_resolution "
3398
                 "or wcs|ows_size values.",
3399
                 "msWCSGetCoverageMetadata()", layer->name);
3400
      return MS_FAILURE;
×
3401
    }
3402

3403
    /* compute geotransform */
3404
    cm->geotransform[0] = cm->extent.minx;
66✔
3405
    cm->geotransform[1] = cm->xresolution;
66✔
3406
    cm->geotransform[2] = 0.0;
66✔
3407
    cm->geotransform[3] = cm->extent.maxy;
66✔
3408
    cm->geotransform[4] = 0.0;
66✔
3409
    cm->geotransform[5] = -fabs(cm->yresolution);
66✔
3410

3411
    /* get bands count, or assume 1 if not found */
3412
    cm->bandcount = 1;
66✔
3413
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "bandcount")) !=
66✔
3414
        NULL) {
3415
      cm->bandcount = atoi(value);
58✔
3416
    }
3417

3418
    /* get bands type, or assume float if not found */
3419
    cm->imagemode = MS_IMAGEMODE_FLOAT32;
66✔
3420
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "imagemode")) !=
66✔
3421
        NULL) {
3422
      if (EQUAL(value, "INT16"))
57✔
3423
        cm->imagemode = MS_IMAGEMODE_INT16;
×
3424
      else if (EQUAL(value, "FLOAT32"))
57✔
3425
        cm->imagemode = MS_IMAGEMODE_FLOAT32;
5✔
3426
      else if (EQUAL(value, "BYTE"))
52✔
3427
        cm->imagemode = MS_IMAGEMODE_BYTE;
52✔
3428
      else {
3429
        msSetError(MS_WCSERR,
×
3430
                   "Content of wcs|ows_imagemode (%s) not recognised.  Should "
3431
                   "be one of BYTE, INT16 or FLOAT32.",
3432
                   "msWCSGetCoverageMetadata()", value);
3433
        return MS_FAILURE;
×
3434
      }
3435
    }
3436
    /* set color interpretation to undefined */
3437
    /* TODO: find better solution */
3438
    for (i = 0; i < 10; ++i) {
726✔
3439
      cm->bandinterpretation[i] = GDALGetColorInterpretationName(GCI_Undefined);
660✔
3440
    }
3441
  } else if (layer->data ==
11✔
3442
             NULL) { /* no virtual metadata, not ok unless we're talking 1
3443
                        image, hopefully we can fix that */
3444
    msSetError(
×
3445
        MS_WCSERR,
3446
        "RASTER Layer with no DATA statement and no WCS virtual dataset "
3447
        "metadata.  Tileindexed raster layers not supported for WCS without "
3448
        "virtual dataset metadata (cm->extent, wcs_res, wcs_size).",
3449
        "msWCSGetCoverageDomain()");
3450
    return MS_FAILURE;
×
3451
  } else { /* work from the file (e.g. DATA) */
3452
    GDALDatasetH hDS;
3453
    GDALRasterBandH hBand;
3454
    char szPath[MS_MAXPATHLEN];
3455
    char *decrypted_path;
3456

3457
    msGDALInitialize();
11✔
3458

3459
    msTryBuildPath3(szPath, layer->map->mappath, layer->map->shapepath,
11✔
3460
                    layer->data);
11✔
3461
    decrypted_path = msDecryptStringTokens(layer->map, szPath);
11✔
3462
    if (!decrypted_path)
11✔
3463
      return MS_FAILURE;
×
3464

3465
    msAcquireLock(TLOCK_GDAL);
11✔
3466
    {
3467
      char **connectionoptions =
3468
          msGetStringListFromHashTable(&(layer->connectionoptions));
11✔
3469
      hDS = GDALOpenEx(decrypted_path, GDAL_OF_RASTER, NULL,
11✔
3470
                       (const char *const *)connectionoptions, NULL);
3471
      CSLDestroy(connectionoptions);
11✔
3472
    }
3473
    if (hDS == NULL) {
11✔
3474
      const char *cpl_error_msg = CPLGetLastErrorMsg();
×
3475

3476
      /* we wish to avoid reporting decrypted paths */
3477
      if (cpl_error_msg != NULL &&
×
3478
          strstr(cpl_error_msg, decrypted_path) != NULL &&
×
3479
          strcmp(decrypted_path, szPath) != 0)
×
3480
        cpl_error_msg = NULL;
3481

3482
      if (cpl_error_msg == NULL)
3483
        cpl_error_msg = "";
3484

3485
      msReleaseLock(TLOCK_GDAL);
×
3486

3487
      msSetError(MS_IOERR, "%s", "msWCSGetCoverageMetadata()", cpl_error_msg);
×
3488

3489
      msFree(decrypted_path);
×
3490
      return MS_FAILURE;
×
3491
    }
3492
    msFree(decrypted_path);
11✔
3493

3494
    msGetGDALGeoTransform(hDS, layer->map, layer, cm->geotransform);
11✔
3495

3496
    cm->xsize = GDALGetRasterXSize(hDS);
11✔
3497
    cm->ysize = GDALGetRasterYSize(hDS);
11✔
3498

3499
    cm->extent.minx = cm->geotransform[0];
11✔
3500
    cm->extent.maxx = cm->geotransform[0] + cm->geotransform[1] * cm->xsize +
11✔
3501
                      cm->geotransform[2] * cm->ysize;
11✔
3502
    cm->extent.miny = cm->geotransform[3] + cm->geotransform[4] * cm->xsize +
11✔
3503
                      cm->geotransform[5] * cm->ysize;
11✔
3504
    cm->extent.maxy = cm->geotransform[3];
11✔
3505

3506
    cm->xresolution = cm->geotransform[1];
11✔
3507
    cm->yresolution = cm->geotransform[5];
11✔
3508

3509
    /* TODO: need to set resolution */
3510

3511
    cm->bandcount = GDALGetRasterCount(hDS);
11✔
3512

3513
    if (cm->bandcount == 0) {
11✔
3514
      msReleaseLock(TLOCK_GDAL);
×
3515
      msSetError(MS_WCSERR,
×
3516
                 "Raster file %s has no raster bands.  This cannot be used in "
3517
                 "a layer.",
3518
                 "msWCSGetCoverageMetadata()", layer->data);
3519
      return MS_FAILURE;
×
3520
    }
3521

3522
    hBand = GDALGetRasterBand(hDS, 1);
11✔
3523
    switch (GDALGetRasterDataType(hBand)) {
11✔
3524
    case GDT_Byte:
3✔
3525
      cm->imagemode = MS_IMAGEMODE_BYTE;
3✔
3526
      break;
3✔
3527
    case GDT_Int16:
8✔
3528
      cm->imagemode = MS_IMAGEMODE_INT16;
8✔
3529
      break;
8✔
3530
    default:
×
3531
      cm->imagemode = MS_IMAGEMODE_FLOAT32;
×
3532
      break;
×
3533
    }
3534

3535
    /* color interpretation */
3536
    for (i = 1; i <= 10 && i <= cm->bandcount; ++i) {
28✔
3537
      GDALColorInterp colorInterp;
3538
      hBand = GDALGetRasterBand(hDS, i);
17✔
3539
      colorInterp = GDALGetRasterColorInterpretation(hBand);
17✔
3540
      cm->bandinterpretation[i - 1] =
17✔
3541
          GDALGetColorInterpretationName(colorInterp);
17✔
3542
    }
3543

3544
    GDALClose(hDS);
11✔
3545
    msReleaseLock(TLOCK_GDAL);
11✔
3546
  }
3547

3548
  /* we must have the bounding box in lat/lon [WGS84(DD)/EPSG:4326] */
3549
  cm->llextent = cm->extent;
77✔
3550

3551
  /* Already in latlong .. use directly. */
3552
  if (layer->projection.proj != NULL &&
154✔
3553
      msProjIsGeographicCRS(&(layer->projection))) {
77✔
3554
    /* no change */
3555
  }
3556

3557
  else if (layer->projection.numargs > 0 &&
130✔
3558
           !msProjIsGeographicCRS(
65✔
3559
               &(layer->projection))) /* check the layer projection */
3560
    msProjectRect(&(layer->projection), NULL, &(cm->llextent));
65✔
3561

3562
  else if (layer->map->projection.numargs > 0 &&
×
3563
           !msProjIsGeographicCRS(
×
3564
               &(layer->map->projection))) /* check the map projection */
×
3565
    msProjectRect(&(layer->map->projection), NULL, &(cm->llextent));
×
3566

3567
  else { /* projection was specified in the metadata only (EPSG:... only at the
3568
            moment)  */
3569
    projectionObj proj;
3570
    char projstring[32];
3571

3572
    msInitProjection(&proj); /* or bad things happen */
×
3573
    msProjectionInheritContextFrom(&proj, &(layer->map->projection));
×
3574

3575
    snprintf(projstring, sizeof(projstring), "init=epsg:%.20s",
×
3576
             cm->srs_epsg + 5);
×
3577
    if (msLoadProjectionString(&proj, projstring) != 0)
×
3578
      return MS_FAILURE;
×
3579
    msProjectRect(&proj, NULL, &(cm->llextent));
×
3580
  }
3581

3582
  return MS_SUCCESS;
3583
}
3584
#endif /* def USE_WCS_SVR */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc