Coverage Report

Created: 2026-04-01 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/mapwcs20.cpp
Line
Count
Source
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OpenGIS Web Coverage Server (WCS) 2.0 implementation.
6
 * Author:   Stephan Meissl <stephan.meissl@eox.at>
7
 *           Fabian Schindler <fabian.schindler@eox.at>
8
 *           and the MapServer team.
9
 *
10
 ******************************************************************************
11
 * Copyright (c) 2010, 2011 EOX IT Services GmbH, Austria
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a
14
 * copy of this software and associated documentation files (the "Software"),
15
 * to deal in the Software without restriction, including without limitation
16
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17
 * and/or sell copies of the Software, and to permit persons to whom the
18
 * Software is furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in
21
 * all copies of this Software or works derived from this Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29
 * DEALINGS IN THE SOFTWARE.
30
 ****************************************************************************/
31
32
#include "mapserver-config.h"
33
#if defined(USE_WCS_SVR)
34
35
#include <assert.h>
36
#include "mapserver.h"
37
#include "maperror.h"
38
#include "mapthread.h"
39
#include "mapows.h"
40
#include "mapwcs.h"
41
#include "mapgdal.h"
42
#include <cmath>
43
#include <float.h>
44
#include "gdal.h"
45
#include "cpl_port.h"
46
#include "maptime.h"
47
#include "mapprimitive.h"
48
#include "cpl_string.h"
49
#include <string.h>
50
51
#if defined(USE_LIBXML2)
52
53
#include <libxml/tree.h>
54
#include "maplibxml2.h"
55
#include <libxml/parser.h>
56
57
#endif /* defined(USE_LIBXML2) */
58
59
#include <string>
60
61
/************************************************************************/
62
/*                          msXMLStripIndentation                       */
63
/************************************************************************/
64
65
0
static void msXMLStripIndentation(char *ptr) {
66
  /* Remove spaces between > and < to get properly indented result */
67
0
  char *afterLastClosingBracket = NULL;
68
0
  if (*ptr == ' ')
69
0
    afterLastClosingBracket = ptr;
70
0
  while (*ptr != '\0') {
71
0
    if (*ptr == '<' && afterLastClosingBracket != NULL) {
72
0
      memmove(afterLastClosingBracket, ptr, strlen(ptr) + 1);
73
0
      ptr = afterLastClosingBracket;
74
0
    } else if (*ptr == '>') {
75
0
      afterLastClosingBracket = ptr + 1;
76
0
    } else if (*ptr != ' ' && *ptr != '\n')
77
0
      afterLastClosingBracket = NULL;
78
0
    ptr++;
79
0
  }
80
0
}
81
82
/************************************************************************/
83
/*                   msStringParseInteger()                             */
84
/*                                                                      */
85
/*      Tries to parse a string as a integer value. If not possible     */
86
/*      the value MS_WCS20_UNBOUNDED is stored as value.                */
87
/*      If no characters could be parsed, MS_FAILURE is returned. If at */
88
/*      least some characters could be parsed, MS_DONE is returned and  */
89
/*      only if all characters could be parsed, MS_SUCCESS is returned. */
90
/************************************************************************/
91
92
0
static int msStringParseInteger(const char *string, int *dest) {
93
0
  char *parse_check;
94
0
  *dest = (int)strtol(string, &parse_check, 0);
95
0
  if (parse_check == string) {
96
0
    return MS_FAILURE;
97
0
  } else if (parse_check - strlen(string) != string) {
98
0
    return MS_DONE;
99
0
  }
100
0
  return MS_SUCCESS;
101
0
}
102
103
/************************************************************************/
104
/*                   msStringParseDouble()                              */
105
/*                                                                      */
106
/*      Tries to parse a string as a double value. If not possible      */
107
/*      the value 0 is stored as value.                                 */
108
/*      If no characters could be parsed, MS_FAILURE is returned. If at */
109
/*      least some characters could be parsed, MS_DONE is returned and  */
110
/*      only if all characters could be parsed, MS_SUCCESS is returned. */
111
/************************************************************************/
112
113
0
static int msStringParseDouble(const char *string, double *dest) {
114
0
  char *parse_check = NULL;
115
0
  *dest = strtod(string, &parse_check);
116
0
  if (parse_check == string) {
117
0
    return MS_FAILURE;
118
0
  } else if (parse_check - strlen(string) != string) {
119
0
    return MS_DONE;
120
0
  }
121
0
  return MS_SUCCESS;
122
0
}
123
124
/************************************************************************/
125
/*                   msWCSParseTimeOrScalar20()                         */
126
/*                                                                      */
127
/*      Parses a string, either as a time or a scalar value and         */
128
/*      writes the output into the timeScalarUnion.                     */
129
/************************************************************************/
130
131
0
static int msWCSParseTimeOrScalar20(timeScalarUnion *u, const char *string) {
132
0
  struct tm time;
133
0
  if (string) {
134
0
    while (*string == ' ')
135
0
      string++;
136
0
  }
137
138
0
  if (!string || strlen(string) == 0 || !u) {
139
0
    msSetError(MS_WCSERR, "Invalid string", "msWCSParseTimeOrScalar20()");
140
0
    return MS_WCS20_ERROR_VALUE;
141
0
  }
142
  /* if the string is equal to "*" it means the value
143
   *  of the interval is unbounded                    */
144
0
  if (EQUAL(string, "*")) {
145
0
    u->scalar = MS_WCS20_UNBOUNDED;
146
0
    u->unbounded = 1;
147
0
    return MS_WCS20_UNDEFINED_VALUE;
148
0
  }
149
150
  /* if returned a valid value, use it */
151
0
  if (msStringParseDouble(string, &(u->scalar)) == MS_SUCCESS) {
152
0
    return MS_WCS20_SCALAR_VALUE;
153
0
  }
154
  /* otherwise it might be a time value */
155
0
  msTimeInit(&time);
156
0
  if (msParseTime(string, &time) == MS_TRUE) {
157
0
    u->time = mktime(&time);
158
0
    return MS_WCS20_TIME_VALUE;
159
0
  }
160
  /* the value could neither be parsed as a double nor as a time value */
161
0
  else {
162
0
    msSetError(MS_WCSERR,
163
0
               "String %s could not be parsed to a time or scalar value",
164
0
               "msWCSParseTimeOrScalar20()", string);
165
0
    return MS_WCS20_ERROR_VALUE;
166
0
  }
167
0
}
168
169
/************************************************************************/
170
/*                   msWCSCreateSubsetObj20()                           */
171
/*                                                                      */
172
/*      Creates a new wcs20SubsetObj and initializes it to standard     */
173
/*      values.                                                         */
174
/************************************************************************/
175
176
0
static wcs20SubsetObjPtr msWCSCreateSubsetObj20() {
177
0
  wcs20SubsetObjPtr subset = (wcs20SubsetObjPtr)malloc(sizeof(wcs20SubsetObj));
178
0
  MS_CHECK_ALLOC(subset, sizeof(wcs20SubsetObj), NULL);
179
180
0
  subset->axis = NULL;
181
0
  subset->crs = NULL;
182
0
  subset->min.scalar = subset->max.scalar = MS_WCS20_UNBOUNDED;
183
0
  subset->min.unbounded = subset->max.unbounded = 0;
184
0
  subset->operation = MS_WCS20_SLICE;
185
186
0
  return subset;
187
0
}
188
189
/************************************************************************/
190
/*                   msWCSFreeSubsetObj20()                             */
191
/*                                                                      */
192
/*      Frees a wcs20SubsetObj and releases all linked resources.       */
193
/************************************************************************/
194
195
0
static void msWCSFreeSubsetObj20(wcs20SubsetObjPtr subset) {
196
0
  if (NULL == subset) {
197
0
    return;
198
0
  }
199
0
  msFree(subset->axis);
200
0
  msFree(subset->crs);
201
0
  msFree(subset);
202
0
}
203
204
/************************************************************************/
205
/*                   msWCSCreateAxisObj20()                             */
206
/*                                                                      */
207
/*      Creates a new wcs20AxisObj and initializes it to standard       */
208
/*      values.                                                         */
209
/************************************************************************/
210
211
0
static wcs20AxisObjPtr msWCSCreateAxisObj20() {
212
0
  wcs20AxisObj *axis = (wcs20AxisObjPtr)malloc(sizeof(wcs20AxisObj));
213
0
  MS_CHECK_ALLOC(axis, sizeof(wcs20AxisObj), NULL);
214
215
0
  axis->name = NULL;
216
0
  axis->size = 0;
217
0
  axis->resolution = MS_WCS20_UNBOUNDED;
218
0
  axis->scale = MS_WCS20_UNBOUNDED;
219
0
  axis->resolutionUOM = NULL;
220
0
  axis->subset = NULL;
221
222
0
  return axis;
223
0
}
224
225
/************************************************************************/
226
/*                   msWCSFreeAxisObj20()                               */
227
/*                                                                      */
228
/*      Frees a wcs20AxisObj and releases all linked resources.         */
229
/************************************************************************/
230
231
0
static void msWCSFreeAxisObj20(wcs20AxisObjPtr axis) {
232
0
  if (NULL == axis) {
233
0
    return;
234
0
  }
235
236
0
  msFree(axis->name);
237
0
  msFree(axis->resolutionUOM);
238
0
  msWCSFreeSubsetObj20(axis->subset);
239
0
  msFree(axis);
240
0
}
241
242
/************************************************************************/
243
/*                   msWCSFindAxis20()                                  */
244
/*                                                                      */
245
/*      Helper function to retrieve an axis by the name from a params   */
246
/*      object.                                                         */
247
/************************************************************************/
248
249
static wcs20AxisObjPtr msWCSFindAxis20(wcs20ParamsObjPtr params,
250
0
                                       const char *name) {
251
0
  int i = 0;
252
0
  for (i = 0; i < params->numaxes; ++i) {
253
0
    if (EQUAL(params->axes[i]->name, name)) {
254
0
      return params->axes[i];
255
0
    }
256
0
  }
257
0
  return NULL;
258
0
}
259
260
/************************************************************************/
261
/*                   msWCSInsertAxisObj20()                             */
262
/*                                                                      */
263
/*      Helper function to insert an axis object into the axes list of  */
264
/*      a params object.                                                */
265
/************************************************************************/
266
267
static void msWCSInsertAxisObj20(wcs20ParamsObjPtr params,
268
0
                                 wcs20AxisObjPtr axis) {
269
0
  params->numaxes++;
270
0
  params->axes = (wcs20AxisObjPtr *)msSmallRealloc(
271
0
      params->axes, sizeof(wcs20AxisObjPtr) * (params->numaxes));
272
0
  params->axes[params->numaxes - 1] = axis;
273
0
}
274
275
/************************************************************************/
276
/*                   msWCSCreateParamsObj20()                           */
277
/*                                                                      */
278
/*      Creates a new wcs20ParamsObj and initializes it to standard     */
279
/*      values.                                                         */
280
/************************************************************************/
281
282
0
wcs20ParamsObjPtr msWCSCreateParamsObj20() {
283
0
  wcs20ParamsObjPtr params = (wcs20ParamsObjPtr)malloc(sizeof(wcs20ParamsObj));
284
0
  MS_CHECK_ALLOC(params, sizeof(wcs20ParamsObj), NULL);
285
286
0
  params->version = NULL;
287
0
  params->request = NULL;
288
0
  params->service = NULL;
289
0
  params->accept_versions = NULL;
290
0
  params->accept_languages = NULL;
291
0
  params->sections = NULL;
292
0
  params->updatesequence = NULL;
293
0
  params->ids = NULL;
294
0
  params->width = 0;
295
0
  params->height = 0;
296
0
  params->resolutionX = MS_WCS20_UNBOUNDED;
297
0
  params->resolutionY = MS_WCS20_UNBOUNDED;
298
0
  params->scale = MS_WCS20_UNBOUNDED;
299
0
  params->scaleX = MS_WCS20_UNBOUNDED;
300
0
  params->scaleY = MS_WCS20_UNBOUNDED;
301
0
  params->resolutionUnits = NULL;
302
0
  params->numaxes = 0;
303
0
  params->axes = NULL;
304
0
  params->format = NULL;
305
0
  params->multipart = 0;
306
0
  params->interpolation = NULL;
307
0
  params->outputcrs = NULL;
308
0
  params->subsetcrs = NULL;
309
0
  params->bbox.minx = params->bbox.miny = -DBL_MAX;
310
0
  params->bbox.maxx = params->bbox.maxy = DBL_MAX;
311
0
  params->range_subset = NULL;
312
0
  params->format_options = NULL;
313
314
0
  return params;
315
0
}
316
317
/************************************************************************/
318
/*                   msWCSFreeParamsObj20()                             */
319
/*                                                                      */
320
/*      Frees a wcs20ParamsObj and releases all linked resources.       */
321
/************************************************************************/
322
323
0
void msWCSFreeParamsObj20(wcs20ParamsObjPtr params) {
324
0
  if (NULL == params) {
325
0
    return;
326
0
  }
327
328
0
  msFree(params->version);
329
0
  msFree(params->request);
330
0
  msFree(params->service);
331
0
  CSLDestroy(params->accept_versions);
332
0
  CSLDestroy(params->accept_languages);
333
0
  CSLDestroy(params->sections);
334
0
  msFree(params->updatesequence);
335
0
  CSLDestroy(params->ids);
336
0
  msFree(params->resolutionUnits);
337
0
  msFree(params->format);
338
0
  msFree(params->interpolation);
339
0
  msFree(params->outputcrs);
340
0
  msFree(params->subsetcrs);
341
0
  while (params->numaxes > 0) {
342
0
    params->numaxes -= 1;
343
0
    msWCSFreeAxisObj20(params->axes[params->numaxes]);
344
0
  }
345
0
  msFree(params->axes);
346
0
  CSLDestroy(params->range_subset);
347
0
  CSLDestroy(params->format_options);
348
0
  msFree(params);
349
0
}
350
351
/************************************************************************/
352
/*                   msWCSParseSubset20()                               */
353
/*                                                                      */
354
/*      Parses several string parameters and fills them into the        */
355
/*      subset object.                                                  */
356
/************************************************************************/
357
358
static int msWCSParseSubset20(wcs20SubsetObjPtr subset, const char *axis,
359
                              const char *crs, const char *min,
360
0
                              const char *max) {
361
0
  int ts1, ts2;
362
0
  ts1 = ts2 = MS_WCS20_UNDEFINED_VALUE;
363
364
0
  if (subset == NULL) {
365
0
    return MS_FAILURE;
366
0
  }
367
368
0
  if (axis == NULL || strlen(axis) == 0) {
369
0
    msSetError(MS_WCSERR, "Subset axis is not given.", "msWCSParseSubset20()");
370
0
    return MS_FAILURE;
371
0
  }
372
373
0
  subset->axis = msStrdup(axis);
374
0
  if (crs != NULL) {
375
0
    subset->crs = msStrdup(crs);
376
0
  }
377
378
  /* Parse first (probably only) part of interval/point;
379
   * check whether its a time value or a scalar value     */
380
0
  ts1 = msWCSParseTimeOrScalar20(&(subset->min), min);
381
0
  if (ts1 == MS_WCS20_ERROR_VALUE) {
382
0
    return MS_FAILURE;
383
0
  }
384
385
  /* check if its an interval */
386
  /* if there is a comma, then it is */
387
0
  if (max != NULL && strlen(max) > 0) {
388
0
    subset->operation = MS_WCS20_TRIM;
389
390
    /* Parse the second value of the interval */
391
0
    ts2 = msWCSParseTimeOrScalar20(&(subset->max), max);
392
0
    if (ts2 == MS_WCS20_ERROR_VALUE) {
393
0
      return MS_FAILURE;
394
0
    }
395
396
    /* if at least one boundary is defined, use that value */
397
0
    if ((ts1 == MS_WCS20_UNDEFINED_VALUE) ^ (ts2 == MS_WCS20_UNDEFINED_VALUE)) {
398
0
      if (ts1 == MS_WCS20_UNDEFINED_VALUE) {
399
0
        ts1 = ts2;
400
0
      }
401
0
    }
402
    /* if time and scalar values do not fit, throw an error */
403
0
    else if (ts1 != MS_WCS20_UNDEFINED_VALUE &&
404
0
             ts2 != MS_WCS20_UNDEFINED_VALUE && ts1 != ts2) {
405
0
      msSetError(MS_WCSERR,
406
0
                 "Interval error: minimum is a %s value, maximum is a %s value",
407
0
                 "msWCSParseSubset20()", ts1 ? "time" : "scalar",
408
0
                 ts2 ? "time" : "scalar");
409
0
      return MS_FAILURE;
410
0
    }
411
    /* if both min and max are unbounded -> throw an error */
412
0
    if (subset->min.unbounded && subset->max.unbounded) {
413
0
      msSetError(MS_WCSERR, "Invalid values: no bounds could be parsed",
414
0
                 "msWCSParseSubset20()");
415
0
      return MS_FAILURE;
416
0
    }
417
0
  }
418
  /* there is no second value, therefore it is a point.
419
   * consequently set the operation to slice */
420
0
  else {
421
0
    subset->operation = MS_WCS20_SLICE;
422
0
    if (ts1 == MS_WCS20_UNDEFINED_VALUE) {
423
0
      msSetError(MS_WCSERR, "Invalid point value given",
424
0
                 "msWCSParseSubset20()");
425
0
      return MS_FAILURE;
426
0
    }
427
0
  }
428
429
0
  subset->timeOrScalar = ts1;
430
431
  /* check whether the min is smaller than the max */
432
0
  if (subset->operation == MS_WCS20_TRIM) {
433
0
    if (subset->timeOrScalar == MS_WCS20_SCALAR_VALUE &&
434
0
        subset->min.scalar == MS_WCS20_UNBOUNDED) {
435
0
      subset->min.scalar = -MS_WCS20_UNBOUNDED;
436
0
    }
437
438
0
    if (subset->timeOrScalar == MS_WCS20_TIME_VALUE &&
439
0
        subset->min.time > subset->max.time) {
440
0
      msSetError(MS_WCSERR,
441
0
                 "Minimum value of subset axis %s is larger than maximum value",
442
0
                 "msWCSParseSubset20()", subset->axis);
443
0
      return MS_FAILURE;
444
0
    }
445
0
    if (subset->timeOrScalar == MS_WCS20_SCALAR_VALUE &&
446
0
        subset->min.scalar > subset->max.scalar) {
447
0
      msSetError(MS_WCSERR,
448
0
                 "Minimum value (%f) of subset axis '%s' is larger than "
449
0
                 "maximum value (%f).",
450
0
                 "msWCSParseSubset20()", subset->min.scalar, subset->axis,
451
0
                 subset->max.scalar);
452
0
      return MS_FAILURE;
453
0
    }
454
0
  }
455
0
  return MS_SUCCESS;
456
0
}
457
458
/************************************************************************/
459
/*                   msWCSParseSubsetKVPString20()                      */
460
/*                                                                      */
461
/*      Creates a new wcs20SubsetObj, parses a string and fills         */
462
/*      the parsed values into the object. Returns NULL on failure.     */
463
/*      Subset string: axis [ , crs ] ( intervalOrPoint )               */
464
/************************************************************************/
465
466
0
static int msWCSParseSubsetKVPString20(wcs20SubsetObjPtr subset, char *string) {
467
0
  char *axis, *crs, *min, *max;
468
469
0
  axis = string;
470
0
  crs = NULL;
471
0
  min = NULL;
472
0
  max = NULL;
473
474
  /* find first '(' */
475
0
  min = strchr(string, '(');
476
477
  /* if min could not be found, the string is invalid */
478
0
  if (min == NULL) {
479
0
    msSetError(MS_WCSERR, "Invalid axis subset string: '%s'",
480
0
               "msWCSParseSubsetKVPString20()", string);
481
0
    return MS_FAILURE;
482
0
  }
483
  /* set min to first letter */
484
0
  *min = '\0';
485
0
  ++min;
486
487
  /* cut the trailing ')' */
488
0
  if (min[strlen(min) - 1] == ')') {
489
0
    min[strlen(min) - 1] = '\0';
490
0
  }
491
  /* look if also a max is defined */
492
0
  max = strchr(min, ',');
493
0
  if (max != NULL) {
494
0
    *max = '\0';
495
0
    ++max;
496
0
  }
497
498
  /* look if also a crs is defined */
499
0
  crs = strchr(axis, ',');
500
0
  if (crs != NULL) {
501
0
    *crs = '\0';
502
0
    ++crs;
503
0
  }
504
505
0
  return msWCSParseSubset20(subset, axis, crs, min, max);
506
0
}
507
508
/************************************************************************/
509
/*                   msWCSParseSizeString20()                           */
510
/*                                                                      */
511
/*      Parses a string containing the axis and the size as an integer. */
512
/*      Size string: axis ( size )                                      */
513
/************************************************************************/
514
515
static int msWCSParseSizeString20(char *string, char *outAxis,
516
0
                                  size_t axisStringLen, int *outSize) {
517
0
  char *number = NULL;
518
0
  char *check = NULL;
519
520
  /* find first '(', the character before the number */
521
0
  number = strchr(string, '(');
522
523
0
  if (NULL == number) {
524
0
    msSetError(MS_WCSERR, "Invalid size parameter value.",
525
0
               "msWCSParseSize20()");
526
0
    return MS_FAILURE;
527
0
  }
528
529
  /* cut trailing ')' */
530
0
  check = strchr(string, ')');
531
0
  if (NULL == check) {
532
0
    msSetError(MS_WCSERR, "Invalid size parameter value.",
533
0
               "msWCSParseSize20()");
534
0
    return MS_FAILURE;
535
0
  }
536
0
  *number = '\0';
537
0
  ++number;
538
0
  *check = '\0';
539
540
0
  strlcpy(outAxis, string, axisStringLen);
541
542
  /* parse size value */
543
0
  if (msStringParseInteger(number, outSize) != MS_SUCCESS) {
544
0
    msSetError(MS_WCSERR, "Parameter value '%s' is not a valid integer.",
545
0
               "msWCSParseSize20()", number);
546
0
    return MS_FAILURE;
547
0
  }
548
549
0
  return MS_SUCCESS;
550
0
}
551
552
/************************************************************************/
553
/*                   msWCSParseResolutionString20()                     */
554
/*                                                                      */
555
/*      Parses a resolution string and returns the axis, the units of   */
556
/*      measure and the resolution value.                               */
557
/*      Subset string: axis ( value )                                   */
558
/************************************************************************/
559
560
static int msWCSParseResolutionString20(char *string, char *outAxis,
561
                                        size_t axisStringLen,
562
0
                                        double *outResolution) {
563
0
  char *number = NULL;
564
0
  char *check = NULL;
565
566
  /* find brackets */
567
0
  number = strchr(string, '(');
568
569
0
  if (NULL == number) {
570
0
    msSetError(MS_WCSERR, "Invalid resolution parameter value : %s.",
571
0
               "msWCSParseSize20()", string);
572
0
    return MS_FAILURE;
573
0
  }
574
575
  /* cut trailing ')' */
576
0
  check = strchr(string, ')');
577
0
  if (NULL == check) {
578
0
    msSetError(MS_WCSERR, "Invalid size parameter value.",
579
0
               "msWCSParseSize20()");
580
0
    return MS_FAILURE;
581
0
  }
582
583
0
  *number = '\0';
584
0
  ++number;
585
0
  *check = '\0';
586
587
0
  strlcpy(outAxis, string, axisStringLen);
588
589
0
  if (msStringParseDouble(number, outResolution) != MS_SUCCESS) {
590
0
    *outResolution = MS_WCS20_UNBOUNDED;
591
0
    msSetError(MS_WCSERR, "Invalid resolution parameter value : %s.",
592
0
               "msWCSParseSize20()", number);
593
0
    return MS_FAILURE;
594
0
  }
595
596
0
  return MS_SUCCESS;
597
0
}
598
599
/************************************************************************/
600
/*                   msWCSParseScaleString20()                          */
601
/*                                                                      */
602
/*      Parses a scale string and returns the axis, the and the value.  */
603
/*      Subset string: axis ( value )                                   */
604
/************************************************************************/
605
606
static int msWCSParseScaleString20(char *string, char *outAxis,
607
0
                                   size_t axisStringLen, double *outScale) {
608
0
  char *number = NULL;
609
0
  char *check = NULL;
610
611
  /* find brackets */
612
0
  number = strchr(string, '(');
613
614
0
  if (NULL == number) {
615
0
    msSetError(MS_WCSERR, "Invalid resolution parameter value : %s.",
616
0
               "msWCSParseScaleString20()", string);
617
0
    return MS_FAILURE;
618
0
  }
619
620
  /* cut trailing ')' */
621
0
  check = strchr(string, ')');
622
0
  if (NULL == check || check < number) {
623
0
    msSetError(MS_WCSERR, "Invalid scale parameter value.",
624
0
               "msWCSParseScaleString20()");
625
0
    return MS_FAILURE;
626
0
  }
627
628
0
  *number = '\0';
629
0
  ++number;
630
0
  *check = '\0';
631
632
0
  strlcpy(outAxis, string, axisStringLen);
633
634
0
  if (msStringParseDouble(number, outScale) != MS_SUCCESS || *outScale <= 0.0) {
635
0
    *outScale = MS_WCS20_UNBOUNDED;
636
0
    msSetError(MS_WCSERR, "Invalid scale parameter value : %s.",
637
0
               "msWCSParseScaleString20()", number);
638
0
    return MS_FAILURE;
639
0
  }
640
641
0
  return MS_SUCCESS;
642
0
}
643
644
/************************************************************************/
645
/*                   msWCSParseResolutionString20()                     */
646
/*                                                                      */
647
/*      Parses a resolution string and returns the axis, the units of   */
648
/*      measure and the resolution value.                               */
649
/*      Subset string: axis ( value )                                   */
650
/************************************************************************/
651
652
static int msWCSParseScaleExtentString20(char *string, char *outAxis,
653
                                         size_t axisStringLen, int *outMin,
654
0
                                         int *outMax) {
655
0
  char *number = NULL;
656
0
  char *check = NULL;
657
0
  char *colon = NULL;
658
659
  /* find brackets */
660
0
  number = strchr(string, '(');
661
662
0
  if (NULL == number) {
663
0
    msSetError(MS_WCSERR, "Invalid extent parameter value : %s.",
664
0
               "msWCSParseScaleExtentString20()", string);
665
0
    return MS_FAILURE;
666
0
  }
667
668
  /* find colon */
669
0
  colon = strchr(string, ':');
670
671
0
  if (NULL == colon || colon < number) {
672
0
    msSetError(MS_WCSERR, "Invalid extent parameter value : %s.",
673
0
               "msWCSParseScaleExtentString20()", string);
674
0
    return MS_FAILURE;
675
0
  }
676
677
  /* cut trailing ')' */
678
0
  check = strchr(string, ')');
679
680
0
  if (NULL == check || check < colon) {
681
0
    msSetError(MS_WCSERR, "Invalid extent parameter value.",
682
0
               "msWCSParseScaleExtentString20()");
683
0
    return MS_FAILURE;
684
0
  }
685
686
0
  *number = '\0';
687
0
  ++number;
688
0
  *colon = '\0';
689
0
  ++colon;
690
0
  *check = '\0';
691
692
0
  strlcpy(outAxis, string, axisStringLen);
693
694
0
  if (msStringParseInteger(number, outMin) != MS_SUCCESS) {
695
0
    *outMin = 0;
696
0
    msSetError(MS_WCSERR, "Invalid min parameter value : %s.",
697
0
               "msWCSParseScaleExtentString20()", number);
698
0
    return MS_FAILURE;
699
0
  } else if (msStringParseInteger(colon, outMax) != MS_SUCCESS) {
700
0
    *outMax = 0;
701
0
    msSetError(MS_WCSERR, "Invalid resolution parameter value : %s.",
702
0
               "msWCSParseScaleExtentString20()", colon);
703
0
    return MS_FAILURE;
704
0
  }
705
706
0
  if (*outMin > *outMax) {
707
0
    msSetError(MS_WCSERR,
708
0
               "Invalid extent: lower part is higher than upper part.",
709
0
               "msWCSParseScaleExtentString20()");
710
0
    return MS_FAILURE;
711
0
  }
712
713
0
  return MS_SUCCESS;
714
0
}
715
716
#if defined(USE_LIBXML2)
717
/*
718
  Utility function to get the first child of a node with a given node name
719
  */
720
721
0
xmlNodePtr msLibXml2GetFirstChild(xmlNodePtr parent, const char *name) {
722
0
  xmlNodePtr node;
723
0
  if (!parent || !name) {
724
0
    return NULL;
725
0
  }
726
727
0
  XML_FOREACH_CHILD(parent, node) {
728
0
    XML_LOOP_IGNORE_COMMENT_OR_TEXT(node);
729
0
    if (EQUAL((char *)node->name, name)) {
730
0
      return node;
731
0
    }
732
0
  }
733
0
  return NULL;
734
0
}
735
736
/*
737
  Utility function to get the first child of a node with a given node name and
738
  namespace.
739
  */
740
741
xmlNodePtr msLibXml2GetFirstChildNs(xmlNodePtr parent, const char *name,
742
0
                                    xmlNsPtr ns) {
743
0
  xmlNodePtr node;
744
0
  if (!parent || !name || !ns) {
745
0
    return NULL;
746
0
  }
747
748
0
  XML_FOREACH_CHILD(parent, node) {
749
0
    XML_LOOP_IGNORE_COMMENT_OR_TEXT(node);
750
0
    if (EQUAL((char *)node->name, name) && ns == node->ns) {
751
0
      return node;
752
0
    }
753
0
  }
754
0
  return NULL;
755
0
}
756
757
/*
758
  Utility function to get the first child of a node with a given node name
759
  */
760
761
0
xmlNodePtr msLibXml2GetFirstChildElement(xmlNodePtr parent) {
762
0
  xmlNodePtr node;
763
0
  if (!parent) {
764
0
    return NULL;
765
0
  }
766
767
0
  XML_FOREACH_CHILD(parent, node) {
768
0
    if (node->type == XML_ELEMENT_NODE) {
769
0
      return node;
770
0
    }
771
0
  }
772
0
  return NULL;
773
0
}
774
#endif /* defined(USE_LIBXML2) */
775
776
/************************************************************************/
777
/*                   msWCSParseRequest20_XMLGetCapabilities()           */
778
/*                                                                      */
779
/*      Parses a DOM element, representing a GetCapabilities-request    */
780
/*      to a params object.                                             */
781
/************************************************************************/
782
#if defined(USE_LIBXML2)
783
static int msWCSParseRequest20_XMLGetCapabilities(xmlNodePtr root,
784
0
                                                  wcs20ParamsObjPtr params) {
785
0
  xmlNodePtr child;
786
0
  char *content = NULL;
787
0
  XML_FOREACH_CHILD(root, child) {
788
0
    XML_LOOP_IGNORE_COMMENT_OR_TEXT(child)
789
0
    else if (EQUAL((char *)child->name, "AcceptVersions")) {
790
0
      xmlNodePtr versionNode = NULL;
791
0
      XML_FOREACH_CHILD(child, versionNode) {
792
0
        XML_LOOP_IGNORE_COMMENT_OR_TEXT(versionNode);
793
0
        XML_ASSERT_NODE_NAME(versionNode, "Version");
794
795
0
        content = (char *)xmlNodeGetContent(versionNode);
796
0
        params->accept_versions =
797
0
            CSLAddString(params->accept_versions, content);
798
0
        xmlFree(content);
799
0
      }
800
0
    }
801
0
    else if (EQUAL((char *)child->name, "Sections")) {
802
0
      xmlNodePtr sectionNode = NULL;
803
0
      XML_FOREACH_CHILD(child, sectionNode) {
804
0
        XML_LOOP_IGNORE_COMMENT_OR_TEXT(sectionNode)
805
0
        XML_ASSERT_NODE_NAME(sectionNode, "Section");
806
807
0
        content = (char *)xmlNodeGetContent(sectionNode);
808
0
        params->sections = CSLAddString(params->sections, content);
809
0
        xmlFree(content);
810
0
      }
811
0
    }
812
0
    else if (EQUAL((char *)child->name, "UpdateSequence")) {
813
0
      params->updatesequence = (char *)xmlNodeGetContent(child);
814
0
    }
815
0
    else if (EQUAL((char *)child->name, "AcceptFormats")) {
816
      /* Maybe not necessary, since only format is xml.   */
817
      /* At least ignore it, to not generate an error.    */
818
0
    }
819
0
    else if (EQUAL((char *)child->name, "AcceptLanguages")) {
820
0
      xmlNodePtr languageNode;
821
0
      XML_FOREACH_CHILD(child, languageNode) {
822
0
        XML_LOOP_IGNORE_COMMENT_OR_TEXT(languageNode)
823
0
        XML_ASSERT_NODE_NAME(languageNode, "Language");
824
825
0
        content = (char *)xmlNodeGetContent(languageNode);
826
0
        params->accept_languages =
827
0
            CSLAddString(params->accept_languages, content);
828
0
        xmlFree(content);
829
0
      }
830
0
    }
831
0
    else {
832
0
      XML_UNKNOWN_NODE_ERROR(child);
833
0
    }
834
0
  }
835
0
  return MS_SUCCESS;
836
0
}
837
#endif
838
839
/************************************************************************/
840
/*                   msWCSParseRequest20_XMLDescribeCoverage()          */
841
/*                                                                      */
842
/*      Parses a DOM element, representing a DescribeCoverage-request   */
843
/*      to a params object.                                             */
844
/************************************************************************/
845
#if defined(USE_LIBXML2)
846
static int msWCSParseRequest20_XMLDescribeCoverage(xmlNodePtr root,
847
0
                                                   wcs20ParamsObjPtr params) {
848
0
  xmlNodePtr child;
849
0
  char *id;
850
851
0
  XML_FOREACH_CHILD(root, child) {
852
0
    XML_LOOP_IGNORE_COMMENT_OR_TEXT(child)
853
0
    XML_ASSERT_NODE_NAME(child, "CoverageID");
854
855
    /* Node content is the coverage ID */
856
0
    id = (char *)xmlNodeGetContent(child);
857
0
    if (id == NULL || strlen(id) == 0) {
858
0
      msSetError(MS_WCSERR, "CoverageID could not be parsed.",
859
0
                 "msWCSParseRequest20_XMLDescribeCoverage()");
860
0
      return MS_FAILURE;
861
0
    }
862
    /* insert coverage ID into the list */
863
0
    params->ids = CSLAddString(params->ids, (char *)id);
864
0
    xmlFree(id);
865
0
  }
866
0
  return MS_SUCCESS;
867
0
}
868
#endif
869
870
/************************************************************************/
871
/*                   msWCSParseRequest20_XMLGetCoverage()               */
872
/*                                                                      */
873
/*      Parses a DOM element, representing a GetCoverage-request to a   */
874
/*      params object.                                                  */
875
/************************************************************************/
876
#if defined(USE_LIBXML2)
877
static int msWCSParseRequest20_XMLGetCoverage(mapObj *map, xmlNodePtr root,
878
0
                                              wcs20ParamsObjPtr params) {
879
0
  xmlNodePtr child;
880
0
  char *id;
881
882
0
  XML_FOREACH_CHILD(root, child) {
883
0
    XML_LOOP_IGNORE_COMMENT_OR_TEXT(child)
884
0
    else if (EQUAL((char *)child->name, "CoverageID")) {
885
      /* Node content is the coverage ID */
886
0
      id = (char *)xmlNodeGetContent(child);
887
0
      if (id == NULL || strlen(id) == 0) {
888
0
        msSetError(MS_WCSERR, "CoverageID could not be parsed.",
889
0
                   "msWCSParseRequest20_XMLGetCoverage()");
890
0
        return MS_FAILURE;
891
0
      }
892
893
      /* insert coverage ID into the list */
894
0
      params->ids = CSLAddString(params->ids, (char *)id);
895
0
      xmlFree(id);
896
0
    }
897
0
    else if (EQUAL((char *)child->name, "Format")) {
898
0
      msFree(params->format);
899
0
      params->format = (char *)xmlNodeGetContent(child);
900
0
    }
901
0
    else if (EQUAL((char *)child->name, "Mediatype")) {
902
0
      char *content = (char *)xmlNodeGetContent(child);
903
0
      if (content != NULL && (EQUAL(content, "multipart/mixed") ||
904
0
                              EQUAL(content, "multipart/related"))) {
905
0
        params->multipart = MS_TRUE;
906
0
      } else {
907
0
        msSetError(MS_WCSERR, "Invalid value '%s' for parameter 'Mediatype'.",
908
0
                   "msWCSParseRequest20()", content);
909
0
        xmlFree(content);
910
0
        return MS_FAILURE;
911
0
      }
912
0
      xmlFree(content);
913
0
    }
914
0
    else if (EQUAL((char *)child->name, "DimensionTrim")) {
915
0
      wcs20AxisObjPtr axis = NULL;
916
0
      wcs20SubsetObjPtr subset = NULL;
917
0
      xmlNodePtr node = NULL;
918
0
      char *axisName = NULL, *min = NULL, *max = NULL, *crs = NULL;
919
920
      /* get strings for axis, min and max */
921
0
      XML_FOREACH_CHILD(child, node) {
922
0
        XML_LOOP_IGNORE_COMMENT_OR_TEXT(node)
923
0
        else if (EQUAL((char *)node->name, "Dimension")) {
924
0
          if (axisName != NULL) {
925
0
            msSetError(MS_WCSERR, "Parameter 'Dimension' is already set.",
926
0
                       "msWCSParseRequest20_XMLGetCoverage()");
927
0
            return MS_FAILURE;
928
0
          }
929
0
          axisName = (char *)xmlNodeGetContent(node);
930
0
          crs = (char *)xmlGetProp(node, BAD_CAST "crs");
931
0
        }
932
0
        else if (EQUAL((char *)node->name, "trimLow")) {
933
0
          min = (char *)xmlNodeGetContent(node);
934
0
        }
935
0
        else if (EQUAL((char *)node->name, "trimHigh")) {
936
0
          max = (char *)xmlNodeGetContent(node);
937
0
        }
938
0
        else {
939
0
          msFree(axisName);
940
0
          msFree(min);
941
0
          msFree(max);
942
0
          msFree(crs);
943
0
          XML_UNKNOWN_NODE_ERROR(node);
944
0
        }
945
0
      }
946
0
      if (NULL == (subset = msWCSCreateSubsetObj20())) {
947
0
        msFree(axisName);
948
0
        msFree(min);
949
0
        msFree(max);
950
0
        msFree(crs);
951
0
        return MS_FAILURE;
952
0
      }
953
954
      /* min and max have to have a value */
955
0
      if (min == NULL) {
956
0
        min = msStrdup("*");
957
0
      }
958
0
      if (max == NULL) {
959
0
        max = msStrdup("*");
960
0
      }
961
0
      if (msWCSParseSubset20(subset, axisName, crs, min, max) == MS_FAILURE) {
962
0
        msWCSFreeSubsetObj20(subset);
963
0
        msWCSException(map, "InvalidSubsetting", "subset", "2.0.1");
964
0
        return MS_DONE;
965
0
      }
966
967
0
      if (NULL == (axis = msWCSFindAxis20(params, subset->axis))) {
968
0
        if (NULL == (axis = msWCSCreateAxisObj20())) {
969
0
          msFree(axisName);
970
0
          msFree(min);
971
0
          msFree(max);
972
0
          msFree(crs);
973
0
          return MS_FAILURE;
974
0
        }
975
0
        axis->name = msStrdup(subset->axis);
976
0
        msWCSInsertAxisObj20(params, axis);
977
0
      }
978
979
0
      axis->subset = subset;
980
981
      /* cleanup */
982
0
      msFree(axisName);
983
0
      msFree(min);
984
0
      msFree(max);
985
0
      msFree(crs);
986
0
    }
987
0
    else if (EQUAL((char *)child->name, "DimensionSlice")) {
988
0
      msSetError(MS_WCSERR, "Operation '%s' is not supported by MapServer.",
989
0
                 "msWCSParseRequest20_XMLGetCoverage()", (char *)child->name);
990
0
      return MS_FAILURE;
991
0
    }
992
0
    else if (EQUAL((char *)child->name, "Size")) {
993
0
      wcs20AxisObjPtr axis;
994
0
      char *axisName;
995
0
      char *content;
996
997
0
      if (NULL ==
998
0
          (axisName = (char *)xmlGetProp(child, BAD_CAST "dimension"))) {
999
0
        msSetError(MS_WCSERR,
1000
0
                   "Attribute 'dimension' is missing in element 'Size'.",
1001
0
                   "msWCSParseRequest20_XMLGetCoverage()");
1002
0
        return MS_FAILURE;
1003
0
      }
1004
1005
0
      if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1006
0
        if (NULL == (axis = msWCSCreateAxisObj20())) {
1007
0
          xmlFree(axisName);
1008
0
          return MS_FAILURE;
1009
0
        }
1010
0
        axis->name = msStrdup(axisName);
1011
0
        msWCSInsertAxisObj20(params, axis);
1012
0
      }
1013
0
      xmlFree(axisName);
1014
1015
0
      content = (char *)xmlNodeGetContent(child);
1016
0
      if (msStringParseInteger(content, &(axis->size)) != MS_SUCCESS) {
1017
0
        xmlFree(content);
1018
0
        msSetError(MS_WCSERR,
1019
0
                   "Value of element 'Size' could not "
1020
0
                   "be parsed to a valid integer.",
1021
0
                   "msWCSParseRequest20_XMLGetCoverage()");
1022
0
        return MS_FAILURE;
1023
0
      }
1024
0
      xmlFree(content);
1025
0
    }
1026
0
    else if (EQUAL((char *)child->name, "Resolution")) {
1027
0
      wcs20AxisObjPtr axis;
1028
0
      char *axisName;
1029
0
      char *content;
1030
1031
0
      if (NULL ==
1032
0
          (axisName = (char *)xmlGetProp(child, BAD_CAST "dimension"))) {
1033
0
        msSetError(MS_WCSERR,
1034
0
                   "Attribute 'dimension' is missing "
1035
0
                   "in element 'Resolution'.",
1036
0
                   "msWCSParseRequest20_XMLGetCoverage()");
1037
0
        return MS_FAILURE;
1038
0
      }
1039
1040
0
      if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1041
0
        if (NULL == (axis = msWCSCreateAxisObj20())) {
1042
0
          xmlFree(axisName);
1043
0
          return MS_FAILURE;
1044
0
        }
1045
0
        axis->name = msStrdup(axisName);
1046
0
        msWCSInsertAxisObj20(params, axis);
1047
0
      }
1048
0
      xmlFree(axisName);
1049
1050
0
      axis->resolutionUOM = (char *)xmlGetProp(child, BAD_CAST "uom");
1051
1052
0
      content = (char *)xmlNodeGetContent(child);
1053
0
      if (msStringParseDouble(content, &(axis->resolution)) != MS_SUCCESS) {
1054
0
        msSetError(MS_WCSERR,
1055
0
                   "Value of element 'Resolution' could not "
1056
0
                   "be parsed to a valid value.",
1057
0
                   "msWCSParseRequest20_XMLGetCoverage()");
1058
0
        xmlFree(content);
1059
0
        return MS_FAILURE;
1060
0
      }
1061
0
      xmlFree(content);
1062
0
    }
1063
0
    else if (EQUAL((char *)child->name, "Interpolation")) {
1064
      /* Deprecated, use wcs:Extension/int:Interpolation/int:globalInterpolation
1065
       */
1066
0
      msFree(params->interpolation);
1067
0
      params->interpolation = (char *)xmlNodeGetContent(child);
1068
0
    }
1069
0
    else if (EQUAL((char *)child->name, "OutputCRS")) {
1070
0
      params->outputcrs = (char *)xmlNodeGetContent(child);
1071
0
    }
1072
0
    else if (EQUAL((char *)child->name, "rangeSubset")) {
1073
      /* Deprecated, use wcs:Extension/rsub:RangeSubset */
1074
0
      xmlNodePtr bandNode = NULL;
1075
0
      XML_FOREACH_CHILD(child, bandNode) {
1076
0
        char *content = NULL;
1077
0
        XML_LOOP_IGNORE_COMMENT_OR_TEXT(bandNode);
1078
0
        XML_ASSERT_NODE_NAME(bandNode, "band");
1079
1080
0
        content = (char *)xmlNodeGetContent(bandNode);
1081
0
        params->range_subset = CSLAddString(params->range_subset, content);
1082
0
        xmlFree(content);
1083
0
      }
1084
0
    }
1085
0
    else if (EQUAL((char *)child->name, "Extension")) {
1086
0
      xmlNodePtr extensionNode = NULL;
1087
0
      XML_FOREACH_CHILD(child, extensionNode) {
1088
0
        XML_LOOP_IGNORE_COMMENT_OR_TEXT(extensionNode);
1089
1090
0
        if (EQUAL((char *)extensionNode->name, "Scaling")) {
1091
0
          xmlNodePtr scaleMethodNode =
1092
0
              msLibXml2GetFirstChildElement(extensionNode);
1093
1094
0
          if (EQUAL((char *)scaleMethodNode->name, "ScaleByFactor")) {
1095
0
            xmlNodePtr scaleFactorNode =
1096
0
                msLibXml2GetFirstChildElement(scaleMethodNode);
1097
0
            char *content;
1098
0
            if (!scaleFactorNode ||
1099
0
                !EQUAL((char *)scaleFactorNode->name, "scaleFactor")) {
1100
0
              msSetError(MS_WCSERR, "Missing 'scaleFactor' node.",
1101
0
                         "msWCSParseRequest20_XMLGetCoverage()");
1102
0
              return MS_FAILURE;
1103
0
            }
1104
0
            content = (char *)xmlNodeGetContent(scaleFactorNode);
1105
0
            if (msStringParseDouble(content, &(params->scale)) != MS_SUCCESS ||
1106
0
                params->scale < 0.0) {
1107
0
              msSetError(MS_WCSERR, "Invalid scaleFactor '%s'.",
1108
0
                         "msWCSParseRequest20_XMLGetCoverage()", content);
1109
0
              xmlFree(content);
1110
0
              return MS_FAILURE;
1111
0
            }
1112
0
            xmlFree(content);
1113
0
          }
1114
1115
0
          else if (EQUAL((char *)scaleMethodNode->name, "ScaleAxesByFactor")) {
1116
0
            xmlNodePtr scaleAxisNode, axisNode, scaleFactorNode;
1117
0
            char *axisName, *content;
1118
0
            wcs20AxisObjPtr axis;
1119
1120
0
            XML_FOREACH_CHILD(scaleMethodNode, scaleAxisNode) {
1121
0
              XML_LOOP_IGNORE_COMMENT_OR_TEXT(scaleAxisNode);
1122
1123
0
              if (!EQUAL((char *)scaleAxisNode->name, "ScaleAxis")) {
1124
0
                msSetError(MS_WCSERR, "Invalid ScaleAxesByFactor.",
1125
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1126
0
                return MS_FAILURE;
1127
0
              }
1128
1129
              /* axis */
1130
0
              if (NULL ==
1131
0
                  (axisNode = msLibXml2GetFirstChild(scaleAxisNode, "axis"))) {
1132
0
                msSetError(MS_WCSERR, "Missing axis node",
1133
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1134
0
                return MS_FAILURE;
1135
0
              }
1136
0
              axisName = (char *)xmlNodeGetContent(axisNode);
1137
0
              if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1138
0
                if (NULL == (axis = msWCSCreateAxisObj20())) {
1139
0
                  xmlFree(axisName);
1140
0
                  return MS_FAILURE;
1141
0
                }
1142
0
                axis->name = msStrdup(axisName);
1143
0
                msWCSInsertAxisObj20(params, axis);
1144
0
              }
1145
0
              xmlFree(axisName);
1146
1147
0
              if (axis->scale != MS_WCS20_UNBOUNDED) {
1148
0
                msSetError(MS_WCSERR,
1149
0
                           "scaleFactor was already set for axis '%s'.",
1150
0
                           "msWCSParseRequest20_XMLGetCoverage()", axis->name);
1151
0
                return MS_FAILURE;
1152
0
              }
1153
1154
              /* scaleFactor */
1155
0
              if (NULL == (scaleFactorNode = msLibXml2GetFirstChild(
1156
0
                               scaleAxisNode, "scaleFactor"))) {
1157
0
                msSetError(MS_WCSERR, "Missing scaleFactor node",
1158
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1159
0
                return MS_FAILURE;
1160
0
              }
1161
1162
0
              content = (char *)xmlNodeGetContent(scaleFactorNode);
1163
0
              if (msStringParseDouble(content, &(axis->scale)) != MS_SUCCESS ||
1164
0
                  axis->scale < 0.0) {
1165
0
                msSetError(MS_WCSERR, "Invalid scaleFactor '%s'.",
1166
0
                           "msWCSParseRequest20_XMLGetCoverage()", content);
1167
0
                xmlFree(content);
1168
0
                return MS_FAILURE;
1169
0
              }
1170
0
              xmlFree(content);
1171
0
            }
1172
0
          }
1173
1174
0
          else if (EQUAL((char *)scaleMethodNode->name, "ScaleToSize")) {
1175
0
            xmlNodePtr scaleAxisNode, axisNode, targetSizeNode;
1176
0
            char *axisName, *content;
1177
0
            wcs20AxisObjPtr axis;
1178
1179
0
            XML_FOREACH_CHILD(scaleMethodNode, scaleAxisNode) {
1180
0
              XML_LOOP_IGNORE_COMMENT_OR_TEXT(scaleAxisNode);
1181
1182
0
              if (!EQUAL((char *)scaleAxisNode->name, "TargetAxisSize")) {
1183
0
                msSetError(MS_WCSERR, "Invalid ScaleToSize.",
1184
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1185
0
                return MS_FAILURE;
1186
0
              }
1187
1188
              /* axis */
1189
0
              if (NULL ==
1190
0
                  (axisNode = msLibXml2GetFirstChild(scaleAxisNode, "axis"))) {
1191
0
                msSetError(MS_WCSERR, "Missing axis node",
1192
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1193
0
                return MS_FAILURE;
1194
0
              }
1195
0
              axisName = (char *)xmlNodeGetContent(axisNode);
1196
0
              if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1197
0
                if (NULL == (axis = msWCSCreateAxisObj20())) {
1198
0
                  xmlFree(axisName);
1199
0
                  return MS_FAILURE;
1200
0
                }
1201
0
                axis->name = msStrdup(axisName);
1202
0
                msWCSInsertAxisObj20(params, axis);
1203
0
              }
1204
0
              xmlFree(axisName);
1205
1206
0
              if (axis->size != 0) {
1207
0
                msSetError(MS_WCSERR,
1208
0
                           "targetSize was already set for axis '%s'.",
1209
0
                           "msWCSParseRequest20_XMLGetCoverage()", axis->name);
1210
0
                return MS_FAILURE;
1211
0
              }
1212
1213
              /* targetSize */
1214
0
              if (NULL == (targetSizeNode = msLibXml2GetFirstChild(
1215
0
                               scaleAxisNode, "targetSize"))) {
1216
0
                msSetError(MS_WCSERR, "Missing targetSize node",
1217
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1218
0
                return MS_FAILURE;
1219
0
              }
1220
1221
0
              content = (char *)xmlNodeGetContent(targetSizeNode);
1222
0
              if (msStringParseInteger(content, &(axis->size)) != MS_SUCCESS ||
1223
0
                  axis->size <= 0) {
1224
0
                msSetError(MS_WCSERR, "Invalid targetSize '%s'.",
1225
0
                           "msWCSParseRequest20_XMLGetCoverage()", content);
1226
0
                xmlFree(content);
1227
0
                return MS_FAILURE;
1228
0
              }
1229
0
              xmlFree(content);
1230
0
            }
1231
0
          }
1232
1233
0
          else if (EQUAL((char *)scaleMethodNode->name, "ScaleToExtent")) {
1234
0
            xmlNodePtr scaleAxisNode, axisNode, lowNode, highNode;
1235
0
            char *axisName, *content;
1236
0
            wcs20AxisObjPtr axis;
1237
0
            int low, high;
1238
1239
0
            XML_FOREACH_CHILD(scaleMethodNode, scaleAxisNode) {
1240
0
              XML_LOOP_IGNORE_COMMENT_OR_TEXT(scaleAxisNode);
1241
1242
0
              if (!EQUAL((char *)scaleAxisNode->name, "TargetAxisExtent")) {
1243
0
                msSetError(MS_WCSERR, "Invalid ScaleToExtent.",
1244
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1245
0
                return MS_FAILURE;
1246
0
              }
1247
1248
              /* axis */
1249
0
              if (NULL ==
1250
0
                  (axisNode = msLibXml2GetFirstChild(scaleAxisNode, "axis"))) {
1251
0
                msSetError(MS_WCSERR, "Missing axis node",
1252
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1253
0
                return MS_FAILURE;
1254
0
              }
1255
0
              axisName = (char *)xmlNodeGetContent(axisNode);
1256
0
              if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1257
0
                if (NULL == (axis = msWCSCreateAxisObj20())) {
1258
0
                  xmlFree(axisName);
1259
0
                  return MS_FAILURE;
1260
0
                }
1261
0
                axis->name = msStrdup(axisName);
1262
0
                msWCSInsertAxisObj20(params, axis);
1263
0
              }
1264
0
              xmlFree(axisName);
1265
1266
0
              if (axis->size != 0) {
1267
0
                msSetError(MS_WCSERR,
1268
0
                           "targetSize was already set for axis '%s'.",
1269
0
                           "msWCSParseRequest20_XMLGetCoverage()", axis->name);
1270
0
                return MS_FAILURE;
1271
0
              }
1272
1273
              /* targetSize */
1274
0
              if (NULL ==
1275
0
                  (lowNode = msLibXml2GetFirstChild(scaleAxisNode, "low"))) {
1276
0
                msSetError(MS_WCSERR, "Missing low node",
1277
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1278
0
                return MS_FAILURE;
1279
0
              }
1280
1281
0
              if (NULL ==
1282
0
                  (highNode = msLibXml2GetFirstChild(scaleAxisNode, "high"))) {
1283
0
                msSetError(MS_WCSERR, "Missing high node",
1284
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1285
0
                return MS_FAILURE;
1286
0
              }
1287
1288
0
              content = (char *)xmlNodeGetContent(lowNode);
1289
0
              if (msStringParseInteger(content, &low) != MS_SUCCESS) {
1290
0
                msSetError(MS_WCSERR, "Invalid low value '%s'.",
1291
0
                           "msWCSParseRequest20_XMLGetCoverage()", content);
1292
0
                xmlFree(content);
1293
0
                return MS_FAILURE;
1294
0
              }
1295
0
              xmlFree(content);
1296
1297
0
              content = (char *)xmlNodeGetContent(highNode);
1298
0
              if (msStringParseInteger(content, &high) != MS_SUCCESS) {
1299
0
                msSetError(MS_WCSERR, "Invalid high value '%s'.",
1300
0
                           "msWCSParseRequest20_XMLGetCoverage()", content);
1301
0
                xmlFree(content);
1302
0
                return MS_FAILURE;
1303
0
              }
1304
0
              xmlFree(content);
1305
1306
0
              if (high <= low) {
1307
0
                msSetError(MS_WCSERR, "Invalid extent, high is lower than low.",
1308
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1309
0
                return MS_FAILURE;
1310
0
              }
1311
1312
0
              axis->size = high - low;
1313
0
            }
1314
0
          }
1315
0
        }
1316
1317
        /* Range Subset */
1318
0
        else if (EQUAL((char *)extensionNode->name, "RangeSubset")) {
1319
0
          xmlNodePtr rangeItemNode = NULL;
1320
1321
0
          XML_FOREACH_CHILD(extensionNode, rangeItemNode) {
1322
0
            xmlNodePtr rangeItemNodeChild =
1323
0
                msLibXml2GetFirstChildElement(rangeItemNode);
1324
0
            XML_LOOP_IGNORE_COMMENT_OR_TEXT(rangeItemNode);
1325
1326
0
            XML_ASSERT_NODE_NAME(rangeItemNode, "RangeItem");
1327
1328
0
            if (!rangeItemNodeChild) {
1329
0
              msSetError(MS_WCSERR, "Missing RangeComponent or RangeInterval.",
1330
0
                         "msWCSParseRequest20_XMLGetCoverage()");
1331
0
              return MS_FAILURE;
1332
0
            } else if (EQUAL((char *)rangeItemNodeChild->name,
1333
0
                             "RangeComponent")) {
1334
0
              char *content = (char *)xmlNodeGetContent(rangeItemNodeChild);
1335
0
              params->range_subset =
1336
0
                  CSLAddString(params->range_subset, content);
1337
0
              xmlFree(content);
1338
0
            } else if (EQUAL((char *)rangeItemNodeChild->name,
1339
0
                             "RangeInterval")) {
1340
0
              xmlNodePtr intervalNode = rangeItemNodeChild;
1341
0
              xmlNodePtr startComponentNode =
1342
0
                  msLibXml2GetFirstChild(intervalNode, "startComponent");
1343
0
              xmlNodePtr endComponentNode =
1344
0
                  msLibXml2GetFirstChild(intervalNode, "endComponent");
1345
0
              char *start;
1346
0
              char *stop;
1347
1348
0
              if (!startComponentNode || !endComponentNode) {
1349
0
                msSetError(MS_WCSERR, "Wrong RangeInterval.",
1350
0
                           "msWCSParseRequest20_XMLGetCoverage()");
1351
0
                return MS_FAILURE;
1352
0
              }
1353
1354
0
              start = (char *)xmlNodeGetContent(startComponentNode);
1355
0
              stop = (char *)xmlNodeGetContent(endComponentNode);
1356
1357
0
              std::string value(start);
1358
0
              value += ':';
1359
0
              value += stop;
1360
1361
0
              xmlFree(start);
1362
0
              xmlFree(stop);
1363
1364
0
              params->range_subset =
1365
0
                  CSLAddString(params->range_subset, value.c_str());
1366
0
            }
1367
0
          }
1368
0
        }
1369
1370
0
        else if (EQUAL((char *)extensionNode->name, "subsettingCrs")) {
1371
0
          msFree(params->subsetcrs);
1372
0
          params->subsetcrs = (char *)xmlNodeGetContent(extensionNode);
1373
0
        }
1374
1375
0
        else if (EQUAL((char *)extensionNode->name, "outputCrs")) {
1376
0
          msFree(params->outputcrs);
1377
0
          params->outputcrs = (char *)xmlNodeGetContent(extensionNode);
1378
0
        }
1379
1380
0
        else if (EQUAL((char *)extensionNode->name, "Interpolation")) {
1381
0
          xmlNodePtr globalInterpolation =
1382
0
              msLibXml2GetFirstChild(extensionNode, "globalInterpolation");
1383
0
          char *content;
1384
0
          if (globalInterpolation == NULL) {
1385
0
            msSetError(MS_WCSERR, "Missing 'globalInterpolation' node.",
1386
0
                       "msWCSParseRequest20_XMLGetCoverage()");
1387
0
            return MS_FAILURE;
1388
0
          }
1389
0
          content = (char *)xmlNodeGetContent(globalInterpolation);
1390
0
          msFree(params->interpolation);
1391
          /* TODO: use URIs/URLs once they are specified */
1392
0
          params->interpolation = msStrdup(content);
1393
0
          xmlFree(content);
1394
0
        }
1395
1396
        /* GeoTIFF parameters */
1397
0
        else if (EQUAL((char *)extensionNode->name, "parameters") &&
1398
0
                 extensionNode->ns &&
1399
0
                 EQUAL((char *)extensionNode->ns->href,
1400
0
                       "http://www.opengis.net/gmlcov/geotiff/1.0")) {
1401
1402
0
          xmlNodePtr parameter;
1403
1404
0
          XML_FOREACH_CHILD(extensionNode, parameter) {
1405
0
            char *content;
1406
0
            XML_LOOP_IGNORE_COMMENT_OR_TEXT(parameter);
1407
1408
0
            content = (char *)xmlNodeGetContent(parameter);
1409
1410
0
            params->format_options = CSLAddNameValue(
1411
0
                params->format_options, (char *)parameter->name, content);
1412
0
            xmlFree(content);
1413
0
          }
1414
0
        }
1415
0
      }
1416
0
    }
1417
0
    else {
1418
0
      XML_UNKNOWN_NODE_ERROR(child);
1419
0
    }
1420
0
  }
1421
0
  return MS_SUCCESS;
1422
0
}
1423
#endif
1424
1425
/************************************************************************/
1426
/*                   msWCSParseRequest20()                              */
1427
/*                                                                      */
1428
/*      Parses a CGI-request to a WCS 20 params object. It is           */
1429
/*      either a POST or a GET request. In case of a POST request       */
1430
/*      the xml content has to be parsed to a DOM structure             */
1431
/*      before the parameters can be extracted.                         */
1432
/************************************************************************/
1433
1434
int msWCSParseRequest20(mapObj *map, cgiRequestObj *request,
1435
0
                        owsRequestObj *ows_request, wcs20ParamsObjPtr params) {
1436
0
  int i;
1437
0
  if (params == NULL || request == NULL || ows_request == NULL) {
1438
0
    msSetError(MS_WCSERR, "Internal error.", "msWCSParseRequest20()");
1439
0
    return MS_FAILURE;
1440
0
  }
1441
1442
  /* Copy arbitrary service, version and request. */
1443
0
  params->service = msStrdup(ows_request->service);
1444
0
  if (ows_request->version != NULL) {
1445
0
    params->version = msStrdup(ows_request->version);
1446
0
  }
1447
0
  params->request = msStrdup(ows_request->request);
1448
1449
  /* Parse the POST request */
1450
0
  if (request->type == MS_POST_REQUEST) {
1451
0
#if defined(USE_LIBXML2)
1452
0
    xmlDocPtr doc = static_cast<xmlDocPtr>(ows_request->document);
1453
0
    xmlNodePtr root = NULL;
1454
0
    const char *validate;
1455
0
    int ret = MS_SUCCESS;
1456
1457
    /* parse to DOM-Structure and get root element */
1458
0
    if (doc == NULL) {
1459
0
      const xmlError *error = xmlGetLastError();
1460
0
      msSetError(MS_WCSERR, "XML parsing error: %s", "msWCSParseRequest20()",
1461
0
                 error->message);
1462
0
      return MS_FAILURE;
1463
0
    }
1464
1465
0
    root = xmlDocGetRootElement(doc);
1466
1467
0
    validate = msOWSLookupMetadata(&(map->web.metadata), "CO", "validate_xml");
1468
0
    if (validate != NULL && EQUAL(validate, "TRUE")) {
1469
0
      char *schema_dir = msStrdup(
1470
0
          msOWSLookupMetadata(&(map->web.metadata), "CO", "schemas_dir"));
1471
0
      if (schema_dir != NULL &&
1472
0
          (params->version == NULL || EQUALN(params->version, "2.0", 3))) {
1473
0
        schema_dir = msStringConcatenate(schema_dir, "wcs/2.0.0/wcsAll.xsd");
1474
0
        if (msOWSSchemaValidation(schema_dir, request->postrequest) != 0) {
1475
0
          msSetError(MS_WCSERR,
1476
0
                     "Invalid POST request. "
1477
0
                     "XML is not valid",
1478
0
                     "msWCSParseRequest20()");
1479
0
          return MS_FAILURE;
1480
0
        }
1481
0
      }
1482
0
      msFree(schema_dir);
1483
0
    }
1484
1485
0
    if (EQUAL(params->request, "GetCapabilities")) {
1486
0
      ret = msWCSParseRequest20_XMLGetCapabilities(root, params);
1487
0
    } else if (params->version != NULL && EQUALN(params->version, "2.0", 3)) {
1488
0
      if (EQUAL(params->request, "DescribeCoverage")) {
1489
0
        ret = msWCSParseRequest20_XMLDescribeCoverage(root, params);
1490
0
      } else if (EQUAL(params->request, "GetCoverage")) {
1491
0
        ret = msWCSParseRequest20_XMLGetCoverage(map, root, params);
1492
0
      }
1493
0
    }
1494
0
    return ret;
1495
1496
#else  /* defined(USE_LIBXML2) */
1497
    /* TODO: maybe with CPLXML? */
1498
    return MS_FAILURE;
1499
#endif /* defined(USE_LIBXML2) */
1500
0
  }
1501
1502
  /* Parse the KVP GET request */
1503
0
  for (i = 0; i < request->NumParams; ++i) {
1504
0
    char *key = NULL, *value = NULL;
1505
0
    char **tokens;
1506
0
    int num, j;
1507
0
    key = request->ParamNames[i];
1508
0
    value = request->ParamValues[i];
1509
1510
0
    if (EQUAL(key, "VERSION")) {
1511
0
      continue;
1512
0
    } else if (EQUAL(key, "REQUEST")) {
1513
0
      continue;
1514
0
    } else if (EQUAL(key, "SERVICE")) {
1515
0
      continue;
1516
0
    } else if (EQUAL(key, "ACCEPTVERSIONS")) {
1517
0
      tokens = msStringSplit(value, ',', &num);
1518
0
      for (j = 0; j < num; ++j) {
1519
0
        params->accept_versions =
1520
0
            CSLAddString(params->accept_versions, tokens[j]);
1521
0
      }
1522
0
      msFreeCharArray(tokens, num);
1523
0
    } else if (EQUAL(key, "SECTIONS")) {
1524
0
      tokens = msStringSplit(value, ',', &num);
1525
0
      for (j = 0; j < num; ++j) {
1526
0
        params->sections = CSLAddString(params->sections, tokens[j]);
1527
0
      }
1528
0
      msFreeCharArray(tokens, num);
1529
0
    } else if (EQUAL(key, "UPDATESEQUENCE")) {
1530
0
      msFree(params->updatesequence);
1531
0
      params->updatesequence = msStrdup(value);
1532
0
    } else if (EQUAL(key, "ACCEPTFORMATS")) {
1533
      /* ignore */
1534
0
    } else if (EQUAL(key, "ACCEPTLANGUAGES")) {
1535
0
      if (params->accept_languages != NULL) {
1536
0
        CSLDestroy(params->accept_languages);
1537
0
      }
1538
0
      params->accept_languages = CSLTokenizeString2(value, ",", 0);
1539
0
    } else if (EQUAL(key, "COVERAGEID")) {
1540
0
      if (params->ids != NULL) {
1541
0
        msSetError(MS_WCSERR,
1542
0
                   "Parameter 'CoverageID' is already set. "
1543
0
                   "For multiple IDs use a comma separated list.",
1544
0
                   "msWCSParseRequest20()");
1545
0
        return MS_FAILURE;
1546
0
      }
1547
0
      params->ids = CSLTokenizeString2(value, ",", 0);
1548
0
    } else if (EQUAL(key, "FORMAT")) {
1549
0
      msFree(params->format);
1550
0
      params->format = msStrdup(value);
1551
0
    } else if (EQUAL(key, "MEDIATYPE")) {
1552
0
      if (EQUAL(value, "multipart/mixed") ||
1553
0
          EQUAL(value, "multipart/related")) {
1554
0
        params->multipart = MS_TRUE;
1555
0
      } else {
1556
0
        msSetError(MS_WCSERR, "Invalid value '%s' for parameter 'Mediatype'.",
1557
0
                   "msWCSParseRequest20()", value);
1558
0
        return MS_FAILURE;
1559
0
      }
1560
0
    } else if (EQUAL(key, "INTERPOLATION")) {
1561
0
      msFree(params->interpolation);
1562
0
      params->interpolation = msStrdup(value);
1563
0
    } else if (EQUAL(key, "OUTPUTCRS")) {
1564
0
      msFree(params->outputcrs);
1565
0
      params->outputcrs = msStrdup(value);
1566
0
    } else if (EQUAL(key, "SUBSETTINGCRS")) {
1567
0
      msFree(params->subsetcrs);
1568
0
      params->subsetcrs = msStrdup(value);
1569
0
    } else if (EQUAL(key, "SCALEFACTOR")) {
1570
0
      double scale = MS_WCS20_UNBOUNDED;
1571
0
      if (params->scale != MS_WCS20_UNBOUNDED) {
1572
0
        msSetError(MS_WCSERR, "Parameter 'SCALEFACTOR' already set.",
1573
0
                   "msWCSParseRequest20()");
1574
0
        return MS_FAILURE;
1575
0
      } else if (msStringParseDouble(value, &scale) != MS_SUCCESS) {
1576
0
        msSetError(MS_WCSERR, "Could not parse parameter 'SCALEFACTOR'.",
1577
0
                   "msWCSParseRequest20()");
1578
0
        return MS_FAILURE;
1579
0
      } else if (scale <= 0.0) {
1580
0
        msSetError(MS_WCSERR, "Invalid value for 'SCALEFACTOR'.",
1581
0
                   "msWCSParseRequest20()");
1582
0
        return MS_FAILURE;
1583
0
      }
1584
0
      params->scale = scale;
1585
0
    } else if (EQUAL(key, "SCALEAXES")) {
1586
0
      wcs20AxisObjPtr axis = NULL;
1587
0
      tokens = msStringSplit(value, ',', &num);
1588
0
      for (j = 0; j < num; ++j) {
1589
0
        char axisName[500];
1590
0
        double scale;
1591
1592
0
        if (msWCSParseScaleString20(tokens[j], axisName, sizeof(axisName),
1593
0
                                    &scale) != MS_SUCCESS) {
1594
0
          msFreeCharArray(tokens, num);
1595
0
          return MS_FAILURE;
1596
0
        }
1597
1598
0
        if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1599
0
          if (NULL == (axis = msWCSCreateAxisObj20())) {
1600
0
            msFreeCharArray(tokens, num);
1601
0
            return MS_FAILURE;
1602
0
          }
1603
0
          axis->name = msStrdup(axisName);
1604
0
          msWCSInsertAxisObj20(params, axis);
1605
0
        }
1606
1607
        /* check if the size of the axis is already set */
1608
0
        if (axis->scale != MS_WCS20_UNBOUNDED) {
1609
0
          msFreeCharArray(tokens, num);
1610
0
          msSetError(MS_WCSERR, "The scale of the axis is already set.",
1611
0
                     "msWCSParseRequest20()");
1612
0
          return MS_FAILURE;
1613
0
        }
1614
0
        axis->scale = scale;
1615
0
      }
1616
0
      msFreeCharArray(tokens, num);
1617
0
    } else if (EQUAL(key, "SCALESIZE")) {
1618
0
      wcs20AxisObjPtr axis = NULL;
1619
0
      tokens = msStringSplit(value, ',', &num);
1620
0
      for (j = 0; j < num; ++j) {
1621
0
        char axisName[500];
1622
0
        int size;
1623
1624
0
        if (msWCSParseSizeString20(tokens[j], axisName, sizeof(axisName),
1625
0
                                   &size) != MS_SUCCESS) {
1626
0
          msFreeCharArray(tokens, num);
1627
0
          return MS_FAILURE;
1628
0
        }
1629
1630
0
        if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1631
0
          if (NULL == (axis = msWCSCreateAxisObj20())) {
1632
0
            msFreeCharArray(tokens, num);
1633
0
            return MS_FAILURE;
1634
0
          }
1635
0
          axis->name = msStrdup(axisName);
1636
0
          msWCSInsertAxisObj20(params, axis);
1637
0
        }
1638
1639
        /* check if the size of the axis is already set */
1640
0
        if (axis->size != 0) {
1641
0
          msFreeCharArray(tokens, num);
1642
0
          msSetError(MS_WCSERR, "The size of the axis is already set.",
1643
0
                     "msWCSParseRequest20()");
1644
0
          return MS_FAILURE;
1645
0
        }
1646
0
        axis->size = size;
1647
0
      }
1648
0
      msFreeCharArray(tokens, num);
1649
0
    } else if (EQUAL(key, "SCALEEXTENT")) {
1650
0
      wcs20AxisObjPtr axis = NULL;
1651
      /* No real support for scaleextent, we just interpret it as SCALESIZE */
1652
0
      tokens = msStringSplit(value, ',', &num);
1653
0
      for (j = 0; j < num; ++j) {
1654
0
        char axisName[500];
1655
0
        int min, max;
1656
1657
0
        if (msWCSParseScaleExtentString20(tokens[j], axisName, sizeof(axisName),
1658
0
                                          &min, &max) != MS_SUCCESS) {
1659
0
          msFreeCharArray(tokens, num);
1660
0
          return MS_FAILURE;
1661
0
        }
1662
1663
0
        if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1664
0
          if (NULL == (axis = msWCSCreateAxisObj20())) {
1665
0
            msFreeCharArray(tokens, num);
1666
0
            return MS_FAILURE;
1667
0
          }
1668
0
          axis->name = msStrdup(axisName);
1669
0
          msWCSInsertAxisObj20(params, axis);
1670
0
        }
1671
1672
        /* check if the size of the axis is already set */
1673
0
        if (axis->size != 0) {
1674
0
          msFreeCharArray(tokens, num);
1675
0
          msSetError(MS_WCSERR, "The size of the axis is already set.",
1676
0
                     "msWCSParseRequest20()");
1677
0
          return MS_FAILURE;
1678
0
        }
1679
0
        axis->size = max - min;
1680
0
      }
1681
0
      msFreeCharArray(tokens, num);
1682
      /* We explicitly don't test for strict equality as the parameter name is
1683
       * supposed to be unique */
1684
0
    } else if (EQUALN(key, "SIZE", 4)) {
1685
      /* Deprecated scaling */
1686
0
      wcs20AxisObjPtr axis = NULL;
1687
0
      char axisName[500];
1688
0
      int size = 0;
1689
1690
0
      if (msWCSParseSizeString20(value, axisName, sizeof(axisName), &size) ==
1691
0
          MS_FAILURE) {
1692
0
        return MS_FAILURE;
1693
0
      }
1694
1695
0
      if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1696
0
        if (NULL == (axis = msWCSCreateAxisObj20())) {
1697
0
          return MS_FAILURE;
1698
0
        }
1699
0
        axis->name = msStrdup(axisName);
1700
0
        msWCSInsertAxisObj20(params, axis);
1701
0
      }
1702
1703
      /* check if the size of the axis is already set */
1704
0
      if (axis->size != 0) {
1705
0
        msSetError(MS_WCSERR, "The size of the axis is already set.",
1706
0
                   "msWCSParseRequest20()");
1707
0
        return MS_FAILURE;
1708
0
      }
1709
0
      axis->size = size;
1710
      /* We explicitly don't test for strict equality as the parameter name is
1711
       * supposed to be unique */
1712
0
    } else if (EQUALN(key, "RESOLUTION", 10)) {
1713
0
      wcs20AxisObjPtr axis = NULL;
1714
0
      char axisName[500];
1715
0
      double resolution = 0;
1716
1717
0
      if (msWCSParseResolutionString20(value, axisName, sizeof(axisName),
1718
0
                                       &resolution) == MS_FAILURE) {
1719
0
        return MS_FAILURE;
1720
0
      }
1721
1722
      /* check if axis object already exists, otherwise create a new one */
1723
0
      if (NULL == (axis = msWCSFindAxis20(params, axisName))) {
1724
0
        if (NULL == (axis = msWCSCreateAxisObj20())) {
1725
0
          return MS_FAILURE;
1726
0
        }
1727
0
        axis->name = msStrdup(axisName);
1728
0
        msWCSInsertAxisObj20(params, axis);
1729
0
      }
1730
1731
      /* check if the resolution of the axis is already set */
1732
0
      if (axis->resolution != MS_WCS20_UNBOUNDED) {
1733
0
        msSetError(MS_WCSERR, "The resolution of the axis is already set.",
1734
0
                   "msWCSParseRequest20()");
1735
0
        return MS_FAILURE;
1736
0
      }
1737
0
      axis->resolution = resolution;
1738
      /* We explicitly don't test for strict equality as the parameter name is
1739
       * supposed to be unique */
1740
0
    } else if (EQUALN(key, "SUBSET", 6)) {
1741
0
      wcs20AxisObjPtr axis = NULL;
1742
0
      wcs20SubsetObjPtr subset = msWCSCreateSubsetObj20();
1743
0
      if (NULL == subset) {
1744
0
        return MS_FAILURE;
1745
0
      }
1746
0
      if (msWCSParseSubsetKVPString20(subset, value) == MS_FAILURE) {
1747
0
        msWCSFreeSubsetObj20(subset);
1748
0
        msWCSException(map, "InvalidSubsetting", "subset",
1749
0
                       ows_request->version);
1750
0
        return MS_DONE;
1751
0
      }
1752
1753
0
      if (NULL == (axis = msWCSFindAxis20(params, subset->axis))) {
1754
0
        if (NULL == (axis = msWCSCreateAxisObj20())) {
1755
0
          return MS_FAILURE;
1756
0
        }
1757
0
        axis->name = msStrdup(subset->axis);
1758
0
        msWCSInsertAxisObj20(params, axis);
1759
0
      }
1760
1761
0
      if (NULL != axis->subset) {
1762
0
        msSetError(MS_WCSERR, "The axis '%s' is already subsetted.",
1763
0
                   "msWCSParseRequest20()", axis->name);
1764
0
        msWCSFreeSubsetObj20(subset);
1765
0
        msWCSException(map, "InvalidAxisLabel", "subset", ows_request->version);
1766
0
        return MS_DONE;
1767
0
      }
1768
0
      axis->subset = subset;
1769
0
    } else if (EQUAL(key, "RANGESUBSET")) {
1770
0
      tokens = msStringSplit(value, ',', &num);
1771
0
      for (j = 0; j < num; ++j) {
1772
0
        params->range_subset = CSLAddString(params->range_subset, tokens[j]);
1773
0
      }
1774
0
      msFreeCharArray(tokens, num);
1775
0
    } else if (EQUALN(key, "GEOTIFF:", 8)) {
1776
0
      params->format_options =
1777
0
          CSLAddNameValue(params->format_options, key, value);
1778
0
    }
1779
    /* Ignore all other parameters here */
1780
0
  }
1781
1782
0
  return MS_SUCCESS;
1783
0
}
1784
1785
#if defined(USE_LIBXML2)
1786
1787
/************************************************************************/
1788
/*                   msWCSValidateAndFindSubsets20()                    */
1789
/*                                                                      */
1790
/*      Iterates over every axis in the parameters and checks if the    */
1791
/*      axis name is in any string list. If found, but there already is */
1792
/*      a sample found for this axis, an Error is returned. Also if no  */
1793
/*      axis is found for a given axis, an error is returned.           */
1794
/************************************************************************/
1795
static int msWCSValidateAndFindAxes20(wcs20ParamsObjPtr params,
1796
0
                                      wcs20AxisObjPtr outAxes[]) {
1797
0
  static const int numAxis = 2;
1798
0
  const char *const validXAxisNames[] = {
1799
0
      "x",         "xaxis", "x-axis",   "x_axis",   "long", "long_axis",
1800
0
      "long-axis", "lon",   "lon_axis", "lon-axis", NULL};
1801
0
  const char *const validYAxisNames[] = {
1802
0
      "y", "yaxis", "y-axis", "y_axis", "lat", "lat_axis", "lat-axis", NULL};
1803
0
  const char *const *const validAxisNames[2] = {validXAxisNames,
1804
0
                                                validYAxisNames};
1805
0
  int iParamAxis, iAcceptedAxis, iName, i;
1806
1807
0
  for (i = 0; i < numAxis; ++i) {
1808
0
    outAxes[i] = NULL;
1809
0
  }
1810
1811
  /* iterate over all subsets */
1812
0
  for (iParamAxis = 0; iParamAxis < params->numaxes; ++iParamAxis) {
1813
0
    int found = 0;
1814
1815
    /* iterate over all given axes */
1816
0
    for (iAcceptedAxis = 0; iAcceptedAxis < numAxis; ++iAcceptedAxis) {
1817
      /* iterate over all possible names for the current axis */
1818
0
      for (iName = 0; validAxisNames[iAcceptedAxis][iName] != NULL; ++iName) {
1819
        /* compare axis name with current possible name */
1820
0
        if (EQUAL(params->axes[iParamAxis]->name,
1821
0
                  validAxisNames[iAcceptedAxis][iName])) {
1822
          /* if there is already a sample for the axis, throw error */
1823
0
          if (outAxes[iAcceptedAxis] != NULL) {
1824
0
            msSetError(MS_WCSERR,
1825
0
                       "The axis with the name '%s' corresponds "
1826
0
                       "to the same axis as the subset with the name '%s'.",
1827
0
                       "msWCSValidateAndFindAxes20()",
1828
0
                       outAxes[iAcceptedAxis]->name,
1829
0
                       params->axes[iParamAxis]->name);
1830
0
            return MS_FAILURE;
1831
0
          }
1832
1833
          /* if match is found, save it */
1834
0
          outAxes[iAcceptedAxis] = params->axes[iParamAxis];
1835
0
          found = 1;
1836
0
          break;
1837
0
        }
1838
0
      }
1839
0
      if (found) {
1840
0
        break;
1841
0
      }
1842
0
    }
1843
1844
    /* no valid representation for current subset found */
1845
    /* exit and throw error                             */
1846
0
    if (found == 0) {
1847
0
      msSetError(MS_WCSERR, "Invalid subset axis '%s'.",
1848
0
                 "msWCSValidateAndFindAxes20()",
1849
0
                 params->axes[iParamAxis]->name);
1850
0
      return MS_FAILURE;
1851
0
    }
1852
0
  }
1853
0
  return MS_SUCCESS;
1854
0
}
1855
1856
/************************************************************************/
1857
/*                   msWCSPrepareNamespaces20()                         */
1858
/*                                                                      */
1859
/*      Inserts namespace definitions into the root node of a DOM       */
1860
/*      structure.                                                      */
1861
/************************************************************************/
1862
1863
static void msWCSPrepareNamespaces20(xmlDocPtr pDoc, xmlNodePtr psRootNode,
1864
0
                                     mapObj *map, int addInspire) {
1865
0
  xmlNsPtr psXsiNs;
1866
0
  char *schemaLocation = NULL;
1867
0
  char *xsi_schemaLocation = NULL;
1868
1869
0
  xmlSetNs(psRootNode,
1870
0
           xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_WCS_20_NAMESPACE_URI,
1871
0
                    BAD_CAST MS_OWSCOMMON_WCS_NAMESPACE_PREFIX));
1872
1873
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_20_NAMESPACE_URI,
1874
0
           BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
1875
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_URI,
1876
0
           BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_PREFIX);
1877
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI,
1878
0
           BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
1879
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_URI,
1880
0
           BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_PREFIX);
1881
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_WCS_20_NAMESPACE_URI,
1882
0
           BAD_CAST MS_OWSCOMMON_WCS_NAMESPACE_PREFIX);
1883
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_GML_32_NAMESPACE_URI,
1884
0
           BAD_CAST MS_OWSCOMMON_GML_NAMESPACE_PREFIX);
1885
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_GMLCOV_10_NAMESPACE_URI,
1886
0
           BAD_CAST MS_OWSCOMMON_GMLCOV_NAMESPACE_PREFIX);
1887
0
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_SWE_20_NAMESPACE_URI,
1888
0
           BAD_CAST MS_OWSCOMMON_SWE_NAMESPACE_PREFIX);
1889
1890
0
  if (addInspire) {
1891
0
    xmlNewNs(psRootNode, BAD_CAST MS_INSPIRE_COMMON_NAMESPACE_URI,
1892
0
             BAD_CAST MS_INSPIRE_COMMON_NAMESPACE_PREFIX);
1893
0
    xmlNewNs(psRootNode, BAD_CAST MS_INSPIRE_DLS_NAMESPACE_URI,
1894
0
             BAD_CAST MS_INSPIRE_DLS_NAMESPACE_PREFIX);
1895
0
  }
1896
1897
0
  psXsiNs = xmlSearchNs(pDoc, psRootNode,
1898
0
                        BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
1899
1900
0
  schemaLocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
1901
0
  xsi_schemaLocation = msStrdup(MS_OWSCOMMON_WCS_20_NAMESPACE_URI);
1902
0
  xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
1903
0
  xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, schemaLocation);
1904
0
  xsi_schemaLocation = msStringConcatenate(
1905
0
      xsi_schemaLocation, MS_OWSCOMMON_WCS_20_SCHEMAS_LOCATION);
1906
0
  xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
1907
1908
0
  if (addInspire) {
1909
0
    xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation,
1910
0
                                             MS_INSPIRE_DLS_NAMESPACE_URI " ");
1911
0
    xsi_schemaLocation = msStringConcatenate(
1912
0
        xsi_schemaLocation, msOWSGetInspireSchemasLocation(map));
1913
0
    xsi_schemaLocation =
1914
0
        msStringConcatenate(xsi_schemaLocation, MS_INSPIRE_DLS_SCHEMA_LOCATION);
1915
0
  }
1916
1917
0
  xmlNewNsProp(psRootNode, psXsiNs, BAD_CAST "schemaLocation",
1918
0
               BAD_CAST xsi_schemaLocation);
1919
1920
0
  msFree(schemaLocation);
1921
0
  msFree(xsi_schemaLocation);
1922
0
}
1923
1924
/************************************************************************/
1925
/*                   msWCSGetFormatList20()                             */
1926
/*                                                                      */
1927
/*      Copied from mapwcs.c.                                           */
1928
/************************************************************************/
1929
1930
0
static char *msWCSGetFormatsList20(mapObj *map, layerObj *layer) {
1931
0
  char *format_list = msStrdup("");
1932
0
  char **tokens = NULL, **formats = NULL;
1933
0
  int i, numtokens = 0, numformats;
1934
0
  char *value;
1935
1936
  /* -------------------------------------------------------------------- */
1937
  /*      Parse from layer metadata.                                      */
1938
  /* -------------------------------------------------------------------- */
1939
0
  if (layer != NULL &&
1940
0
      (value = msOWSGetEncodeMetadata(&(layer->metadata), "CO", "formats",
1941
0
                                      NULL)) != NULL) {
1942
0
    tokens = msStringSplit(value, ' ', &numtokens);
1943
0
    msFree(value);
1944
0
  }
1945
1946
  /* -------------------------------------------------------------------- */
1947
  /*      Parse from map.web metadata.                                    */
1948
  /* -------------------------------------------------------------------- */
1949
0
  else if ((value = msOWSGetEncodeMetadata(&(map->web.metadata), "CO",
1950
0
                                           "formats", NULL)) != NULL) {
1951
0
    tokens = msStringSplit(value, ' ', &numtokens);
1952
0
    msFree(value);
1953
0
  }
1954
1955
  /* -------------------------------------------------------------------- */
1956
  /*      Or generate from all configured raster output formats that      */
1957
  /*      look plausible.                                                 */
1958
  /* -------------------------------------------------------------------- */
1959
0
  else {
1960
0
    tokens = (char **)msSmallCalloc(map->numoutputformats, sizeof(char *));
1961
0
    for (i = 0; i < map->numoutputformats; i++) {
1962
0
      switch (map->outputformatlist[i]->renderer) {
1963
        /* seemingly normal raster format */
1964
0
      case MS_RENDER_WITH_AGG:
1965
0
      case MS_RENDER_WITH_RAWDATA:
1966
0
        tokens[numtokens++] = msStrdup(map->outputformatlist[i]->name);
1967
0
        break;
1968
        /* rest of formats aren't really WCS compatible */
1969
0
      default:
1970
0
        break;
1971
0
      }
1972
0
    }
1973
0
  }
1974
1975
  /* -------------------------------------------------------------------- */
1976
  /*      Convert outputFormatObj names into mime types and remove        */
1977
  /*      duplicates.                                                     */
1978
  /* -------------------------------------------------------------------- */
1979
0
  numformats = 0;
1980
0
  formats = (char **)msSmallCalloc(sizeof(char *), numtokens);
1981
1982
0
  for (i = 0; i < numtokens; i++) {
1983
0
    int format_i, j;
1984
0
    const char *mimetype;
1985
1986
0
    for (format_i = 0; format_i < map->numoutputformats; format_i++) {
1987
0
      if (EQUAL(map->outputformatlist[format_i]->name, tokens[i]))
1988
0
        break;
1989
0
    }
1990
1991
0
    if (format_i == map->numoutputformats) {
1992
0
      msDebug("Failed to find outputformat info on format '%s', ignore.\n",
1993
0
              tokens[i]);
1994
0
      continue;
1995
0
    }
1996
1997
0
    mimetype = map->outputformatlist[format_i]->mimetype;
1998
0
    if (mimetype == NULL || strlen(mimetype) == 0) {
1999
0
      msDebug("No mimetime for format '%s', ignoring.\n", tokens[i]);
2000
0
      continue;
2001
0
    }
2002
2003
0
    for (j = 0; j < numformats; j++) {
2004
0
      if (EQUAL(mimetype, formats[j]))
2005
0
        break;
2006
0
    }
2007
2008
0
    if (j < numformats) {
2009
0
      msDebug("Format '%s' ignored since mimetype '%s' duplicates another "
2010
0
              "outputFormatObj.\n",
2011
0
              tokens[i], mimetype);
2012
0
      continue;
2013
0
    }
2014
2015
0
    formats[numformats++] = msStrdup(mimetype);
2016
0
  }
2017
2018
0
  msFreeCharArray(tokens, numtokens);
2019
2020
  /* -------------------------------------------------------------------- */
2021
  /*      Turn mimetype list into comma delimited form for easy use       */
2022
  /*      with xml functions.                                             */
2023
  /* -------------------------------------------------------------------- */
2024
0
  for (i = 0; i < numformats; i++) {
2025
0
    if (i > 0) {
2026
0
      format_list = msStringConcatenate(format_list, (char *)",");
2027
0
    }
2028
0
    format_list = msStringConcatenate(format_list, formats[i]);
2029
0
  }
2030
0
  msFreeCharArray(formats, numformats);
2031
2032
0
  return format_list;
2033
0
}
2034
2035
/************************************************************************/
2036
/*                   msWCSSwapAxes20                                    */
2037
/*                                                                      */
2038
/*      Helper function to determine if a SRS mandates swapped axes.    */
2039
/************************************************************************/
2040
2041
0
static int msWCSSwapAxes20(char *srs_uri) {
2042
0
  int srid = 0;
2043
2044
  /* get SRID from the srs uri */
2045
0
  if (srs_uri != NULL && strlen(srs_uri) > 0) {
2046
0
    if (sscanf(srs_uri, "http://www.opengis.net/def/crs/EPSG/0/%d", &srid) !=
2047
0
        EOF)
2048
0
      ;
2049
0
    else if (sscanf(srs_uri, "http://www.opengis.net/def/crs/%d", &srid) != EOF)
2050
0
      ;
2051
0
    else
2052
0
      srid = 0;
2053
0
  }
2054
0
  if (srid == 0)
2055
0
    return MS_FALSE;
2056
2057
0
  return msIsAxisInverted(srid);
2058
0
}
2059
2060
/************************************************************************/
2061
/*                   msWCSCommon20_CreateBoundedBy()                    */
2062
/*                                                                      */
2063
/*      Inserts the BoundedBy section into an existing DOM structure.   */
2064
/************************************************************************/
2065
2066
static void msWCSCommon20_CreateBoundedBy(wcs20coverageMetadataObjPtr cm,
2067
                                          xmlNsPtr psGmlNs, xmlNodePtr psRoot,
2068
                                          projectionObj *projection,
2069
0
                                          int swapAxes) {
2070
0
  xmlNodePtr psBoundedBy, psEnvelope;
2071
0
  char lowerCorner[100], upperCorner[100], axisLabels[100], uomLabels[100];
2072
2073
0
  psBoundedBy = xmlNewChild(psRoot, psGmlNs, BAD_CAST "boundedBy", NULL);
2074
0
  {
2075
0
    psEnvelope = xmlNewChild(psBoundedBy, psGmlNs, BAD_CAST "Envelope", NULL);
2076
0
    {
2077
0
      xmlNewProp(psEnvelope, BAD_CAST "srsName", BAD_CAST cm->srs_uri);
2078
2079
0
      if (projection->proj != NULL && msProjIsGeographicCRS(projection)) {
2080
0
        if (swapAxes == MS_FALSE) {
2081
0
          strlcpy(axisLabels, "long lat", sizeof(axisLabels));
2082
0
        } else {
2083
0
          strlcpy(axisLabels, "lat long", sizeof(axisLabels));
2084
0
        }
2085
0
        strlcpy(uomLabels, "deg deg", sizeof(uomLabels));
2086
0
      } else {
2087
0
        if (swapAxes == MS_FALSE) {
2088
0
          strlcpy(axisLabels, "x y", sizeof(axisLabels));
2089
0
        } else {
2090
0
          strlcpy(axisLabels, "y x", sizeof(axisLabels));
2091
0
        }
2092
0
        strlcpy(uomLabels, "m m", sizeof(uomLabels));
2093
0
      }
2094
0
      xmlNewProp(psEnvelope, BAD_CAST "axisLabels", BAD_CAST axisLabels);
2095
0
      xmlNewProp(psEnvelope, BAD_CAST "uomLabels", BAD_CAST uomLabels);
2096
0
      xmlNewProp(psEnvelope, BAD_CAST "srsDimension", BAD_CAST "2");
2097
2098
0
      if (swapAxes == MS_FALSE) {
2099
0
        snprintf(lowerCorner, sizeof(lowerCorner), "%.15g %.15g",
2100
0
                 cm->extent.minx, cm->extent.miny);
2101
0
        snprintf(upperCorner, sizeof(upperCorner), "%.15g %.15g",
2102
0
                 cm->extent.maxx, cm->extent.maxy);
2103
0
      } else {
2104
0
        snprintf(lowerCorner, sizeof(lowerCorner), "%.15g %.15g",
2105
0
                 cm->extent.miny, cm->extent.minx);
2106
0
        snprintf(upperCorner, sizeof(upperCorner), "%.15g %.15g",
2107
0
                 cm->extent.maxy, cm->extent.maxx);
2108
0
      }
2109
2110
0
      xmlNewChild(psEnvelope, psGmlNs, BAD_CAST "lowerCorner",
2111
0
                  BAD_CAST lowerCorner);
2112
0
      xmlNewChild(psEnvelope, psGmlNs, BAD_CAST "upperCorner",
2113
0
                  BAD_CAST upperCorner);
2114
0
    }
2115
0
  }
2116
0
}
2117
2118
/************************************************************************/
2119
/*                   msWCSCommon20_CreateDomainSet()                    */
2120
/*                                                                      */
2121
/*      Inserts the DomainSet section into an existing DOM structure.   */
2122
/************************************************************************/
2123
2124
static void msWCSCommon20_CreateDomainSet(layerObj *layer,
2125
                                          wcs20coverageMetadataObjPtr cm,
2126
                                          xmlNsPtr psGmlNs, xmlNodePtr psRoot,
2127
                                          projectionObj *projection,
2128
0
                                          int swapAxes) {
2129
0
  xmlNodePtr psDomainSet, psGrid, psLimits, psGridEnvelope, psOrigin, psOffsetX,
2130
0
      psOffsetY;
2131
0
  char low[100], high[100], id[100], point[100];
2132
0
  char offsetVector1[100], offsetVector2[100], axisLabels[100];
2133
2134
0
  psDomainSet = xmlNewChild(psRoot, psGmlNs, BAD_CAST "domainSet", NULL);
2135
0
  {
2136
0
    psGrid = xmlNewChild(psDomainSet, psGmlNs, BAD_CAST "RectifiedGrid", NULL);
2137
0
    {
2138
0
      double x0 = cm->geotransform[0] + cm->geotransform[1] / 2 +
2139
0
                  cm->geotransform[2] / 2;
2140
0
      double y0 = cm->geotransform[3] + cm->geotransform[4] / 2 +
2141
0
                  cm->geotransform[5] / 2;
2142
0
      double resx = cm->geotransform[1];
2143
0
      double resy = cm->geotransform[5];
2144
2145
0
      xmlNewProp(psGrid, BAD_CAST "dimension", BAD_CAST "2");
2146
0
      snprintf(id, sizeof(id), "grid_%s", layer->name);
2147
0
      xmlNewNsProp(psGrid, psGmlNs, BAD_CAST "id", BAD_CAST id);
2148
2149
0
      psLimits = xmlNewChild(psGrid, psGmlNs, BAD_CAST "limits", NULL);
2150
0
      {
2151
0
        psGridEnvelope =
2152
0
            xmlNewChild(psLimits, psGmlNs, BAD_CAST "GridEnvelope", NULL);
2153
0
        {
2154
0
          strlcpy(low, "0 0", sizeof(low));
2155
0
          snprintf(high, sizeof(high), "%d %d", cm->xsize - 1, cm->ysize - 1);
2156
2157
0
          xmlNewChild(psGridEnvelope, psGmlNs, BAD_CAST "low", BAD_CAST low);
2158
0
          xmlNewChild(psGridEnvelope, psGmlNs, BAD_CAST "high", BAD_CAST high);
2159
0
        }
2160
0
      }
2161
2162
0
      if (projection->proj != NULL && msProjIsGeographicCRS(projection)) {
2163
0
        strlcpy(axisLabels, "long lat", sizeof(axisLabels));
2164
0
      } else {
2165
0
        strlcpy(axisLabels, "x y", sizeof(axisLabels));
2166
0
      }
2167
2168
0
      xmlNewChild(psGrid, psGmlNs, BAD_CAST "axisLabels", BAD_CAST axisLabels);
2169
2170
0
      psOrigin = xmlNewChild(psGrid, psGmlNs, BAD_CAST "origin", NULL);
2171
0
      {
2172
0
        if (swapAxes == MS_FALSE) {
2173
0
          snprintf(point, sizeof(point), "%f %f", x0, y0);
2174
0
        } else {
2175
0
          snprintf(point, sizeof(point), "%f %f", y0, x0);
2176
0
        }
2177
0
        psOrigin = xmlNewChild(psOrigin, psGmlNs, BAD_CAST "Point", NULL);
2178
0
        snprintf(id, sizeof(id), "grid_origin_%s", layer->name);
2179
0
        xmlNewNsProp(psOrigin, psGmlNs, BAD_CAST "id", BAD_CAST id);
2180
0
        xmlNewProp(psOrigin, BAD_CAST "srsName", BAD_CAST cm->srs_uri);
2181
2182
0
        xmlNewChild(psOrigin, psGmlNs, BAD_CAST "pos", BAD_CAST point);
2183
0
      }
2184
2185
0
      if (swapAxes == MS_FALSE) {
2186
0
        snprintf(offsetVector1, sizeof(offsetVector1), "%f 0", resx);
2187
0
        snprintf(offsetVector2, sizeof(offsetVector2), "0 %f", resy);
2188
0
      } else {
2189
0
        snprintf(offsetVector1, sizeof(offsetVector1), "0 %f", resx);
2190
0
        snprintf(offsetVector2, sizeof(offsetVector2), "%f 0", resy);
2191
0
      }
2192
0
      psOffsetX = xmlNewChild(psGrid, psGmlNs, BAD_CAST "offsetVector",
2193
0
                              BAD_CAST offsetVector1);
2194
0
      psOffsetY = xmlNewChild(psGrid, psGmlNs, BAD_CAST "offsetVector",
2195
0
                              BAD_CAST offsetVector2);
2196
2197
0
      xmlNewProp(psOffsetX, BAD_CAST "srsName", BAD_CAST cm->srs_uri);
2198
0
      xmlNewProp(psOffsetY, BAD_CAST "srsName", BAD_CAST cm->srs_uri);
2199
0
    }
2200
0
  }
2201
0
}
2202
2203
/************************************************************************/
2204
/*                   msWCSCommon20_CreateRangeType()                    */
2205
/*                                                                      */
2206
/*      Inserts the RangeType section into an existing DOM structure.   */
2207
/************************************************************************/
2208
2209
static void msWCSCommon20_CreateRangeType(wcs20coverageMetadataObjPtr cm,
2210
                                          char *bands, xmlNsPtr psGmlcovNs,
2211
0
                                          xmlNsPtr psSweNs, xmlNodePtr psRoot) {
2212
0
  xmlNodePtr psRangeType, psDataRecord, psField, psQuantity, psUom,
2213
0
      psConstraint, psAllowedValues = NULL, psNilValues = NULL;
2214
0
  char **arr = NULL;
2215
0
  int num = 0;
2216
2217
0
  if (NULL != bands) {
2218
0
    arr = msStringSplit(bands, ',', &num);
2219
0
  }
2220
2221
0
  psRangeType = xmlNewChild(psRoot, psGmlcovNs, BAD_CAST "rangeType", NULL);
2222
0
  psDataRecord = xmlNewChild(psRangeType, psSweNs, BAD_CAST "DataRecord", NULL);
2223
2224
  /* iterate over every band */
2225
0
  for (unsigned i = 0; i < cm->numbands; ++i) {
2226
    /* only add bands that are in the range subset */
2227
0
    if (NULL != arr && num > 0) {
2228
0
      int found = MS_FALSE, j;
2229
0
      for (j = 0; j < num; ++j) {
2230
0
        int repr = 0;
2231
0
        if (msStringParseInteger(arr[j], &repr) == MS_SUCCESS &&
2232
0
            static_cast<unsigned>(repr) == i + 1) {
2233
0
          found = MS_TRUE;
2234
0
          break;
2235
0
        }
2236
0
      }
2237
0
      if (found == MS_FALSE) {
2238
        /* ignore this band since it is not in the range subset */
2239
0
        continue;
2240
0
      }
2241
0
    }
2242
2243
    /* add field tag */
2244
0
    psField = xmlNewChild(psDataRecord, psSweNs, BAD_CAST "field", NULL);
2245
2246
0
    if (cm->bands[i].name != NULL) {
2247
0
      xmlNewProp(psField, BAD_CAST "name", BAD_CAST cm->bands[i].name);
2248
0
    } else {
2249
0
      xmlNewProp(psField, BAD_CAST "name", BAD_CAST "band");
2250
0
    }
2251
    /* add Quantity tag */
2252
0
    psQuantity = xmlNewChild(psField, psSweNs, BAD_CAST "Quantity", NULL);
2253
0
    if (cm->bands[i].definition != NULL) {
2254
0
      xmlNewProp(psQuantity, BAD_CAST "definition",
2255
0
                 BAD_CAST cm->bands[i].definition);
2256
0
    }
2257
0
    if (cm->bands[i].description != NULL) {
2258
0
      xmlNewChild(psQuantity, psSweNs, BAD_CAST "description",
2259
0
                  BAD_CAST cm->bands[i].description);
2260
0
    }
2261
2262
    /* if there are given nilvalues -> add them to the first field */
2263
    /* all other fields get a reference to these */
2264
0
    if (cm->numnilvalues > 0) {
2265
0
      psNilValues = xmlNewChild(
2266
0
          xmlNewChild(psQuantity, psSweNs, BAD_CAST "nilValues", NULL), psSweNs,
2267
0
          BAD_CAST "NilValues", NULL);
2268
0
      for (unsigned j = 0; j < cm->numnilvalues; ++j) {
2269
0
        xmlNodePtr psTemp =
2270
0
            xmlNewChild(psNilValues, psSweNs, BAD_CAST "nilValue",
2271
0
                        BAD_CAST cm->nilvalues[j]);
2272
0
        if (j < cm->numnilvalues)
2273
0
          xmlNewProp(psTemp, BAD_CAST "reason",
2274
0
                     BAD_CAST cm->nilvalues_reasons[j]);
2275
0
      }
2276
0
    } else { /* create an empty nilValues tag */
2277
0
      xmlNewChild(psQuantity, psSweNs, BAD_CAST "nilValues", NULL);
2278
0
    }
2279
2280
0
    psUom = xmlNewChild(psQuantity, psSweNs, BAD_CAST "uom", NULL);
2281
0
    if (cm->bands[i].uom != NULL) {
2282
0
      xmlNewProp(psUom, BAD_CAST "code", BAD_CAST cm->bands[i].uom);
2283
0
    } else {
2284
0
      xmlNewProp(psUom, BAD_CAST "code", BAD_CAST "W.m-2.Sr-1");
2285
0
    }
2286
2287
    /* add constraint */
2288
0
    psConstraint =
2289
0
        xmlNewChild(psQuantity, psSweNs, BAD_CAST "constraint", NULL);
2290
2291
0
    {
2292
0
      char interval[100], significant_figures[100];
2293
0
      psAllowedValues =
2294
0
          xmlNewChild(psConstraint, psSweNs, BAD_CAST "AllowedValues", NULL);
2295
2296
      /* Interval */
2297
0
      snprintf(interval, sizeof(interval), "%.5g %.5g",
2298
0
               cm->bands[i].interval_min, cm->bands[i].interval_max);
2299
0
      xmlNewChild(psAllowedValues, psSweNs, BAD_CAST "interval",
2300
0
                  BAD_CAST interval);
2301
2302
      /* Significant figures */
2303
0
      snprintf(significant_figures, sizeof(significant_figures), "%d",
2304
0
               cm->bands[i].significant_figures);
2305
0
      xmlNewChild(psAllowedValues, psSweNs, BAD_CAST "significantFigures",
2306
0
                  BAD_CAST significant_figures);
2307
0
    }
2308
0
  }
2309
0
  msFreeCharArray(arr, num);
2310
0
}
2311
2312
/************************************************************************/
2313
/*                   msWCSWriteDocument20()                             */
2314
/*                                                                      */
2315
/*      Writes out an xml structure to the stream.                      */
2316
/************************************************************************/
2317
2318
0
static int msWCSWriteDocument20(xmlDocPtr psDoc) {
2319
0
  xmlChar *buffer = NULL;
2320
0
  int size = 0;
2321
0
  msIOContext *context = NULL;
2322
0
  char *contenttype = NULL;
2323
2324
0
  if (msIO_needBinaryStdout() == MS_FAILURE) {
2325
0
    return MS_FAILURE;
2326
0
  }
2327
2328
0
  if (EQUAL((char *)xmlDocGetRootElement(psDoc)->name, "RectifiedGridCoverage"))
2329
0
    contenttype = msStrdup("application/gml+xml");
2330
0
  else
2331
0
    contenttype = msStrdup("text/xml");
2332
2333
0
  msIO_setHeader("Content-Type", "%s; charset=UTF-8", contenttype);
2334
0
  msIO_sendHeaders();
2335
0
  msFree(contenttype);
2336
2337
0
  context = msIO_getHandler(stdout);
2338
2339
0
  xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, "UTF-8", 1);
2340
0
  msIO_contextWrite(context, buffer, size);
2341
0
  xmlFree(buffer);
2342
2343
0
  return MS_SUCCESS;
2344
0
}
2345
2346
/************************************************************************/
2347
/*                   msWCSWriteFile20()                                 */
2348
/*                                                                      */
2349
/*      Writes an image object to the stream. If multipart is set,      */
2350
/*      then content sections are inserted.                             */
2351
/************************************************************************/
2352
2353
static int msWCSWriteFile20(mapObj *map, imageObj *image,
2354
0
                            wcs20ParamsObjPtr params, int multipart) {
2355
0
  int status;
2356
0
  char *filename = NULL;
2357
0
  char *base_dir = NULL;
2358
0
  const char *fo_filename;
2359
0
  int i;
2360
2361
0
  fo_filename = msGetOutputFormatOption(image->format, "FILENAME", NULL);
2362
2363
  /* -------------------------------------------------------------------- */
2364
  /*      Fetch the driver we will be using and check if it supports      */
2365
  /*      VSIL IO.                                                        */
2366
  /* -------------------------------------------------------------------- */
2367
0
  if (EQUALN(image->format->driver, "GDAL/", 5)) {
2368
0
    GDALDriverH hDriver;
2369
0
    const char *pszExtension = image->format->extension;
2370
2371
0
    msAcquireLock(TLOCK_GDAL);
2372
0
    hDriver = GDALGetDriverByName(image->format->driver + 5);
2373
0
    if (hDriver == NULL) {
2374
0
      msReleaseLock(TLOCK_GDAL);
2375
0
      msSetError(MS_MISCERR, "Failed to find %s driver.", "msWCSWriteFile20()",
2376
0
                 image->format->driver + 5);
2377
0
      return msWCSException(map, "NoApplicableCode", "mapserv",
2378
0
                            params->version);
2379
0
    }
2380
2381
0
    if (pszExtension == NULL)
2382
0
      pszExtension = "img.tmp";
2383
2384
0
    if (msGDALDriverSupportsVirtualIOOutput(hDriver)) {
2385
0
      base_dir = msTmpFile(map, map->mappath, "/vsimem/wcsout", NULL);
2386
0
      if (fo_filename)
2387
0
        filename = msStrdup(CPLFormFilename(base_dir, fo_filename, NULL));
2388
0
      else
2389
0
        filename = msStrdup(CPLFormFilename(base_dir, "out", pszExtension));
2390
2391
      /*            CleanVSIDir( "/vsimem/wcsout" ); */
2392
2393
0
      msReleaseLock(TLOCK_GDAL);
2394
0
      status = msSaveImage(map, image, filename);
2395
0
      if (status != MS_SUCCESS) {
2396
0
        msSetError(MS_MISCERR, "msSaveImage() failed", "msWCSWriteFile20()");
2397
0
        return msWCSException20(map, "NoApplicableCode", "mapserv",
2398
0
                                params->version);
2399
0
      }
2400
0
    }
2401
0
    msReleaseLock(TLOCK_GDAL);
2402
0
  }
2403
2404
  /* -------------------------------------------------------------------- */
2405
  /*      If we weren't able to write data under /vsimem, then we just    */
2406
  /*      output a single "stock" filename.                               */
2407
  /* -------------------------------------------------------------------- */
2408
0
  if (filename == NULL) {
2409
0
    msOutputFormatResolveFromImage(map, image);
2410
0
    if (multipart) {
2411
0
      msIO_fprintf(stdout, "\r\n--wcs\r\n");
2412
0
      msIO_fprintf(stdout,
2413
0
                   "Content-Type: %s\r\n"
2414
0
                   "Content-Description: coverage data\r\n"
2415
0
                   "Content-Transfer-Encoding: binary\r\n",
2416
0
                   MS_IMAGE_MIME_TYPE(map->outputformat));
2417
2418
0
      if (fo_filename != NULL)
2419
0
        msIO_fprintf(stdout,
2420
0
                     "Content-ID: coverage/%s\r\n"
2421
0
                     "Content-Disposition: INLINE; filename=%s\r\n\r\n",
2422
0
                     fo_filename, fo_filename);
2423
0
      else
2424
0
        msIO_fprintf(stdout,
2425
0
                     "Content-ID: coverage/wcs.%s\r\n"
2426
0
                     "Content-Disposition: INLINE\r\n\r\n",
2427
0
                     MS_IMAGE_EXTENSION(map->outputformat));
2428
0
    } else {
2429
0
      msIO_setHeader("Content-Type", "%s",
2430
0
                     MS_IMAGE_MIME_TYPE(map->outputformat));
2431
0
      msIO_setHeader("Content-Description", "coverage data");
2432
0
      msIO_setHeader("Content-Transfer-Encoding", "binary");
2433
2434
0
      if (fo_filename != NULL) {
2435
0
        msIO_setHeader("Content-ID", "coverage/%s", fo_filename);
2436
0
        msIO_setHeader("Content-Disposition", "INLINE; filename=%s",
2437
0
                       fo_filename);
2438
0
      } else {
2439
0
        msIO_setHeader("Content-ID", "coverage/wcs.%s",
2440
0
                       MS_IMAGE_EXTENSION(map->outputformat));
2441
0
        msIO_setHeader("Content-Disposition", "INLINE");
2442
0
      }
2443
0
      msIO_sendHeaders();
2444
0
    }
2445
2446
0
    status = msSaveImage(map, image, NULL);
2447
0
    if (status != MS_SUCCESS) {
2448
0
      msSetError(MS_MISCERR, "msSaveImage() failed", "msWCSWriteFile20()");
2449
0
      return msWCSException(map, "NoApplicableCode", "mapserv",
2450
0
                            params->version);
2451
0
    }
2452
0
    if (multipart)
2453
0
      msIO_fprintf(stdout, "\r\n--wcs--\r\n");
2454
0
    return MS_SUCCESS;
2455
0
  }
2456
2457
  /* -------------------------------------------------------------------- */
2458
  /*      When potentially listing multiple files, we take great care     */
2459
  /*      to identify the "primary" file and list it first.  In fact      */
2460
  /*      it is the only file listed in the coverages document.           */
2461
  /* -------------------------------------------------------------------- */
2462
0
  {
2463
0
    char **all_files = CPLReadDir(base_dir);
2464
0
    int count = CSLCount(all_files);
2465
2466
0
    if (msIO_needBinaryStdout() == MS_FAILURE)
2467
0
      return MS_FAILURE;
2468
2469
0
    msAcquireLock(TLOCK_GDAL);
2470
0
    for (i = count - 1; i >= 0; i--) {
2471
0
      const char *this_file = all_files[i];
2472
2473
0
      if (EQUAL(this_file, ".") || EQUAL(this_file, "..")) {
2474
0
        all_files = CSLRemoveStrings(all_files, i, 1, NULL);
2475
0
        continue;
2476
0
      }
2477
2478
0
      if (i > 0 && EQUAL(this_file, CPLGetFilename(filename))) {
2479
0
        all_files = CSLRemoveStrings(all_files, i, 1, NULL);
2480
0
        all_files = CSLInsertString(all_files, 0, CPLGetFilename(filename));
2481
0
        i++;
2482
0
      }
2483
0
    }
2484
2485
    /* -------------------------------------------------------------------- */
2486
    /*      Dump all the files in the memory directory as mime sections.    */
2487
    /* -------------------------------------------------------------------- */
2488
0
    count = CSLCount(all_files);
2489
2490
0
    if (count > 1 && multipart == MS_FALSE) {
2491
0
      msDebug("msWCSWriteFile20(): force multipart output without gml summary "
2492
0
              "because we have multiple files in the result.\n");
2493
2494
0
      multipart = MS_TRUE;
2495
0
      msIO_setHeader("Content-Type", "multipart/related; boundary=wcs");
2496
0
      msIO_sendHeaders();
2497
0
    }
2498
2499
0
    for (i = 0; i < count; i++) {
2500
0
      const char *mimetype = NULL;
2501
0
      VSILFILE *fp;
2502
0
      unsigned char block[4000];
2503
0
      int bytes_read;
2504
2505
0
      if (i == 0 && !EQUAL(MS_IMAGE_MIME_TYPE(map->outputformat), "unknown"))
2506
0
        mimetype = MS_IMAGE_MIME_TYPE(map->outputformat);
2507
2508
0
      if (mimetype == NULL)
2509
0
        mimetype = "application/octet-stream";
2510
0
      if (multipart) {
2511
0
        msIO_fprintf(stdout, "\r\n--wcs\r\n");
2512
0
        msIO_fprintf(stdout,
2513
0
                     "Content-Type: %s\r\n"
2514
0
                     "Content-Description: coverage data\r\n"
2515
0
                     "Content-Transfer-Encoding: binary\r\n"
2516
0
                     "Content-ID: coverage/%s\r\n"
2517
0
                     "Content-Disposition: INLINE; filename=%s\r\n\r\n",
2518
0
                     mimetype, all_files[i], all_files[i]);
2519
0
      } else {
2520
0
        msIO_setHeader("Content-Type", "%s", mimetype);
2521
0
        msIO_setHeader("Content-Description", "coverage data");
2522
0
        msIO_setHeader("Content-Transfer-Encoding", "binary");
2523
0
        msIO_setHeader("Content-ID", "coverage/%s", all_files[i]);
2524
0
        msIO_setHeader("Content-Disposition", "INLINE; filename=%s",
2525
0
                       all_files[i]);
2526
0
        msIO_sendHeaders();
2527
0
      }
2528
2529
0
      fp = VSIFOpenL(CPLFormFilename(base_dir, all_files[i], NULL), "rb");
2530
0
      if (fp == NULL) {
2531
0
        msReleaseLock(TLOCK_GDAL);
2532
0
        msSetError(MS_MISCERR, "Failed to open %s for streaming to stdout.",
2533
0
                   "msWCSWriteFile20()", all_files[i]);
2534
0
        return MS_FAILURE;
2535
0
      }
2536
2537
0
      while ((bytes_read = VSIFReadL(block, 1, sizeof(block), fp)) > 0)
2538
0
        msIO_fwrite(block, 1, bytes_read, stdout);
2539
2540
0
      VSIFCloseL(fp);
2541
2542
0
      VSIUnlink(CPLFormFilename(base_dir, all_files[i], NULL));
2543
0
    }
2544
2545
0
    msFree(base_dir);
2546
0
    msFree(filename);
2547
0
    CSLDestroy(all_files);
2548
0
    msReleaseLock(TLOCK_GDAL);
2549
0
    if (multipart)
2550
0
      msIO_fprintf(stdout, "\r\n--wcs--\r\n");
2551
0
    return MS_SUCCESS;
2552
0
  }
2553
2554
0
  return MS_SUCCESS;
2555
0
}
2556
2557
/************************************************************************/
2558
/*                   msWCSGetRangesetAxisMetadata20()                   */
2559
/*                                                                      */
2560
/*      Looks up a layers metadata for a specific axis information.     */
2561
/************************************************************************/
2562
2563
static const char *msWCSLookupRangesetAxisMetadata20(hashTableObj *table,
2564
                                                     const char *axis,
2565
0
                                                     const char *item) {
2566
0
  char buf[500];
2567
0
  const char *value;
2568
2569
0
  if (table == NULL || axis == NULL || item == NULL) {
2570
0
    return NULL;
2571
0
  }
2572
2573
0
  strlcpy(buf, axis, sizeof(buf));
2574
0
  strlcat(buf, "_", sizeof(buf));
2575
0
  strlcat(buf, item, sizeof(buf));
2576
0
  if ((value = msLookupHashTable(table, buf)) != NULL) {
2577
0
    return value;
2578
0
  }
2579
0
  return msOWSLookupMetadata(table, "CO", buf);
2580
0
}
2581
2582
/************************************************************************/
2583
/*                   msWCSGetCoverageMetadata20()                       */
2584
/*                                                                      */
2585
/*      Inits a coverageMetadataObj. Uses msWCSGetCoverageMetadata()    */
2586
/*      but exchanges the SRS URN by an URI for compliance with 2.0.    */
2587
/************************************************************************/
2588
2589
static int msWCSGetCoverageMetadata20(layerObj *layer,
2590
0
                                      wcs20coverageMetadataObj *cm) {
2591
0
  char *srs_uri = NULL;
2592
0
  memset(cm, 0, sizeof(wcs20coverageMetadataObj));
2593
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
2594
0
    return MS_FAILURE;
2595
2596
0
  msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE,
2597
0
                   &(cm->srs_epsg));
2598
0
  if (!cm->srs_epsg) {
2599
0
    msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata),
2600
0
                     "CO", MS_TRUE, &cm->srs_epsg);
2601
0
    if (!cm->srs_epsg) {
2602
0
      msSetError(MS_WCSERR,
2603
0
                 "Unable to determine the SRS for this layer, "
2604
0
                 "no projection defined and no metadata available.",
2605
0
                 "msWCSGetCoverageMetadata20()");
2606
0
      return MS_FAILURE;
2607
0
    }
2608
0
  }
2609
2610
  /* -------------------------------------------------------------------- */
2611
  /*      Get the SRS in uri format.                                      */
2612
  /* -------------------------------------------------------------------- */
2613
0
  if ((srs_uri = msOWSGetProjURI(&(layer->projection), &(layer->metadata), "CO",
2614
0
                                 MS_TRUE)) == NULL) {
2615
0
    srs_uri = msOWSGetProjURI(&(layer->map->projection),
2616
0
                              &(layer->map->web.metadata), "CO", MS_TRUE);
2617
0
  }
2618
2619
0
  if (srs_uri != NULL) {
2620
0
    if (strlen(srs_uri) > sizeof(cm->srs_uri) - 1) {
2621
0
      msSetError(MS_WCSERR, "SRS URI too long!", "msWCSGetCoverageMetadata()");
2622
0
      return MS_FAILURE;
2623
0
    }
2624
2625
0
    strlcpy(cm->srs_uri, srs_uri, sizeof(cm->srs_uri));
2626
0
    msFree(srs_uri);
2627
0
  } else {
2628
0
    cm->srs_uri[0] = '\0';
2629
0
  }
2630
2631
  /* setup nilvalues */
2632
0
  cm->numnilvalues = 0;
2633
0
  cm->nilvalues = NULL;
2634
0
  cm->nilvalues_reasons = NULL;
2635
0
  cm->native_format = NULL;
2636
2637
  /* -------------------------------------------------------------------- */
2638
  /*      If we have "virtual dataset" metadata on the layer, then use    */
2639
  /*      that in preference to inspecting the file(s).                   */
2640
  /*      We require extent and either size or resolution.                */
2641
  /* -------------------------------------------------------------------- */
2642
0
  if (msOWSLookupMetadata(&(layer->metadata), "CO", "extent") != NULL &&
2643
0
      (msOWSLookupMetadata(&(layer->metadata), "CO", "resolution") != NULL ||
2644
0
       msOWSLookupMetadata(&(layer->metadata), "CO", "size") != NULL)) {
2645
0
    const char *value;
2646
2647
    /* get extent */
2648
0
    cm->extent.minx = 0.0;
2649
0
    cm->extent.maxx = 0.0;
2650
0
    cm->extent.miny = 0.0;
2651
0
    cm->extent.maxy = 0.0;
2652
0
    if (msOWSGetLayerExtent(layer->map, layer, "CO", &cm->extent) == MS_FAILURE)
2653
0
      return MS_FAILURE;
2654
2655
    /* get resolution */
2656
0
    cm->xresolution = 0.0;
2657
0
    cm->yresolution = 0.0;
2658
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "resolution")) !=
2659
0
        NULL) {
2660
0
      char **tokens;
2661
0
      int n;
2662
2663
0
      tokens = msStringSplit(value, ' ', &n);
2664
0
      if (tokens == NULL || n != 2) {
2665
0
        msSetError(MS_WCSERR,
2666
0
                   "Wrong number of arguments for wcs|ows_resolution metadata.",
2667
0
                   "msWCSGetCoverageMetadata20()");
2668
0
        msFreeCharArray(tokens, n);
2669
0
        return MS_FAILURE;
2670
0
      }
2671
0
      cm->xresolution = atof(tokens[0]);
2672
0
      cm->yresolution = atof(tokens[1]);
2673
0
      msFreeCharArray(tokens, n);
2674
0
    }
2675
2676
    /* get Size (in pixels and lines) */
2677
0
    cm->xsize = 0;
2678
0
    cm->ysize = 0;
2679
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "size")) !=
2680
0
        NULL) {
2681
0
      char **tokens;
2682
0
      int n;
2683
2684
0
      tokens = msStringSplit(value, ' ', &n);
2685
0
      if (tokens == NULL || n != 2) {
2686
0
        msSetError(MS_WCSERR,
2687
0
                   "Wrong number of arguments for wcs|ows_size metadata.",
2688
0
                   "msWCSGetCoverageMetadata20()");
2689
0
        msFreeCharArray(tokens, n);
2690
0
        return MS_FAILURE;
2691
0
      }
2692
0
      cm->xsize = atoi(tokens[0]);
2693
0
      cm->ysize = atoi(tokens[1]);
2694
0
      msFreeCharArray(tokens, n);
2695
0
    }
2696
2697
    /* try to compute raster size */
2698
0
    if (cm->xsize == 0 && cm->ysize == 0 && cm->xresolution != 0.0 &&
2699
0
        cm->yresolution != 0.0 && cm->extent.minx != cm->extent.maxx &&
2700
0
        cm->extent.miny != cm->extent.maxy) {
2701
0
      cm->xsize =
2702
0
          (int)((cm->extent.maxx - cm->extent.minx) / cm->xresolution + 0.5);
2703
0
      cm->ysize = (int)fabs(
2704
0
          (cm->extent.maxy - cm->extent.miny) / cm->yresolution + 0.5);
2705
0
    }
2706
2707
    /* try to compute raster resolution */
2708
0
    if ((cm->xresolution == 0.0 || cm->yresolution == 0.0) && cm->xsize != 0 &&
2709
0
        cm->ysize != 0) {
2710
0
      cm->xresolution = (cm->extent.maxx - cm->extent.minx) / cm->xsize;
2711
0
      cm->yresolution = (cm->extent.maxy - cm->extent.miny) / cm->ysize;
2712
0
    }
2713
2714
    /* do we have information to do anything */
2715
0
    if (cm->xresolution == 0.0 || cm->yresolution == 0.0 || cm->xsize == 0 ||
2716
0
        cm->ysize == 0) {
2717
0
      msSetError(MS_WCSERR,
2718
0
                 "Failed to collect extent and resolution for WCS coverage "
2719
0
                 "from metadata for layer '%s'.  Need value wcs|ows_resolution "
2720
0
                 "or wcs|ows_size values.",
2721
0
                 "msWCSGetCoverageMetadata20()", layer->name);
2722
0
      return MS_FAILURE;
2723
0
    }
2724
2725
    /* compute geotransform */
2726
0
    cm->geotransform[0] = cm->extent.minx;
2727
0
    cm->geotransform[1] = cm->xresolution;
2728
0
    cm->geotransform[2] = 0.0;
2729
0
    cm->geotransform[3] = cm->extent.maxy;
2730
0
    cm->geotransform[4] = 0.0;
2731
0
    cm->geotransform[5] = -fabs(cm->yresolution);
2732
2733
    /* get bands count, or assume 1 if not found */
2734
0
    cm->numbands = 1;
2735
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "bandcount")) !=
2736
0
        NULL) {
2737
0
      int numbands = 0;
2738
0
      if (msStringParseInteger(value, &numbands) != MS_SUCCESS) {
2739
0
        return MS_FAILURE;
2740
0
      }
2741
0
      cm->numbands = (size_t)numbands;
2742
0
    }
2743
2744
0
    cm->bands = static_cast<wcs20rasterbandMetadataObj *>(
2745
0
        msSmallCalloc(sizeof(wcs20rasterbandMetadataObj), cm->numbands));
2746
2747
    /* get bands type, or assume float if not found */
2748
0
    cm->imagemode = MS_IMAGEMODE_FLOAT32;
2749
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO", "imagemode")) !=
2750
0
        NULL) {
2751
0
      if (EQUAL(value, "INT16"))
2752
0
        cm->imagemode = MS_IMAGEMODE_INT16;
2753
0
      else if (EQUAL(value, "FLOAT32"))
2754
0
        cm->imagemode = MS_IMAGEMODE_FLOAT32;
2755
0
      else if (EQUAL(value, "BYTE"))
2756
0
        cm->imagemode = MS_IMAGEMODE_BYTE;
2757
0
      else {
2758
0
        msSetError(MS_WCSERR,
2759
0
                   "Content of wcs|ows_imagemode (%s) not recognised.  Should "
2760
0
                   "be one of BYTE, INT16 or FLOAT32.",
2761
0
                   "msWCSGetCoverageMetadata20()", value);
2762
0
        return MS_FAILURE;
2763
0
      }
2764
0
    }
2765
2766
0
    if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
2767
0
                                     "native_format")) != NULL) {
2768
0
      cm->native_format = msStrdup(value);
2769
0
    }
2770
2771
    /* determine nilvalues and reasons */
2772
0
    {
2773
0
      int n_nilvalues = 0, n_nilvalues_reasons = 0;
2774
0
      char **t_nilvalues = NULL, **t_nilvalues_reasons = NULL;
2775
0
      if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
2776
0
                                       "nilvalues")) != NULL) {
2777
0
        t_nilvalues = msStringSplit(value, ' ', &n_nilvalues);
2778
0
      } else if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
2779
0
                                              "rangeset_nullvalue")) != NULL) {
2780
0
        t_nilvalues = msStringSplit(value, ' ', &n_nilvalues);
2781
0
      }
2782
2783
0
      if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
2784
0
                                       "nilvalues_reasons")) != NULL) {
2785
0
        t_nilvalues_reasons = msStringSplit(value, ' ', &n_nilvalues_reasons);
2786
0
      }
2787
2788
0
      if (n_nilvalues > 0) {
2789
0
        int i;
2790
0
        cm->numnilvalues = n_nilvalues;
2791
0
        cm->nilvalues =
2792
0
            static_cast<char **>(msSmallCalloc(sizeof(char *), n_nilvalues));
2793
0
        cm->nilvalues_reasons =
2794
0
            static_cast<char **>(msSmallCalloc(sizeof(char *), n_nilvalues));
2795
0
        for (i = 0; i < n_nilvalues; ++i) {
2796
0
          cm->nilvalues[i] = msStrdup(t_nilvalues[i]);
2797
0
          if (i < n_nilvalues_reasons) {
2798
0
            cm->nilvalues_reasons[i] = msStrdup(t_nilvalues_reasons[i]);
2799
0
          } else {
2800
0
            cm->nilvalues_reasons[i] = msStrdup("");
2801
0
          }
2802
0
        }
2803
0
      }
2804
0
      msFreeCharArray(t_nilvalues, n_nilvalues);
2805
0
      msFreeCharArray(t_nilvalues_reasons, n_nilvalues_reasons);
2806
0
    }
2807
2808
0
    {
2809
0
      int num_band_names = 0, j;
2810
0
      char **band_names = NULL;
2811
2812
0
      const char *wcs11_band_names_key = "rangeset_axes";
2813
0
      const char *wcs20_band_names_key = "band_names";
2814
2815
0
      const char *wcs11_interval_key = "interval";
2816
0
      const char *wcs20_interval_key = "interval";
2817
0
      const char *interval_key = NULL;
2818
2819
0
      const char *significant_figures_key = "significant_figures";
2820
2821
0
      const char *wcs11_keys[] = {"label", "semantic", "values_type",
2822
0
                                  "values_semantic", "description"};
2823
0
      const char *wcs20_keys[] = {"band_name", "band_interpretation",
2824
0
                                  "band_uom", "band_definition",
2825
0
                                  "band_description"};
2826
0
      const char **keys = NULL;
2827
2828
0
      char **interval_array;
2829
0
      int num_interval;
2830
2831
0
      wcs20rasterbandMetadataObj default_values;
2832
2833
      /* Decide whether WCS1.1 or WCS2.0 keys should be used */
2834
0
      if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
2835
0
                                       wcs20_band_names_key)) != NULL) {
2836
0
        keys = wcs20_keys;
2837
0
        interval_key = wcs20_interval_key;
2838
0
        band_names = msStringSplit(value, ' ', &num_band_names);
2839
0
      } else if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
2840
0
                                              wcs11_band_names_key)) != NULL) {
2841
0
        keys = wcs11_keys;
2842
0
        interval_key = wcs11_interval_key;
2843
        /* "bands" has a special processing in WCS 1.0. See */
2844
        /* msWCSSetDefaultBandsRangeSetInfo */
2845
0
        if (EQUAL(value, "bands")) {
2846
0
          num_band_names = cm->numbands;
2847
0
          band_names = (char **)msSmallMalloc(sizeof(char *) * num_band_names);
2848
0
          for (int i = 0; i < num_band_names; i++) {
2849
0
            char szName[30];
2850
0
            snprintf(szName, sizeof(szName), "Band%d", i + 1);
2851
0
            band_names[i] = msStrdup(szName);
2852
0
          }
2853
0
        } else {
2854
          /* WARNING: in WCS 1.x,, "rangeset_axes" has never been intended */
2855
          /* to contain the list of band names... This code should probably */
2856
          /* be removed */
2857
0
          band_names = msStringSplit(value, ' ', &num_band_names);
2858
0
        }
2859
0
      }
2860
2861
      /* return with error when number of bands does not match    */
2862
      /* the number of names                                      */
2863
0
      if (static_cast<size_t>(num_band_names) != cm->numbands &&
2864
0
          num_band_names != 0) {
2865
0
        msFreeCharArray(band_names, num_band_names);
2866
0
        msSetError(MS_WCSERR,
2867
0
                   "Wrong number of band names given in layer '%s'. "
2868
0
                   "Expected %d, got %d.",
2869
0
                   "msWCSGetCoverageMetadata20()", layer->name,
2870
0
                   (int)cm->numbands, num_band_names);
2871
0
        return MS_FAILURE;
2872
0
      }
2873
2874
      /* look up default metadata values */
2875
0
      for (j = 1; j < 5; ++j) {
2876
0
        if (keys != NULL) {
2877
0
          default_values.values[j] =
2878
0
              (char *)msOWSLookupMetadata(&(layer->metadata), "CO", keys[j]);
2879
0
        }
2880
0
      }
2881
2882
      /* set default values for interval and significant figures */
2883
0
      switch (cm->imagemode) {
2884
0
      case MS_IMAGEMODE_BYTE:
2885
0
        default_values.interval_min = 0.;
2886
0
        default_values.interval_max = 255.;
2887
0
        default_values.significant_figures = 3;
2888
0
        break;
2889
0
      case MS_IMAGEMODE_INT16:
2890
0
        default_values.interval_min = 0.;
2891
0
        default_values.interval_max = (double)USHRT_MAX;
2892
0
        default_values.significant_figures = 5;
2893
0
        break;
2894
0
      case MS_IMAGEMODE_FLOAT32:
2895
0
        default_values.interval_min = -FLT_MAX;
2896
0
        default_values.interval_max = FLT_MAX;
2897
0
        default_values.significant_figures = 12;
2898
0
        break;
2899
0
      default:
2900
        // other imagemodes are invalid (see above), just keep the compiler
2901
        // happy
2902
0
        msFreeCharArray(band_names, num_band_names);
2903
0
        return MS_FAILURE;
2904
0
        break;
2905
0
      }
2906
2907
      /* lookup default interval values */
2908
0
      if (interval_key != NULL &&
2909
0
          (value = msOWSLookupMetadata(&(layer->metadata), "CO",
2910
0
                                       interval_key)) != NULL) {
2911
0
        interval_array = msStringSplit(value, ' ', &num_interval);
2912
2913
0
        if (num_interval != 2 ||
2914
0
            msStringParseDouble(interval_array[0],
2915
0
                                &(default_values.interval_min)) != MS_SUCCESS ||
2916
0
            msStringParseDouble(interval_array[1],
2917
0
                                &(default_values.interval_max)) != MS_SUCCESS) {
2918
0
          msFreeCharArray(band_names, num_band_names);
2919
0
          msFreeCharArray(interval_array, num_interval);
2920
0
          msSetError(MS_WCSERR, "Wrong interval format for default axis.",
2921
0
                     "msWCSGetCoverageMetadata20()");
2922
0
          return MS_FAILURE;
2923
0
        }
2924
0
        msFreeCharArray(interval_array, num_interval);
2925
0
      }
2926
2927
      /* lookup default value for significant figures */
2928
0
      if ((value = msOWSLookupMetadata(&(layer->metadata), "CO",
2929
0
                                       significant_figures_key)) != NULL) {
2930
0
        if (msStringParseInteger(
2931
0
                value, &(default_values.significant_figures)) != MS_SUCCESS) {
2932
0
          msFreeCharArray(band_names, num_band_names);
2933
0
          msSetError(MS_WCSERR,
2934
0
                     "Wrong significant figures format "
2935
0
                     "for default axis.",
2936
0
                     "msWCSGetCoverageMetadata20()");
2937
0
          return MS_FAILURE;
2938
0
        }
2939
0
      }
2940
2941
      /* iterate over every band */
2942
0
      for (unsigned i = 0; i < cm->numbands; ++i) {
2943
0
        cm->bands[i].name = NULL;
2944
2945
        /* look up band metadata or copy defaults */
2946
0
        if (num_band_names != 0) {
2947
0
          cm->bands[i].name = msStrdup(band_names[i]);
2948
0
          for (j = 1; j < 5; ++j) {
2949
0
            value = (char *)msWCSLookupRangesetAxisMetadata20(
2950
0
                &(layer->metadata), cm->bands[i].name, keys[j]);
2951
0
            if (value != NULL) {
2952
0
              cm->bands[i].values[j] = msStrdup(value);
2953
0
            } else if (default_values.values[j] != NULL) {
2954
0
              cm->bands[i].values[j] = msStrdup(default_values.values[j]);
2955
0
            }
2956
0
          }
2957
0
        }
2958
2959
        /* set up interval */
2960
0
        value = (char *)msWCSLookupRangesetAxisMetadata20(
2961
0
            &(layer->metadata), cm->bands[i].name, interval_key);
2962
0
        if (value != NULL) {
2963
0
          num_interval = 0;
2964
0
          interval_array = msStringSplit(value, ' ', &num_interval);
2965
2966
0
          if (num_interval != 2 ||
2967
0
              msStringParseDouble(interval_array[0],
2968
0
                                  &(cm->bands[i].interval_min)) != MS_SUCCESS ||
2969
0
              msStringParseDouble(interval_array[1],
2970
0
                                  &(cm->bands[i].interval_max)) != MS_SUCCESS) {
2971
0
            msFreeCharArray(band_names, num_band_names);
2972
0
            msFreeCharArray(interval_array, num_interval);
2973
0
            msSetError(MS_WCSERR, "Wrong interval format for axis %s.",
2974
0
                       "msWCSGetCoverageMetadata20()", cm->bands[i].name);
2975
0
            return MS_FAILURE;
2976
0
          }
2977
2978
0
          msFreeCharArray(interval_array, num_interval);
2979
0
        } else {
2980
0
          cm->bands[i].interval_min = default_values.interval_min;
2981
0
          cm->bands[i].interval_max = default_values.interval_max;
2982
0
        }
2983
2984
        /* set up significant figures */
2985
0
        value = (char *)msWCSLookupRangesetAxisMetadata20(
2986
0
            &(layer->metadata), cm->bands[i].name, significant_figures_key);
2987
0
        if (value != NULL) {
2988
0
          if (msStringParseInteger(
2989
0
                  value, &(cm->bands[i].significant_figures)) != MS_SUCCESS) {
2990
0
            msFreeCharArray(band_names, num_band_names);
2991
0
            msSetError(MS_WCSERR,
2992
0
                       "Wrong significant figures format "
2993
0
                       "for axis %s.",
2994
0
                       "msWCSGetCoverageMetadata20()", cm->bands[i].name);
2995
0
            return MS_FAILURE;
2996
0
          }
2997
0
        } else {
2998
0
          cm->bands[i].significant_figures = default_values.significant_figures;
2999
0
        }
3000
0
      }
3001
3002
0
      msFreeCharArray(band_names, num_band_names);
3003
0
    }
3004
0
  } else if (layer->data == NULL) {
3005
    /* no virtual metadata, not ok unless we're talking 1 image, hopefully we
3006
     * can fix that */
3007
0
    msSetError(
3008
0
        MS_WCSERR,
3009
0
        "RASTER Layer with no DATA statement and no WCS virtual dataset "
3010
0
        "metadata.  Tileindexed raster layers not supported for WCS without "
3011
0
        "virtual dataset metadata (cm->extent, wcs_res, wcs_size).",
3012
0
        "msWCSGetCoverageMetadata20()");
3013
0
    return MS_FAILURE;
3014
0
  } else {
3015
    /* work from the file (e.g. DATA) */
3016
0
    GDALDatasetH hDS;
3017
0
    GDALDriverH hDriver;
3018
0
    GDALRasterBandH hBand;
3019
0
    char szPath[MS_MAXPATHLEN];
3020
0
    const char *driver_short_name, *driver_long_name;
3021
3022
0
    msGDALInitialize();
3023
3024
0
    msTryBuildPath3(szPath, layer->map->mappath, layer->map->shapepath,
3025
0
                    layer->data);
3026
0
    msAcquireLock(TLOCK_GDAL);
3027
0
    {
3028
0
      char **connectionoptions =
3029
0
          msGetStringListFromHashTable(&(layer->connectionoptions));
3030
0
      hDS = GDALOpenEx(szPath, GDAL_OF_RASTER, NULL,
3031
0
                       (const char *const *)connectionoptions, NULL);
3032
0
      CSLDestroy(connectionoptions);
3033
0
    }
3034
0
    if (hDS == NULL) {
3035
0
      msReleaseLock(TLOCK_GDAL);
3036
0
      msSetError(MS_IOERR, "%s", "msWCSGetCoverageMetadata20()",
3037
0
                 CPLGetLastErrorMsg());
3038
0
      return MS_FAILURE;
3039
0
    }
3040
3041
0
    msGetGDALGeoTransform(hDS, layer->map, layer, cm->geotransform);
3042
3043
0
    cm->xsize = GDALGetRasterXSize(hDS);
3044
0
    cm->ysize = GDALGetRasterYSize(hDS);
3045
3046
0
    cm->extent.minx = cm->geotransform[0];
3047
0
    cm->extent.maxx = cm->geotransform[0] + cm->geotransform[1] * cm->xsize +
3048
0
                      cm->geotransform[2] * cm->ysize;
3049
0
    cm->extent.miny = cm->geotransform[3] + cm->geotransform[4] * cm->xsize +
3050
0
                      cm->geotransform[5] * cm->ysize;
3051
0
    cm->extent.maxy = cm->geotransform[3];
3052
3053
0
    cm->xresolution = cm->geotransform[1];
3054
0
    cm->yresolution = cm->geotransform[5];
3055
3056
0
    cm->numbands = GDALGetRasterCount(hDS);
3057
3058
    /* TODO nilvalues? */
3059
3060
0
    if (cm->numbands == 0) {
3061
0
      msReleaseLock(TLOCK_GDAL);
3062
0
      msSetError(MS_WCSERR,
3063
0
                 "Raster file %s has no raster bands.  This cannot be used in "
3064
0
                 "a layer.",
3065
0
                 "msWCSGetCoverageMetadata20()", layer->data);
3066
0
      return MS_FAILURE;
3067
0
    }
3068
3069
0
    hBand = GDALGetRasterBand(hDS, 1);
3070
0
    switch (GDALGetRasterDataType(hBand)) {
3071
0
    case GDT_Byte:
3072
0
      cm->imagemode = MS_IMAGEMODE_BYTE;
3073
0
      break;
3074
0
    case GDT_Int16:
3075
0
      cm->imagemode = MS_IMAGEMODE_INT16;
3076
0
      break;
3077
0
    default:
3078
0
      cm->imagemode = MS_IMAGEMODE_FLOAT32;
3079
0
      break;
3080
0
    }
3081
3082
0
    cm->bands = static_cast<wcs20rasterbandMetadataObj *>(
3083
0
        msSmallCalloc(sizeof(wcs20rasterbandMetadataObj), cm->numbands));
3084
3085
    /* get as much band metadata as possible */
3086
0
    for (unsigned i = 0; i < cm->numbands; ++i) {
3087
0
      char bandname[32];
3088
0
      GDALColorInterp colorInterp;
3089
0
      hBand = GDALGetRasterBand(hDS, i + 1);
3090
0
      snprintf(bandname, sizeof(bandname), "band%d", i + 1);
3091
0
      cm->bands[i].name = msStrdup(bandname);
3092
0
      colorInterp = GDALGetRasterColorInterpretation(hBand);
3093
0
      cm->bands[i].interpretation =
3094
0
          msStrdup(GDALGetColorInterpretationName(colorInterp));
3095
0
      cm->bands[i].uom = msStrdup(GDALGetRasterUnitType(hBand));
3096
0
      if (strlen(cm->bands[i].uom) == 0) {
3097
0
        msFree(cm->bands[i].uom);
3098
0
        cm->bands[i].uom = NULL;
3099
0
      }
3100
3101
      /* set up interval and significant figures */
3102
0
      switch (cm->imagemode) {
3103
0
      case MS_IMAGEMODE_BYTE:
3104
0
      case MS_IMAGEMODE_PC256:
3105
0
        cm->bands[i].interval_min = 0.;
3106
0
        cm->bands[i].interval_max = 255.;
3107
0
        cm->bands[i].significant_figures = 3;
3108
0
        break;
3109
0
      case MS_IMAGEMODE_INT16:
3110
0
        cm->bands[i].interval_min = 0.;
3111
0
        cm->bands[i].interval_max = (double)USHRT_MAX;
3112
0
        cm->bands[i].significant_figures = 5;
3113
0
        break;
3114
0
      case MS_IMAGEMODE_FLOAT32:
3115
0
        cm->bands[i].interval_min = -FLT_MAX;
3116
0
        cm->bands[i].interval_max = FLT_MAX;
3117
0
        cm->bands[i].significant_figures = 12;
3118
0
        break;
3119
0
      }
3120
0
    }
3121
3122
0
    hDriver = GDALGetDatasetDriver(hDS);
3123
0
    driver_short_name = GDALGetDriverShortName(hDriver);
3124
0
    driver_long_name = GDALGetDriverLongName(hDriver);
3125
    /* TODO: improve this, exchange strstr() */
3126
0
    for (int i = 0; i < layer->map->numoutputformats; ++i) {
3127
0
      if (strstr(layer->map->outputformatlist[i]->driver, driver_short_name) !=
3128
0
              NULL ||
3129
0
          strstr(layer->map->outputformatlist[i]->driver, driver_long_name) !=
3130
0
              NULL) {
3131
0
        cm->native_format = msStrdup(layer->map->outputformatlist[i]->mimetype);
3132
0
        break;
3133
0
      }
3134
0
    }
3135
3136
0
    GDALClose(hDS);
3137
0
    msReleaseLock(TLOCK_GDAL);
3138
0
  }
3139
3140
0
  return MS_SUCCESS;
3141
0
}
3142
3143
/************************************************************************/
3144
/*                   msWCSClearCoverageMetadata20()                     */
3145
/*                                                                      */
3146
/*      Returns all dynamically allocated memory from a coverage meta-  */
3147
/*      data object.                                                    */
3148
/************************************************************************/
3149
3150
0
static int msWCSClearCoverageMetadata20(wcs20coverageMetadataObj *cm) {
3151
0
  msFree(cm->native_format);
3152
0
  for (unsigned i = 0; i < cm->numnilvalues; ++i) {
3153
0
    msFree(cm->nilvalues[i]);
3154
0
    msFree(cm->nilvalues_reasons[i]);
3155
0
  }
3156
0
  msFree(cm->nilvalues);
3157
0
  msFree(cm->nilvalues_reasons);
3158
3159
0
  for (unsigned i = 0; i < cm->numbands; ++i) {
3160
0
    for (int j = 0; j < 5; ++j) {
3161
0
      msFree(cm->bands[i].values[j]);
3162
0
    }
3163
0
  }
3164
0
  msFree(cm->bands);
3165
0
  msFree(cm->srs_epsg);
3166
0
  return MS_SUCCESS;
3167
0
}
3168
3169
/************************************************************************/
3170
/*                   msWCSException20()                                 */
3171
/*                                                                      */
3172
/*      Writes out an OWS compliant exception.                          */
3173
/************************************************************************/
3174
3175
int msWCSException20(mapObj *map, const char *exceptionCode,
3176
0
                     const char *locator, const char *version) {
3177
0
  int size = 0;
3178
0
  const char *status = "400 Bad Request";
3179
0
  char *errorString = NULL;
3180
0
  char *schemasLocation = NULL;
3181
0
  char *xsi_schemaLocation = NULL;
3182
0
  char version_string[OWS_VERSION_MAXLEN];
3183
3184
0
  xmlDocPtr psDoc = NULL;
3185
0
  xmlNodePtr psRootNode = NULL;
3186
0
  xmlNodePtr psMainNode = NULL;
3187
0
  xmlNsPtr psNsOws = NULL;
3188
0
  xmlNsPtr psNsXsi = NULL;
3189
0
  xmlChar *buffer = NULL;
3190
3191
0
  errorString = msGetErrorString("\n");
3192
0
  schemasLocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
3193
3194
0
  psDoc = xmlNewDoc(BAD_CAST "1.0");
3195
3196
0
  psRootNode = xmlNewNode(NULL, BAD_CAST "ExceptionReport");
3197
0
  psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_20_NAMESPACE_URI,
3198
0
                     BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
3199
0
  xmlSetNs(psRootNode, psNsOws);
3200
3201
0
  psNsXsi = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI,
3202
0
                     BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
3203
3204
  /* add attributes to root element */
3205
0
  xmlNewProp(psRootNode, BAD_CAST "version", BAD_CAST version);
3206
0
  xmlNewProp(psRootNode, BAD_CAST "xml:lang",
3207
0
             BAD_CAST msOWSGetLanguage(map, "exception"));
3208
3209
  /* get 2 digits version string: '2.0' */
3210
0
  msOWSGetVersionString(OWS_2_0_0, version_string);
3211
0
  version_string[3] = '\0';
3212
3213
0
  xsi_schemaLocation = msStrdup((char *)psNsOws->href);
3214
0
  xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
3215
0
  xsi_schemaLocation =
3216
0
      msStringConcatenate(xsi_schemaLocation, (char *)schemasLocation);
3217
0
  xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/ows/");
3218
0
  xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, version_string);
3219
0
  xsi_schemaLocation =
3220
0
      msStringConcatenate(xsi_schemaLocation, "/owsExceptionReport.xsd");
3221
3222
  /* add namespace'd attributes to root element */
3223
0
  xmlNewNsProp(psRootNode, psNsXsi, BAD_CAST "schemaLocation",
3224
0
               BAD_CAST xsi_schemaLocation);
3225
3226
  /* add child element */
3227
0
  psMainNode = xmlNewChild(psRootNode, NULL, BAD_CAST "Exception", NULL);
3228
3229
  /* add attributes to child */
3230
0
  xmlNewProp(psMainNode, BAD_CAST "exceptionCode", BAD_CAST exceptionCode);
3231
3232
0
  if (locator != NULL) {
3233
0
    xmlNewProp(psMainNode, BAD_CAST "locator", BAD_CAST locator);
3234
0
  }
3235
3236
0
  if (errorString != NULL) {
3237
0
    char *errorMessage = msEncodeHTMLEntities(errorString);
3238
0
    xmlNewChild(psMainNode, NULL, BAD_CAST "ExceptionText",
3239
0
                BAD_CAST errorMessage);
3240
0
    msFree(errorMessage);
3241
0
  }
3242
3243
  /*psRootNode = msOWSCommonExceptionReport(psNsOws, OWS_2_0_0,
3244
          schemasLocation, version, msOWSGetLanguage(map, "exception"),
3245
          exceptionCode, locator, errorMessage);*/
3246
3247
0
  xmlDocSetRootElement(psDoc, psRootNode);
3248
3249
0
  if (exceptionCode == NULL) {
3250
    /* Do nothing */
3251
0
  } else if (EQUAL(exceptionCode, "OperationNotSupported") ||
3252
0
             EQUAL(exceptionCode, "OptionNotSupported")) {
3253
0
    status = "501 Not Implemented";
3254
0
  } else if (EQUAL(exceptionCode, "NoSuchCoverage") ||
3255
0
             EQUAL(exceptionCode, "emptyCoverageIdList") ||
3256
0
             EQUAL(exceptionCode, "InvalidAxisLabel") ||
3257
0
             EQUAL(exceptionCode, "InvalidSubsetting")) {
3258
0
    status = "404 Not Found";
3259
0
  }
3260
3261
0
  msIO_setHeader("Status", "%s", status);
3262
0
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
3263
0
  msIO_sendHeaders();
3264
3265
0
  xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, "UTF-8", 1);
3266
3267
0
  msIO_printf("%s", buffer);
3268
3269
  /* free buffer and the document */
3270
0
  free(errorString);
3271
0
  free(schemasLocation);
3272
0
  free(xsi_schemaLocation);
3273
0
  xmlFree(buffer);
3274
0
  xmlFreeDoc(psDoc);
3275
3276
  /* clear error since we have already reported it */
3277
0
  msResetErrorList();
3278
0
  return MS_FAILURE;
3279
0
}
3280
3281
0
#define MAX_MIMES 20
3282
3283
static int msWCSGetCapabilities20_CreateProfiles(
3284
0
    mapObj *map, xmlNodePtr psServiceIdentification, xmlNsPtr psOwsNs) {
3285
0
  xmlNodePtr psProfile, psTmpNode;
3286
0
  char *available_mime_types[MAX_MIMES];
3287
0
  int i = 0;
3288
3289
  /* even indices are urls, uneven are mime-types, or null*/
3290
0
  const char *const urls_and_mime_types[] = {
3291
0
      MS_WCS_20_PROFILE_CORE,
3292
0
      NULL,
3293
0
      MS_WCS_20_PROFILE_KVP,
3294
0
      NULL,
3295
0
      MS_WCS_20_PROFILE_POST,
3296
0
      NULL,
3297
0
      MS_WCS_20_PROFILE_GML,
3298
0
      NULL,
3299
0
      MS_WCS_20_PROFILE_GML_MULTIPART,
3300
0
      NULL,
3301
0
      MS_WCS_20_PROFILE_GML_SPECIAL,
3302
0
      NULL,
3303
0
      MS_WCS_20_PROFILE_GML_GEOTIFF,
3304
0
      "image/tiff",
3305
0
      MS_WCS_20_PROFILE_CRS,
3306
0
      NULL,
3307
0
      MS_WCS_20_PROFILE_SCALING,
3308
0
      NULL,
3309
0
      MS_WCS_20_PROFILE_RANGESUBSET,
3310
0
      NULL,
3311
0
      MS_WCS_20_PROFILE_INTERPOLATION,
3312
0
      NULL,
3313
0
      NULL,
3314
      NULL /* guardian */
3315
0
  };
3316
3317
  /* navigate to node where profiles shall be inserted */
3318
0
  for (psTmpNode = psServiceIdentification->children; psTmpNode->next != NULL;
3319
0
       psTmpNode = psTmpNode->next) {
3320
0
    if (EQUAL((char *)psTmpNode->name, "ServiceTypeVersion") &&
3321
0
        !EQUAL((char *)psTmpNode->next->name, "ServiceTypeVersion"))
3322
0
      break;
3323
0
  }
3324
3325
  /* get list of all available mime types */
3326
0
  msGetOutputFormatMimeList(map, available_mime_types, MAX_MIMES);
3327
3328
0
  while (urls_and_mime_types[i] != NULL) {
3329
0
    const char *mime_type = urls_and_mime_types[i + 1];
3330
3331
    /* check if there is a mime type */
3332
0
    if (mime_type != NULL) {
3333
      /* check if the mime_type is in the list of outputformats */
3334
0
      if (CSLPartialFindString(available_mime_types, mime_type) == -1)
3335
0
        continue;
3336
0
    }
3337
3338
    /* create a new node and attach it in the tree */
3339
0
    psProfile = xmlNewNode(psOwsNs, BAD_CAST "Profile");
3340
0
    xmlNodeSetContent(psProfile, BAD_CAST urls_and_mime_types[i]);
3341
0
    xmlAddNextSibling(psTmpNode, psProfile);
3342
3343
0
    psTmpNode = psProfile;
3344
0
    i += 2;
3345
0
  }
3346
0
  return MS_SUCCESS;
3347
0
}
3348
3349
/************************************************************************/
3350
/*                   msWCSGetCapabilities20_CoverageSummary()           */
3351
/*                                                                      */
3352
/*      Helper function to create a short summary for a specific        */
3353
/*      coverage.                                                       */
3354
/************************************************************************/
3355
3356
static int msWCSGetCapabilities20_CoverageSummary(xmlDocPtr doc,
3357
                                                  xmlNodePtr psContents,
3358
0
                                                  layerObj *layer) {
3359
0
  wcs20coverageMetadataObj cm;
3360
0
  int status;
3361
0
  xmlNodePtr psCSummary;
3362
3363
0
  xmlNsPtr psWcsNs =
3364
0
      xmlSearchNs(doc, xmlDocGetRootElement(doc), BAD_CAST "wcs");
3365
3366
0
  status = msWCSGetCoverageMetadata20(layer, &cm);
3367
0
  if (status != MS_SUCCESS)
3368
0
    return MS_FAILURE;
3369
3370
0
  psCSummary =
3371
0
      xmlNewChild(psContents, psWcsNs, BAD_CAST "CoverageSummary", NULL);
3372
0
  xmlNewChild(psCSummary, psWcsNs, BAD_CAST "CoverageId", BAD_CAST layer->name);
3373
0
  xmlNewChild(psCSummary, psWcsNs, BAD_CAST "CoverageSubtype",
3374
0
              BAD_CAST "RectifiedGridCoverage");
3375
3376
0
  msWCS_11_20_PrintMetadataLinks(layer, doc, psCSummary);
3377
3378
0
  msWCSClearCoverageMetadata20(&cm);
3379
3380
0
  return MS_SUCCESS;
3381
0
}
3382
3383
/************************************************************************/
3384
/*                          msWCSAddInspireDSID20                       */
3385
/************************************************************************/
3386
3387
static void msWCSAddInspireDSID20(mapObj *map, xmlNsPtr psNsInspireDls,
3388
                                  xmlNsPtr psNsInspireCommon,
3389
0
                                  xmlNodePtr pDlsExtendedCapabilities) {
3390
0
  const char *dsid_code =
3391
0
      msOWSLookupMetadata(&(map->web.metadata), "CO", "inspire_dsid_code");
3392
0
  const char *dsid_ns =
3393
0
      msOWSLookupMetadata(&(map->web.metadata), "CO", "inspire_dsid_ns");
3394
0
  if (dsid_code == NULL) {
3395
0
    xmlAddChild(
3396
0
        pDlsExtendedCapabilities,
3397
0
        xmlNewComment(
3398
0
            BAD_CAST
3399
0
            "WARNING: Required metadata \"inspire_dsid_code\" missing"));
3400
0
  } else {
3401
0
    int ntokensCode = 0, ntokensNS = 0;
3402
0
    char **tokensCode;
3403
0
    char **tokensNS = NULL;
3404
0
    int i;
3405
3406
0
    tokensCode = msStringSplit(dsid_code, ',', &ntokensCode);
3407
0
    if (dsid_ns != NULL)
3408
0
      tokensNS =
3409
0
          msStringSplitComplex(dsid_ns, ",", &ntokensNS, MS_ALLOWEMPTYTOKENS);
3410
0
    if (ntokensNS > 0 && ntokensNS != ntokensCode) {
3411
0
      xmlAddChild(
3412
0
          pDlsExtendedCapabilities,
3413
0
          xmlNewComment(
3414
0
              BAD_CAST
3415
0
              "WARNING: \"inspire_dsid_code\" and \"inspire_dsid_ns\" have not "
3416
0
              "the same number of elements. Ignoring inspire_dsid_ns"));
3417
0
      msFreeCharArray(tokensNS, ntokensNS);
3418
0
      tokensNS = NULL;
3419
0
      ntokensNS = 0;
3420
0
    }
3421
0
    for (i = 0; i < ntokensCode; i++) {
3422
0
      xmlNodePtr pSDSI =
3423
0
          xmlNewNode(psNsInspireDls, BAD_CAST "SpatialDataSetIdentifier");
3424
0
      xmlAddChild(pDlsExtendedCapabilities, pSDSI);
3425
0
      xmlNewTextChild(pSDSI, psNsInspireCommon, BAD_CAST "Code",
3426
0
                      BAD_CAST tokensCode[i]);
3427
0
      if (ntokensNS > 0 && tokensNS[i][0] != '\0')
3428
0
        xmlNewTextChild(pSDSI, psNsInspireCommon, BAD_CAST "Namespace",
3429
0
                        BAD_CAST tokensNS[i]);
3430
0
    }
3431
0
    msFreeCharArray(tokensCode, ntokensCode);
3432
0
    msFreeCharArray(tokensNS, ntokensNS);
3433
0
  }
3434
0
}
3435
3436
/************************************************************************/
3437
/*                   msWCSGetCapabilities20()                           */
3438
/*                                                                      */
3439
/*      Performs the GetCapabilities operation. Writes out a xml        */
3440
/*      structure with server specific information and a summary        */
3441
/*      of all available coverages.                                     */
3442
/************************************************************************/
3443
3444
int msWCSGetCapabilities20(mapObj *map, cgiRequestObj *req,
3445
                           wcs20ParamsObjPtr params,
3446
0
                           owsRequestObj *ows_request) {
3447
0
  xmlDocPtr psDoc = NULL; /* document pointer */
3448
0
  xmlNodePtr psRootNode, psOperationsNode, psNode;
3449
0
  const char *updatesequence = NULL;
3450
0
  xmlNsPtr psOwsNs = NULL, psXLinkNs = NULL, psWcsNs = NULL, psCrsNs = NULL,
3451
0
           psIntNs = NULL;
3452
0
  char *script_url = NULL, *script_url_encoded = NULL, *format_list = NULL;
3453
0
  int i;
3454
0
  xmlDocPtr pInspireTmpDoc = NULL;
3455
3456
0
  const char *inspire_capabilities =
3457
0
      msOWSLookupMetadata(&(map->web.metadata), "CO", "inspire_capabilities");
3458
3459
0
  char *validated_language = msOWSLanguageNegotiation(
3460
0
      map, "CO", params->accept_languages, CSLCount(params->accept_languages));
3461
3462
  /* -------------------------------------------------------------------- */
3463
  /*      Create document.                                                */
3464
  /* -------------------------------------------------------------------- */
3465
0
  psDoc = xmlNewDoc(BAD_CAST "1.0");
3466
3467
0
  psRootNode = xmlNewNode(NULL, BAD_CAST "Capabilities");
3468
3469
0
  xmlDocSetRootElement(psDoc, psRootNode);
3470
3471
  /* -------------------------------------------------------------------- */
3472
  /*      Name spaces                                                     */
3473
  /* -------------------------------------------------------------------- */
3474
3475
0
  msWCSPrepareNamespaces20(psDoc, psRootNode, map,
3476
0
                           inspire_capabilities != NULL);
3477
3478
  /* lookup namespaces */
3479
0
  psOwsNs = xmlSearchNs(psDoc, psRootNode,
3480
0
                        BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
3481
0
  psWcsNs = xmlSearchNs(psDoc, psRootNode,
3482
0
                        BAD_CAST MS_OWSCOMMON_WCS_NAMESPACE_PREFIX);
3483
0
  xmlSearchNs(psDoc, psRootNode, BAD_CAST MS_OWSCOMMON_GML_NAMESPACE_PREFIX);
3484
0
  psXLinkNs = xmlSearchNs(psDoc, psRootNode, BAD_CAST "xlink");
3485
0
  psCrsNs = xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/wcs/crs/1.0",
3486
0
                     BAD_CAST "crs");
3487
0
  psIntNs = xmlNewNs(psRootNode,
3488
0
                     BAD_CAST "http://www.opengis.net/wcs/interpolation/1.0",
3489
0
                     BAD_CAST "int");
3490
3491
0
  xmlSetNs(psRootNode, psWcsNs);
3492
3493
0
  xmlNewProp(psRootNode, BAD_CAST "version", BAD_CAST params->version);
3494
3495
0
  updatesequence =
3496
0
      msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
3497
0
  if (params->updatesequence != NULL) {
3498
0
    i = msOWSNegotiateUpdateSequence(params->updatesequence, updatesequence);
3499
0
    if (i == 0) { /* current */
3500
0
      msSetError(
3501
0
          MS_WCSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)",
3502
0
          "msWCSGetCapabilities20()", params->updatesequence, updatesequence);
3503
0
      xmlFreeDoc(psDoc);
3504
0
      return msWCSException(map, "CurrentUpdateSequence", "updatesequence",
3505
0
                            params->version);
3506
0
    }
3507
0
    if (i > 0) { /* invalid */
3508
0
      msSetError(
3509
0
          MS_WCSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)",
3510
0
          "msWCSGetCapabilities20()", params->updatesequence, updatesequence);
3511
0
      xmlFreeDoc(psDoc);
3512
0
      return msWCSException(map, "InvalidUpdateSequence", "updatesequence",
3513
0
                            params->version);
3514
0
    }
3515
0
  }
3516
0
  if (updatesequence != NULL) {
3517
0
    xmlNewProp(psRootNode, BAD_CAST "updateSequence", BAD_CAST updatesequence);
3518
0
  }
3519
3520
  /* -------------------------------------------------------------------- */
3521
  /*      Service identification.                                         */
3522
  /* -------------------------------------------------------------------- */
3523
0
  if (MS_WCS_20_CAPABILITIES_INCLUDE_SECTION(params, "ServiceIdentification")) {
3524
0
    psNode = xmlAddChild(
3525
0
        psRootNode, msOWSCommonServiceIdentification(psOwsNs, map, "OGC WCS",
3526
0
                                                     "2.0.1,1.1.1,1.0.0", "CO",
3527
0
                                                     validated_language));
3528
0
    msWCSGetCapabilities20_CreateProfiles(map, psNode, psOwsNs);
3529
0
  }
3530
3531
  /* Service Provider */
3532
0
  if (MS_WCS_20_CAPABILITIES_INCLUDE_SECTION(params, "ServiceProvider")) {
3533
0
    xmlAddChild(psRootNode,
3534
0
                msOWSCommonServiceProvider(psOwsNs, psXLinkNs, map, "CO",
3535
0
                                           validated_language));
3536
0
  }
3537
3538
  /* -------------------------------------------------------------------- */
3539
  /*      Operations metadata.                                            */
3540
  /* -------------------------------------------------------------------- */
3541
0
  if (MS_WCS_20_CAPABILITIES_INCLUDE_SECTION(params, "OperationsMetadata")) {
3542
0
    if ((script_url = msOWSGetOnlineResource(map, "CO", "onlineresource",
3543
0
                                             req)) == NULL ||
3544
0
        (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
3545
0
      xmlFreeDoc(psDoc);
3546
0
      msSetError(MS_WCSERR, "Server URL not found", "msWCSGetCapabilities20()");
3547
0
      return msWCSException(map, "NoApplicableCode", "mapserv",
3548
0
                            params->version);
3549
0
    }
3550
0
    free(script_url);
3551
3552
0
    psOperationsNode =
3553
0
        xmlAddChild(psRootNode, msOWSCommonOperationsMetadata(psOwsNs));
3554
3555
    /* -------------------------------------------------------------------- */
3556
    /*      GetCapabilities - add Sections and AcceptVersions?              */
3557
    /* -------------------------------------------------------------------- */
3558
0
    psNode = msOWSCommonOperationsMetadataOperation(
3559
0
        psOwsNs, psXLinkNs, "GetCapabilities", OWS_METHOD_GETPOST,
3560
0
        script_url_encoded);
3561
3562
0
    xmlAddChild(psNode->last->last->last,
3563
0
                msOWSCommonOperationsMetadataDomainType(
3564
0
                    OWS_2_0_0, psOwsNs, "Constraint", "PostEncoding", "XML"));
3565
0
    xmlAddChild(psOperationsNode, psNode);
3566
3567
    /* -------------------------------------------------------------------- */
3568
    /*      DescribeCoverage                                                */
3569
    /* -------------------------------------------------------------------- */
3570
0
    if (msOWSRequestIsEnabled(map, NULL, "C", "DescribeCoverage", MS_FALSE)) {
3571
0
      psNode = msOWSCommonOperationsMetadataOperation(
3572
0
          psOwsNs, psXLinkNs, "DescribeCoverage", OWS_METHOD_GETPOST,
3573
0
          script_url_encoded);
3574
0
      xmlAddChild(psNode->last->last->last,
3575
0
                  msOWSCommonOperationsMetadataDomainType(
3576
0
                      OWS_2_0_0, psOwsNs, "Constraint", "PostEncoding", "XML"));
3577
0
      xmlAddChild(psOperationsNode, psNode);
3578
0
    }
3579
3580
    /* -------------------------------------------------------------------- */
3581
    /*      GetCoverage                                                     */
3582
    /* -------------------------------------------------------------------- */
3583
0
    if (msOWSRequestIsEnabled(map, NULL, "C", "GetCoverage", MS_FALSE)) {
3584
0
      psNode = msOWSCommonOperationsMetadataOperation(
3585
0
          psOwsNs, psXLinkNs, "GetCoverage", OWS_METHOD_GETPOST,
3586
0
          script_url_encoded);
3587
3588
0
      xmlAddChild(psNode->last->last->last,
3589
0
                  msOWSCommonOperationsMetadataDomainType(
3590
0
                      OWS_2_0_0, psOwsNs, "Constraint", "PostEncoding", "XML"));
3591
0
      xmlAddChild(psOperationsNode, psNode);
3592
0
    }
3593
0
    msFree(script_url_encoded);
3594
3595
    /* -------------------------------------------------------------------- */
3596
    /*      Extended Capabilities for inspire                               */
3597
    /* -------------------------------------------------------------------- */
3598
3599
0
    if (inspire_capabilities) {
3600
0
      msIOContext *old_context;
3601
0
      msIOContext *new_context;
3602
0
      msIOBuffer *buffer;
3603
3604
0
      xmlNodePtr pRoot;
3605
0
      xmlNodePtr pOWSExtendedCapabilities;
3606
0
      xmlNodePtr pDlsExtendedCapabilities;
3607
0
      xmlNodePtr pChild;
3608
3609
0
      xmlNsPtr psInspireCommonNs = xmlSearchNs(
3610
0
          psDoc, psRootNode, BAD_CAST MS_INSPIRE_COMMON_NAMESPACE_PREFIX);
3611
0
      xmlNsPtr psInspireDlsNs = xmlSearchNs(
3612
0
          psDoc, psRootNode, BAD_CAST MS_INSPIRE_DLS_NAMESPACE_PREFIX);
3613
3614
0
      old_context = msIO_pushStdoutToBufferAndGetOldContext();
3615
0
      msOWSPrintInspireCommonExtendedCapabilities(
3616
0
          stdout, map, "CO", OWS_WARN, "foo",
3617
0
          "xmlns:" MS_INSPIRE_COMMON_NAMESPACE_PREFIX
3618
0
          "=\"" MS_INSPIRE_COMMON_NAMESPACE_URI "\" "
3619
0
          "xmlns:" MS_INSPIRE_DLS_NAMESPACE_PREFIX
3620
0
          "=\"" MS_INSPIRE_DLS_NAMESPACE_URI "\" "
3621
0
          "xmlns:xsi=\"" MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI "\"",
3622
0
          validated_language, OWS_WCS);
3623
3624
0
      new_context = msIO_getHandler(stdout);
3625
0
      buffer = (msIOBuffer *)new_context->cbData;
3626
3627
      /* Remove spaces between > and < to get properly indented result */
3628
0
      msXMLStripIndentation((char *)buffer->data);
3629
3630
0
      pInspireTmpDoc = xmlParseDoc((const xmlChar *)buffer->data);
3631
0
      pRoot = xmlDocGetRootElement(pInspireTmpDoc);
3632
0
      xmlReconciliateNs(psDoc, pRoot);
3633
3634
0
      pOWSExtendedCapabilities =
3635
0
          xmlNewNode(psOwsNs, BAD_CAST "ExtendedCapabilities");
3636
0
      xmlAddChild(psOperationsNode, pOWSExtendedCapabilities);
3637
3638
0
      pDlsExtendedCapabilities =
3639
0
          xmlNewNode(psInspireDlsNs, BAD_CAST "ExtendedCapabilities");
3640
0
      xmlAddChild(pOWSExtendedCapabilities, pDlsExtendedCapabilities);
3641
3642
0
      pChild = pRoot->children;
3643
0
      while (pChild != NULL) {
3644
0
        xmlNodePtr pNext = pChild->next;
3645
0
        xmlUnlinkNode(pChild);
3646
0
        xmlAddChild(pDlsExtendedCapabilities, pChild);
3647
0
        pChild = pNext;
3648
0
      }
3649
3650
0
      msWCSAddInspireDSID20(map, psInspireDlsNs, psInspireCommonNs,
3651
0
                            pDlsExtendedCapabilities);
3652
3653
0
      msIO_restoreOldStdoutContext(old_context);
3654
0
    }
3655
0
  }
3656
3657
  /* -------------------------------------------------------------------- */
3658
  /*      Service metadata.                                               */
3659
  /* -------------------------------------------------------------------- */
3660
3661
0
  if (MS_WCS_20_CAPABILITIES_INCLUDE_SECTION(params, "ServiceMetadata")) {
3662
0
    xmlNodePtr psExtensionNode = NULL;
3663
0
    psNode = xmlNewChild(psRootNode, psWcsNs, BAD_CAST "ServiceMetadata", NULL);
3664
3665
    /* Apply default formats */
3666
0
    msApplyDefaultOutputFormats(map);
3667
3668
    /* Add formats list */
3669
0
    format_list = msWCSGetFormatsList20(map, NULL);
3670
0
    msLibXml2GenerateList(psNode, psWcsNs, "formatSupported", format_list, ',');
3671
0
    msFree(format_list);
3672
3673
0
    psExtensionNode = xmlNewChild(psNode, psWcsNs, BAD_CAST "Extension", NULL);
3674
    /* interpolations supported */
3675
0
    {
3676
      /* TODO: use URIs/URLs once they are specified */
3677
0
      const char *const interpolation_methods[] = {"NEAREST", "AVERAGE",
3678
0
                                                   "BILINEAR"};
3679
0
      int i;
3680
0
      xmlNodePtr imNode = xmlNewChild(psExtensionNode, psIntNs,
3681
0
                                      BAD_CAST "InterpolationMetadata", NULL);
3682
3683
0
      for (i = 0; i < 3; ++i) {
3684
0
        xmlNewChild(imNode, psIntNs, BAD_CAST "InterpolationSupported",
3685
0
                    BAD_CAST interpolation_methods[i]);
3686
0
      }
3687
0
    }
3688
3689
    /* Report the supported CRSs */
3690
0
    {
3691
0
      char *crs_list = NULL;
3692
0
      xmlNodePtr crsMetadataNode =
3693
0
          xmlNewChild(psExtensionNode, psCrsNs, BAD_CAST "CrsMetadata", NULL);
3694
3695
0
      if ((crs_list = msOWSGetProjURI(&(map->projection), &(map->web.metadata),
3696
0
                                      "CO", MS_FALSE)) != NULL) {
3697
0
        msLibXml2GenerateList(crsMetadataNode, psCrsNs, "crsSupported",
3698
0
                              crs_list, ' ');
3699
0
        msFree(crs_list);
3700
0
      } else {
3701
        /* could not determine list of CRSs */
3702
0
      }
3703
0
    }
3704
0
  }
3705
3706
  /* -------------------------------------------------------------------- */
3707
  /*      Contents section.                                               */
3708
  /* -------------------------------------------------------------------- */
3709
0
  if (MS_WCS_20_CAPABILITIES_INCLUDE_SECTION(params, "Contents")) {
3710
0
    psNode = xmlNewChild(psRootNode, psWcsNs, BAD_CAST "Contents", NULL);
3711
3712
0
    if (ows_request->numlayers == 0) {
3713
0
      xmlAddChild(psNode, xmlNewComment(BAD_CAST(
3714
0
                              "WARNING: No WCS layers are enabled. "
3715
0
                              "Check wcs/ows_enable_request settings.")));
3716
0
    } else {
3717
0
      for (i = 0; i < map->numlayers; ++i) {
3718
0
        layerObj *layer = map->layers[i];
3719
0
        int status;
3720
3721
0
        if (!msWCSIsLayerSupported(layer))
3722
0
          continue;
3723
3724
0
        if (!msIntegerInArray(layer->index, ows_request->enabled_layers,
3725
0
                              ows_request->numlayers))
3726
0
          continue;
3727
3728
0
        status = msWCSGetCapabilities20_CoverageSummary(psDoc, psNode, layer);
3729
0
        if (status != MS_SUCCESS) {
3730
0
          msFree(validated_language);
3731
0
          xmlFreeDoc(psDoc);
3732
0
          xmlCleanupParser();
3733
0
          return msWCSException(map, "Internal", "mapserv", params->version);
3734
0
        }
3735
0
      }
3736
0
    }
3737
0
  }
3738
  /* -------------------------------------------------------------------- */
3739
  /*      Write out the document and clean up.                            */
3740
  /* -------------------------------------------------------------------- */
3741
0
  msWCSWriteDocument20(psDoc);
3742
0
  msFree(validated_language);
3743
0
  xmlFreeDoc(psDoc);
3744
0
  if (pInspireTmpDoc)
3745
0
    xmlFreeDoc(pInspireTmpDoc);
3746
0
  xmlCleanupParser();
3747
0
  return MS_SUCCESS;
3748
0
}
3749
3750
/************************************************************************/
3751
/*                   msWCSDescribeCoverage20_CoverageDescription()      */
3752
/*                                                                      */
3753
/*      Creates a description of a specific coverage with the three     */
3754
/*      major sections: BoundedBy, DomainSet and RangeType.             */
3755
/************************************************************************/
3756
3757
static int msWCSDescribeCoverage20_CoverageDescription(layerObj *layer,
3758
                                                       xmlDocPtr psDoc,
3759
0
                                                       xmlNodePtr psRootNode) {
3760
0
  int status, swapAxes;
3761
0
  wcs20coverageMetadataObj cm;
3762
0
  xmlNodePtr psCD;
3763
0
  xmlNsPtr psWcsNs, psGmlNs, psGmlcovNs, psSweNs, psXLinkNs;
3764
0
  psWcsNs = psGmlNs = psGmlcovNs = psSweNs = psXLinkNs = NULL;
3765
3766
0
  psWcsNs = xmlSearchNs(psDoc, xmlDocGetRootElement(psDoc),
3767
0
                        BAD_CAST MS_OWSCOMMON_WCS_NAMESPACE_PREFIX);
3768
0
  psGmlNs = xmlSearchNs(psDoc, xmlDocGetRootElement(psDoc),
3769
0
                        BAD_CAST MS_OWSCOMMON_GML_NAMESPACE_PREFIX);
3770
0
  psGmlcovNs = xmlSearchNs(psDoc, xmlDocGetRootElement(psDoc),
3771
0
                           BAD_CAST MS_OWSCOMMON_GMLCOV_NAMESPACE_PREFIX);
3772
0
  psSweNs = xmlSearchNs(psDoc, xmlDocGetRootElement(psDoc),
3773
0
                        BAD_CAST MS_OWSCOMMON_SWE_NAMESPACE_PREFIX);
3774
0
  psXLinkNs = xmlSearchNs(psDoc, xmlDocGetRootElement(psDoc),
3775
0
                          BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_PREFIX);
3776
3777
  /* -------------------------------------------------------------------- */
3778
  /*      Verify layer is processable.                                    */
3779
  /* -------------------------------------------------------------------- */
3780
0
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
3781
0
    return MS_FAILURE;
3782
3783
0
  if (!msWCSIsLayerSupported(layer))
3784
0
    return MS_SUCCESS;
3785
3786
  /* -------------------------------------------------------------------- */
3787
  /*      Setup coverage metadata.                                        */
3788
  /* -------------------------------------------------------------------- */
3789
0
  status = msWCSGetCoverageMetadata20(layer, &cm);
3790
0
  if (status != MS_SUCCESS)
3791
0
    return status;
3792
3793
0
  swapAxes = msWCSSwapAxes20(cm.srs_uri);
3794
3795
  /* fill in bands rangeset info, if required. */
3796
  /* msWCSSetDefaultBandsRangeSetInfo( NULL, &cm, layer ); */
3797
3798
  /* -------------------------------------------------------------------- */
3799
  /*      Create CoverageDescription node.                                */
3800
  /* -------------------------------------------------------------------- */
3801
0
  psCD = xmlNewChild(psRootNode, psWcsNs, BAD_CAST "CoverageDescription", NULL);
3802
0
  xmlNewNsProp(psCD, psGmlNs, BAD_CAST "id", BAD_CAST layer->name);
3803
3804
  /* -------------------------------------------------------------------- */
3805
  /*      gml:boundedBy                                                   */
3806
  /* -------------------------------------------------------------------- */
3807
0
  msWCSCommon20_CreateBoundedBy(&cm, psGmlNs, psCD, &(layer->projection),
3808
0
                                swapAxes);
3809
3810
0
  xmlNewChild(psCD, psWcsNs, BAD_CAST "CoverageId", BAD_CAST layer->name);
3811
3812
  /* -------------------------------------------------------------------- */
3813
  /*      gml:domainSet                                                   */
3814
  /* -------------------------------------------------------------------- */
3815
0
  msWCSCommon20_CreateDomainSet(layer, &cm, psGmlNs, psCD, &(layer->projection),
3816
0
                                swapAxes);
3817
3818
  /* -------------------------------------------------------------------- */
3819
  /*      gmlcov:rangeType                                                */
3820
  /* -------------------------------------------------------------------- */
3821
0
  msWCSCommon20_CreateRangeType(&cm, NULL, psGmlcovNs, psSweNs, psCD);
3822
3823
  /* -------------------------------------------------------------------- */
3824
  /*      wcs:ServiceParameters                                           */
3825
  /* -------------------------------------------------------------------- */
3826
0
  {
3827
0
    xmlNodePtr psSP;
3828
3829
0
    psSP = xmlNewChild(psCD, psWcsNs, BAD_CAST "ServiceParameters", NULL);
3830
0
    xmlNewChild(psSP, psWcsNs, BAD_CAST "CoverageSubtype",
3831
0
                BAD_CAST "RectifiedGridCoverage");
3832
3833
    /* -------------------------------------------------------------------- */
3834
    /*      SupportedCRS                                                    */
3835
    /* -------------------------------------------------------------------- */
3836
    /* for now, WCS 2.0 does not allow per coverage CRS definitions */
3837
    /*{
3838
      xmlNodePtr psSupportedCrss;
3839
      char *owned_value;
3840
3841
      psSupportedCrss = xmlNewChild(psSP, psWcsNs,
3842
                                    BAD_CAST "SupportedCRSs", NULL);
3843
3844
      if ((owned_value = msOWSGetProjURI(&(layer->projection),
3845
                                         &(layer->metadata), "CO", MS_FALSE)) !=
3846
    NULL) { } else if ((owned_value = msOWSGetProjURI(&(layer->map->projection),
3847
                                              &(layer->map->web.metadata), "CO",
3848
    MS_FALSE)) != NULL) { } else { msDebug("missing required information, no
3849
    SRSs defined.\n");
3850
      }
3851
3852
      if (owned_value != NULL && strlen(owned_value) > 0) {
3853
        msLibXml2GenerateList(psSupportedCrss, psWcsNs,
3854
                              "SupportedCRS", owned_value, ' ');
3855
      }
3856
3857
      xmlNewChild(psSupportedCrss, psWcsNs,
3858
                  BAD_CAST "NativeCRS", BAD_CAST cm.srs_uri);
3859
3860
      msFree(owned_value);
3861
    }*/
3862
3863
    /* -------------------------------------------------------------------- */
3864
    /*      SupportedFormats                                                */
3865
    /* -------------------------------------------------------------------- */
3866
    /* for now, WCS 2.0 does not allow per coverage format definitions */
3867
    /*{
3868
      xmlNodePtr psSupportedFormats;
3869
      char *format_list;
3870
3871
      psSupportedFormats =
3872
      xmlNewChild(psSP, psWcsNs, BAD_CAST "SupportedFormats", NULL);
3873
3874
      format_list = msWCSGetFormatsList20(layer->map, layer);
3875
3876
      if (strlen(format_list) > 0) {
3877
        msLibXml2GenerateList(psSupportedFormats, psWcsNs,
3878
                              "SupportedFormat", format_list, ',');
3879
3880
      msFree(format_list);
3881
    }*/
3882
3883
    /* -------------------------------------------------------------------- */
3884
    /*      nativeFormat                                                    */
3885
    /* -------------------------------------------------------------------- */
3886
0
    xmlNewChild(psSP, psWcsNs, BAD_CAST "nativeFormat",
3887
0
                BAD_CAST(cm.native_format ? cm.native_format : ""));
3888
3889
0
    if (!cm.native_format) {
3890
0
      msDebug("msWCSDescribeCoverage20_CoverageDescription(): "
3891
0
              "No native format specified.\n");
3892
0
    }
3893
0
  }
3894
3895
0
  msWCSClearCoverageMetadata20(&cm);
3896
3897
0
  return MS_SUCCESS;
3898
0
}
3899
3900
/************************************************************************/
3901
/*                   msWCSDescribeCoverage20()                          */
3902
/*                                                                      */
3903
/*      Implementation of the DescribeCoverage Operation. The result     */
3904
/*      of this operation is a xml document, containing specific        */
3905
/*      information about a coverage identified by an ID. The result    */
3906
/*      is written on the stream.                                       */
3907
/************************************************************************/
3908
3909
int msWCSDescribeCoverage20(mapObj *map, wcs20ParamsObjPtr params,
3910
0
                            owsRequestObj *ows_request) {
3911
0
  xmlDocPtr psDoc = NULL; /* document pointer */
3912
0
  xmlNodePtr psRootNode;
3913
0
  xmlNsPtr psWcsNs = NULL;
3914
0
  int i, j;
3915
3916
  /* create DOM document and root node */
3917
0
  psDoc = xmlNewDoc(BAD_CAST "1.0");
3918
0
  psRootNode = xmlNewNode(NULL, BAD_CAST "CoverageDescriptions");
3919
0
  xmlDocSetRootElement(psDoc, psRootNode);
3920
3921
  /* prepare initial namespace definitions */
3922
0
  msWCSPrepareNamespaces20(psDoc, psRootNode, map, MS_FALSE);
3923
3924
0
  psWcsNs = xmlSearchNs(psDoc, psRootNode,
3925
0
                        BAD_CAST MS_OWSCOMMON_WCS_NAMESPACE_PREFIX);
3926
0
  xmlSetNs(psRootNode, psWcsNs);
3927
3928
  /* check if IDs are given */
3929
0
  if (params->ids) {
3930
    /* for each given ID in the ID-list */
3931
0
    for (j = 0; params->ids[j]; j++) {
3932
0
      i = msGetLayerIndex(map, params->ids[j]);
3933
0
      if (i == -1 || (!msIntegerInArray(GET_LAYER(map, i)->index,
3934
0
                                        ows_request->enabled_layers,
3935
0
                                        ows_request->numlayers))) {
3936
0
        msSetError(MS_WCSERR, "Unknown coverage: (%s)",
3937
0
                   "msWCSDescribeCoverage20()", params->ids[j]);
3938
0
        return msWCSException(map, "NoSuchCoverage", "coverage",
3939
0
                              params->version);
3940
0
      }
3941
      /* create coverage description for the specified layer */
3942
0
      if (msWCSDescribeCoverage20_CoverageDescription(
3943
0
              (GET_LAYER(map, i)), psDoc, psRootNode) == MS_FAILURE) {
3944
0
        msSetError(MS_WCSERR, "Error retrieving coverage description.",
3945
0
                   "msWCSDescribeCoverage20()");
3946
0
        return msWCSException(map, "MissingParameterValue", "coverage",
3947
0
                              params->version);
3948
0
      }
3949
0
    }
3950
0
  } else {
3951
    /* Throw error, since IDs are mandatory */
3952
0
    msSetError(MS_WCSERR, "Missing COVERAGEID parameter.",
3953
0
               "msWCSDescribeCoverage20()");
3954
0
    return msWCSException(map, "MissingParameterValue", "coverage",
3955
0
                          params->version);
3956
0
  }
3957
3958
  /* write out the DOM document to the stream */
3959
0
  msWCSWriteDocument20(psDoc);
3960
3961
  /* cleanup */
3962
0
  xmlFreeDoc(psDoc);
3963
0
  xmlCleanupParser();
3964
3965
0
  return MS_SUCCESS;
3966
0
}
3967
3968
/************************************************************************/
3969
/*                   msWCSGetCoverage_FinalizeParamsObj20()             */
3970
/*                                                                      */
3971
/*      Finalizes a wcs20ParamsObj for a GetCoverage operation. In the  */
3972
/*      process, the params bounding box is adjusted to the subsets,     */
3973
/*      width, height and resolution are determined and the subset crs  */
3974
/*      is found out.                                                   */
3975
/************************************************************************/
3976
3977
static int msWCSGetCoverage20_FinalizeParamsObj(wcs20ParamsObjPtr params,
3978
0
                                                wcs20AxisObjPtr *axes) {
3979
0
  char *crs = NULL;
3980
0
  int have_scale, have_size, have_resolution;
3981
0
  int have_global_scale = (params->scale != MS_WCS20_UNBOUNDED) ? 1 : 0;
3982
3983
0
  if (axes[0] != NULL) {
3984
0
    if (axes[0]->subset != NULL) {
3985
0
      msDebug("Subset for X-axis found: %s\n", axes[0]->subset->axis);
3986
0
      if (!axes[0]->subset->min.unbounded)
3987
0
        params->bbox.minx = axes[0]->subset->min.scalar;
3988
0
      if (!axes[0]->subset->max.unbounded)
3989
0
        params->bbox.maxx = axes[0]->subset->max.scalar;
3990
0
      crs = axes[0]->subset->crs;
3991
0
    }
3992
0
    params->width = axes[0]->size;
3993
0
    params->resolutionX = axes[0]->resolution;
3994
0
    params->scaleX = axes[0]->scale;
3995
3996
0
    if (axes[0]->resolutionUOM != NULL) {
3997
0
      params->resolutionUnits = msStrdup(axes[0]->resolutionUOM);
3998
0
    }
3999
4000
0
    have_scale = (params->scaleX != MS_WCS20_UNBOUNDED) ? 1 : 0;
4001
0
    have_size = (params->width != 0) ? 1 : 0;
4002
0
    have_resolution = (params->resolutionX != MS_WCS20_UNBOUNDED) ? 1 : 0;
4003
4004
0
    if ((have_global_scale + have_scale + have_size + have_resolution) > 1) {
4005
0
      msSetError(
4006
0
          MS_WCSERR,
4007
0
          "Axis '%s' defines scale, size and/or resolution multiple times.",
4008
0
          "msWCSGetCoverage20_FinalizeParamsObj()", axes[0]->name);
4009
0
      return MS_FAILURE;
4010
0
    }
4011
0
  }
4012
4013
0
  if (axes[1] != NULL) {
4014
0
    if (axes[1]->subset != NULL) {
4015
0
      msDebug("Subset for Y-axis found: %s\n", axes[1]->subset->axis);
4016
0
      if (!axes[1]->subset->min.unbounded)
4017
0
        params->bbox.miny = axes[1]->subset->min.scalar;
4018
0
      if (!axes[1]->subset->max.unbounded)
4019
0
        params->bbox.maxy = axes[1]->subset->max.scalar;
4020
0
      if (crs != NULL && axes[0] != NULL && axes[0]->subset != NULL) {
4021
0
        if (!EQUAL(crs, axes[1]->subset->crs)) {
4022
0
          msSetError(MS_WCSERR, "CRS for axis %s and axis %s are not the same.",
4023
0
                     "msWCSGetCoverage20_FinalizeParamsObj()", axes[0]->name,
4024
0
                     axes[1]->name);
4025
0
          return MS_FAILURE;
4026
0
        }
4027
0
      } else {
4028
0
        crs = axes[1]->subset->crs;
4029
0
      }
4030
0
    }
4031
0
    params->height = axes[1]->size;
4032
0
    params->resolutionY = axes[1]->resolution;
4033
0
    params->scaleY = axes[1]->scale;
4034
4035
0
    if (params->resolutionUnits == NULL && axes[1]->resolutionUOM != NULL) {
4036
0
      params->resolutionUnits = msStrdup(axes[1]->resolutionUOM);
4037
0
    } else if (params->resolutionUnits != NULL &&
4038
0
               axes[1]->resolutionUOM != NULL &&
4039
0
               !EQUAL(params->resolutionUnits, axes[1]->resolutionUOM)) {
4040
0
      msSetError(MS_WCSERR,
4041
0
                 "The units of measure of the resolution for"
4042
0
                 "axis %s and axis %s are not the same.",
4043
0
                 "msWCSGetCoverage20_FinalizeParamsObj()", axes[0]->name,
4044
0
                 axes[1]->name);
4045
0
      return MS_FAILURE;
4046
0
    }
4047
4048
0
    have_scale = (params->scaleY != MS_WCS20_UNBOUNDED) ? 1 : 0;
4049
0
    have_size = (params->height != 0) ? 1 : 0;
4050
0
    have_resolution = (params->resolutionY != MS_WCS20_UNBOUNDED) ? 1 : 0;
4051
4052
0
    if ((have_global_scale + have_scale + have_size + have_resolution) > 1) {
4053
0
      msSetError(
4054
0
          MS_WCSERR,
4055
0
          "Axis '%s' defines scale, size and/or resolution multiple times.",
4056
0
          "msWCSGetCoverage20_FinalizeParamsObj()", axes[1]->name);
4057
0
      return MS_FAILURE;
4058
0
    }
4059
0
  }
4060
4061
  /* if scale is globally set, use this value instead */
4062
0
  if (params->scale != MS_WCS20_UNBOUNDED) {
4063
0
    params->scaleX = params->scaleY = params->scale;
4064
0
  }
4065
4066
  /* check if projections are equal */
4067
0
  if (crs != NULL) {
4068
0
    if (params->subsetcrs && !EQUAL(crs, params->subsetcrs)) {
4069
      /* already set and not equal -> raise exception */
4070
0
      msSetError(MS_WCSERR,
4071
0
                 "SubsetCRS does not match the CRS of the axes: "
4072
0
                 "'%s' != '%s'",
4073
0
                 "msWCSCreateBoundingBox20()", params->subsetcrs, crs);
4074
0
      return MS_FAILURE;
4075
0
    } else if (params->subsetcrs) {
4076
      /* equal, and already set -> do nothing */
4077
0
    } else {
4078
      /* not yet globally set -> set it */
4079
0
      params->subsetcrs = msStrdup(crs);
4080
0
    }
4081
0
  } else if (!params->subsetcrs) {
4082
    /* default to CRS of image */
4083
    /* leave params->subsetcrs to null */
4084
0
  }
4085
4086
0
  return MS_SUCCESS;
4087
0
}
4088
4089
/************************************************************************/
4090
/*                   msWCSGetCoverage20_GetBands()                      */
4091
/*                                                                      */
4092
/*      Returns a string, containing a comma-separated list of band     */
4093
/*      indices.                                                        */
4094
/************************************************************************/
4095
4096
static int msWCSGetCoverage20_GetBands(layerObj *layer,
4097
                                       wcs20ParamsObjPtr params,
4098
                                       wcs20coverageMetadataObjPtr cm,
4099
0
                                       char **bandlist) {
4100
0
  int maxlen, index;
4101
0
  char *interval_stop;
4102
0
  char **band_ids = NULL;
4103
4104
  /* if rangesubset parameter is not given, default to all bands */
4105
0
  if (NULL == params->range_subset) {
4106
0
    *bandlist = msStrdup("1");
4107
0
    for (unsigned i = 1; i < cm->numbands; ++i) {
4108
0
      char strnumber[12];
4109
0
      snprintf(strnumber, sizeof(strnumber), ",%d", i + 1);
4110
0
      *bandlist = msStringConcatenate(*bandlist, strnumber);
4111
0
    }
4112
0
    return MS_SUCCESS;
4113
0
  }
4114
4115
0
  maxlen = cm->numbands * 4 * sizeof(char);
4116
0
  *bandlist = static_cast<char *>(msSmallCalloc(sizeof(char), maxlen));
4117
4118
  /* Use WCS 2.0 metadata items in priority */
4119
0
  {
4120
0
    char *tmp =
4121
0
        msOWSGetEncodeMetadata(&layer->metadata, "CO", "band_names", NULL);
4122
4123
0
    if (NULL == tmp) {
4124
      /* Otherwise default to WCS 1.x*/
4125
0
      tmp =
4126
0
          msOWSGetEncodeMetadata(&layer->metadata, "CO", "rangeset_axes", NULL);
4127
      /* "bands" has a special processing in WCS 1.0. See */
4128
      /* msWCSSetDefaultBandsRangeSetInfo */
4129
0
      if (tmp != NULL && EQUAL(tmp, "bands")) {
4130
0
        int num_band_names = cm->numbands;
4131
0
        band_ids = (char **)msSmallCalloc(sizeof(char *), (num_band_names + 1));
4132
0
        for (int i = 0; i < num_band_names; i++) {
4133
0
          char szName[30];
4134
0
          snprintf(szName, sizeof(szName), "Band%d", i + 1);
4135
0
          band_ids[i] = msStrdup(szName);
4136
0
        }
4137
0
      }
4138
0
    }
4139
4140
0
    if (NULL != tmp && band_ids == NULL) {
4141
0
      band_ids = CSLTokenizeString2(tmp, " ", 0);
4142
0
    }
4143
0
    msFree(tmp);
4144
0
  }
4145
4146
  /* If we still don't have band names, use the band names from the coverage
4147
   * metadata */
4148
0
  if (band_ids == NULL) {
4149
0
    band_ids = (char **)CPLCalloc(sizeof(char *), (cm->numbands + 1));
4150
0
    for (unsigned i = 0; i < cm->numbands; ++i) {
4151
0
      band_ids[i] = CPLStrdup(cm->bands[i].name);
4152
0
    }
4153
0
  }
4154
4155
  /* Iterate over all supplied range */
4156
0
  const int count = CSLCount(params->range_subset);
4157
0
  for (int i = 0; i < count; ++i) {
4158
    /* RangeInterval case: defined as "<start>:<stop>" */
4159
0
    if ((interval_stop = strchr(params->range_subset[i], ':')) != NULL) {
4160
0
      int start, stop;
4161
0
      *interval_stop = '\0';
4162
0
      ++interval_stop;
4163
4164
      /* check if the string represents an integer */
4165
0
      if (msStringParseInteger(params->range_subset[i], &start) == MS_SUCCESS) {
4166
0
      }
4167
      /* get index of start clause, or raise an error if none was found */
4168
0
      else if ((start = CSLFindString(band_ids, params->range_subset[i])) !=
4169
0
               -1) {
4170
0
        start += 1; /* adjust index, since GDAL bands start with index 1 */
4171
0
      } else {
4172
0
        msSetError(MS_WCSERR, "'%s' is not a valid band identifier.",
4173
0
                   "msWCSGetCoverage20_GetBands()", params->range_subset[i]);
4174
0
        return MS_FAILURE;
4175
0
      }
4176
4177
      /* check if the string represents an integer */
4178
0
      if (msStringParseInteger(interval_stop, &stop) == MS_SUCCESS) {
4179
0
      }
4180
      /* get index of stop clause, or raise an error if none was found */
4181
0
      else if ((stop = CSLFindString(band_ids, interval_stop)) != -1) {
4182
0
        stop += 1; /* adjust index, since GDAL bands start with index 1 */
4183
0
      } else {
4184
0
        msSetError(MS_WCSERR, "'%s' is not a valid band identifier.",
4185
0
                   "msWCSGetCoverage20_GetBands()", interval_stop);
4186
0
        return MS_FAILURE;
4187
0
      }
4188
4189
      /* Check whether or not start and stop are different and stop is higher
4190
       * than start */
4191
0
      if (stop <= start) {
4192
0
        msSetError(MS_WCSERR, "Invalid range interval given.",
4193
0
                   "msWCSGetCoverage20_GetBands()");
4194
0
        return MS_FAILURE;
4195
0
      } else if (start < 1 || static_cast<unsigned>(stop) > cm->numbands) {
4196
0
        msSetError(MS_WCSERR, "Band interval is out of the valid range: 1-%d",
4197
0
                   "msWCSGetCoverage20_GetBands()", (int)cm->numbands);
4198
0
        return MS_FAILURE;
4199
0
      }
4200
4201
      /* expand the interval to a list of indices and push them on the list */
4202
0
      for (int j = start; j <= stop; ++j) {
4203
0
        char *tmp;
4204
0
        if (i != 0 || j != start) {
4205
0
          strlcat(*bandlist, ",", maxlen);
4206
0
        }
4207
0
        tmp = msIntToString(j);
4208
0
        strlcat(*bandlist, tmp, maxlen);
4209
0
        msFree(tmp);
4210
0
      }
4211
0
    }
4212
    /* RangeComponent case */
4213
0
    else {
4214
0
      if (i != 0) {
4215
0
        strlcat(*bandlist, ",", maxlen);
4216
0
      }
4217
4218
      /* check if the string represents an integer */
4219
0
      if (msStringParseInteger(params->range_subset[i], &index) == MS_SUCCESS) {
4220
0
        char *tmp;
4221
0
        if (index < 1 || static_cast<unsigned>(index) > cm->numbands) {
4222
0
          msSetError(MS_WCSERR, "Band index is out of the valid range: 1-%d",
4223
0
                     "msWCSGetCoverage20_GetBands()", (int)cm->numbands);
4224
0
          return MS_FAILURE;
4225
0
        }
4226
4227
0
        tmp = msIntToString((int)index);
4228
0
        strlcat(*bandlist, tmp, maxlen);
4229
0
        msFree(tmp);
4230
0
        continue;
4231
0
      }
4232
4233
      /* check if the string is equal to a band identifier    */
4234
      /* if so, what is the index of the band                 */
4235
0
      if ((index = CSLFindString(band_ids, params->range_subset[i])) != -1) {
4236
0
        char *tmp = msIntToString((int)index + 1);
4237
0
        strlcat(*bandlist, tmp, maxlen);
4238
0
        msFree(tmp);
4239
0
      } else {
4240
0
        msSetError(MS_WCSERR, "'%s' is not a valid band identifier.",
4241
0
                   "msWCSGetCoverage20_GetBands()", params->range_subset[i]);
4242
0
        return MS_FAILURE;
4243
0
      }
4244
0
    }
4245
0
  }
4246
4247
0
  CSLDestroy(band_ids);
4248
0
  return MS_SUCCESS;
4249
0
}
4250
4251
/* Fixes CPLParseNameValue to allow ':' characters for namespaced keys */
4252
4253
0
static const char *fixedCPLParseNameValue(const char *string, char **key) {
4254
0
  const char *value;
4255
0
  size_t size;
4256
0
  value = strchr(string, '=');
4257
4258
0
  if (value == NULL) {
4259
0
    *key = NULL;
4260
0
    return NULL;
4261
0
  }
4262
4263
0
  size = value - string;
4264
0
  *key = static_cast<char *>(msSmallMalloc(size + 1));
4265
0
  strncpy(*key, string, size + 1);
4266
0
  (*key)[size] = '\0';
4267
0
  return value + 1;
4268
0
}
4269
4270
/************************************************************************/
4271
/*                   msWCSSetFormatParams20()                           */
4272
/*                                                                      */
4273
/*      Parses the given format options and sets the appropriate        */
4274
/*      output format values.                                           */
4275
/************************************************************************/
4276
4277
static int msWCSSetFormatParams20(outputFormatObj *format,
4278
0
                                  char **format_options) {
4279
  /* currently geotiff only */
4280
0
  char *format_option;
4281
0
  int i = 0;
4282
0
  int is_geotiff = (format->mimetype && EQUAL(format->mimetype, "image/tiff"));
4283
4284
0
  if (!is_geotiff || !format_options) {
4285
    /* Currently only geotiff available */
4286
0
    return MS_SUCCESS;
4287
0
  }
4288
4289
0
  format_option = format_options[0];
4290
0
  while (format_option) {
4291
    /* key, value */
4292
0
    char *key;
4293
0
    const char *value = fixedCPLParseNameValue(format_option, &key);
4294
4295
0
    if (!key) {
4296
0
      msSetError(MS_WCSERR, "Could not deduct the option key.",
4297
0
                 "msWCSSetFormatParams20()");
4298
0
      return MS_FAILURE;
4299
0
    } else if (!value) {
4300
0
      msSetError(MS_WCSERR, "Missing value for parameter '%s'.",
4301
0
                 "msWCSSetFormatParams20()", key);
4302
0
      return MS_FAILURE;
4303
0
    }
4304
4305
0
    if (EQUAL(key, "geotiff:compression") && is_geotiff) {
4306
      /*COMPRESS=[JPEG/LZW/PACKBITS/DEFLATE/CCITTRLE/CCITTFAX3/CCITTFAX4/NONE]*/
4307
0
      if (EQUAL(value, "None")) {
4308
0
        msSetOutputFormatOption(format, "COMPRESS", "NONE");
4309
0
      } else if (EQUAL(value, "PackBits")) {
4310
0
        msSetOutputFormatOption(format, "COMPRESS", "PACKBITS");
4311
0
      } else if (EQUAL(value, "Deflate")) {
4312
0
        msSetOutputFormatOption(format, "COMPRESS", "DEFLATE");
4313
0
      } else if (EQUAL(value, "Huffman")) {
4314
0
        msSetOutputFormatOption(format, "COMPRESS", "CCITTRLE");
4315
0
      } else if (EQUAL(value, "LZW")) {
4316
0
        msSetOutputFormatOption(format, "COMPRESS", "LZW");
4317
0
      } else if (EQUAL(value, "JPEG")) {
4318
0
        msSetOutputFormatOption(format, "COMPRESS", "JPEG");
4319
0
      }
4320
      /* unsupported compression methods: CCITTFAX3/CCITTFAX4 */
4321
0
      else {
4322
0
        msSetError(MS_WCSERR, "Compression method '%s' not supported.",
4323
0
                   "msWCSSetFormatParams20()", value);
4324
0
        return MS_FAILURE;
4325
0
      }
4326
0
    }
4327
4328
0
    else if (EQUAL(key, "geotiff:jpeg_quality") && is_geotiff) {
4329
0
      int quality;
4330
      /* JPEG_QUALITY=[1-100] */
4331
0
      if (msStringParseInteger(value, &quality) != MS_SUCCESS) {
4332
0
        msSetError(MS_WCSERR, "Could not parse jpeg_quality value.",
4333
0
                   "msWCSSetFormatParams20()");
4334
0
        return MS_FAILURE;
4335
0
      } else if (quality < 1 || quality > 100) {
4336
0
        msSetError(MS_WCSERR, "Invalid jpeg_quality value '%d'.",
4337
0
                   "msWCSSetFormatParams20()", quality);
4338
0
        return MS_FAILURE;
4339
0
      }
4340
0
      msSetOutputFormatOption(format, "JPEG_QUALITY", value);
4341
0
    } else if (EQUAL(key, "geotiff:predictor") && is_geotiff) {
4342
      /* PREDICTOR=[None/Horizontal/FloatingPoint] */
4343
0
      const char *predictor;
4344
0
      if (EQUAL(value, "None") || EQUAL(value, "1")) {
4345
0
        predictor = "1";
4346
0
      } else if (EQUAL(value, "Horizontal") || EQUAL(value, "2")) {
4347
0
        predictor = "2";
4348
0
      } else if (EQUAL(value, "FloatingPoint") || EQUAL(value, "3")) {
4349
0
        predictor = "3";
4350
0
      } else {
4351
0
        msSetError(MS_WCSERR, "Invalid predictor value '%s'.",
4352
0
                   "msWCSSetFormatParams20()", value);
4353
0
        return MS_FAILURE;
4354
0
      }
4355
0
      msSetOutputFormatOption(format, "PREDICTOR", predictor);
4356
0
    } else if (EQUAL(key, "geotiff:interleave") && is_geotiff) {
4357
      /* INTERLEAVE=[BAND,PIXEL] */
4358
0
      if (EQUAL(value, "Band")) {
4359
0
        msSetOutputFormatOption(format, "INTERLEAVE", "BAND");
4360
0
      } else if (EQUAL(value, "Pixel")) {
4361
0
        msSetOutputFormatOption(format, "INTERLEAVE", "PIXEL");
4362
0
      } else {
4363
0
        msSetError(MS_WCSERR, "Interleave method '%s' not supported.",
4364
0
                   "msWCSSetFormatParams20()", value);
4365
0
        return MS_FAILURE;
4366
0
      }
4367
0
    } else if (EQUAL(key, "geotiff:tiling") && is_geotiff) {
4368
      /* TILED=YES */
4369
0
      if (EQUAL(value, "true")) {
4370
0
        msSetOutputFormatOption(format, "TILED", "YES");
4371
0
      } else if (EQUAL(value, "false")) {
4372
0
        msSetOutputFormatOption(format, "TILED", "NO");
4373
0
      } else {
4374
0
        msSetError(MS_WCSERR, "Invalid boolean value '%s'.",
4375
0
                   "msWCSSetFormatParams20()", value);
4376
0
        return MS_FAILURE;
4377
0
      }
4378
0
    } else if (EQUAL(key, "geotiff:tileheight") && is_geotiff) {
4379
      /* BLOCKXSIZE=n */
4380
0
      int tileheight;
4381
4382
0
      if (msStringParseInteger(value, &tileheight) != MS_SUCCESS) {
4383
0
        msSetError(MS_WCSERR, "Could not parse tileheight value.",
4384
0
                   "msWCSSetFormatParams20()");
4385
0
        return MS_FAILURE;
4386
0
      } else if (tileheight < 1 || tileheight % 16) {
4387
0
        msSetError(MS_WCSERR,
4388
0
                   "Invalid tileheight value '%d'. "
4389
0
                   "Must be greater than 0 and dividable by 16.",
4390
0
                   "msWCSSetFormatParams20()", tileheight);
4391
0
        return MS_FAILURE;
4392
0
      }
4393
0
      msSetOutputFormatOption(format, "BLOCKXSIZE", value);
4394
0
    } else if (EQUAL(key, "geotiff:tilewidth") && is_geotiff) {
4395
      /* BLOCKYSIZE=n */
4396
0
      int tilewidth;
4397
4398
0
      if (msStringParseInteger(value, &tilewidth) != MS_SUCCESS) {
4399
0
        msSetError(MS_WCSERR, "Could not parse tilewidth value.",
4400
0
                   "msWCSSetFormatParams20()");
4401
0
        return MS_FAILURE;
4402
0
      } else if (tilewidth < 1 || tilewidth % 16) {
4403
0
        msSetError(MS_WCSERR,
4404
0
                   "Invalid tilewidth value '%d'. "
4405
0
                   "Must be greater than 0 and dividable by 16.",
4406
0
                   "msWCSSetFormatParams20()", tilewidth);
4407
0
        return MS_FAILURE;
4408
0
      }
4409
0
      msSetOutputFormatOption(format, "BLOCKYSIZE", value);
4410
0
    } else if (EQUALN(key, "geotiff:", 8)) {
4411
0
      msSetError(MS_WCSERR, "Unrecognized GeoTIFF parameter '%s'.",
4412
0
                 "msWCSSetFormatParams20()", key);
4413
0
      return MS_FAILURE;
4414
0
    }
4415
4416
0
    msFree(key);
4417
4418
    /* fetch next option */
4419
0
    format_option = format_options[++i];
4420
0
  }
4421
4422
0
  return MS_SUCCESS;
4423
0
}
4424
4425
/************************************************************************/
4426
/*                   msWCSGetCoverage20()                               */
4427
/*                                                                      */
4428
/*      Implementation of the GetCoverage Operation. The coverage       */
4429
/*      is either returned as an image or as a multipart xml/image.     */
4430
/*      The result is written on the stream.                            */
4431
/************************************************************************/
4432
4433
int msWCSGetCoverage20(mapObj *map, cgiRequestObj *request,
4434
0
                       wcs20ParamsObjPtr params, owsRequestObj *ows_request) {
4435
0
  (void)request;
4436
0
  layerObj *layer = NULL;
4437
0
  wcs20coverageMetadataObj cm;
4438
0
  imageObj *image = NULL;
4439
0
  outputFormatObj *format = NULL;
4440
4441
0
  rectObj subsets, bbox;
4442
0
  projectionObj imageProj;
4443
4444
0
  int status, i;
4445
0
  double x_1, x_2, y_1, y_2;
4446
0
  char *coverageName, *bandlist = NULL, numbands[12];
4447
4448
0
  int doDrawRasterLayerDraw = MS_TRUE;
4449
0
  GDALDatasetH hDS = NULL;
4450
4451
0
  int widthFromComputationInImageCRS = 0;
4452
0
  int heightFromComputationInImageCRS = 0;
4453
4454
  /* number of coverage ids should be 1 */
4455
0
  if (params->ids == NULL || params->ids[0] == NULL) {
4456
0
    msSetError(MS_WCSERR, "Required parameter CoverageID was not supplied.",
4457
0
               "msWCSGetCoverage20()");
4458
0
    return msWCSException(map, "MissingParameterValue", "coverage",
4459
0
                          params->version);
4460
0
  }
4461
0
  if (params->ids[1] != NULL) {
4462
0
    msSetError(MS_WCSERR, "GetCoverage operation supports only one coverage.",
4463
0
               "msWCSGetCoverage20()");
4464
0
    return msWCSException(map, "TooManyParameterValues", "coverage",
4465
0
                          params->version);
4466
0
  }
4467
4468
  /* find the right layer */
4469
0
  layer = NULL;
4470
0
  for (i = 0; i < map->numlayers; i++) {
4471
0
    coverageName = msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO",
4472
0
                                          "name", GET_LAYER(map, i)->name);
4473
0
    if (EQUAL(coverageName, params->ids[0]) &&
4474
0
        (msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers,
4475
0
                          ows_request->numlayers))) {
4476
0
      layer = GET_LAYER(map, i);
4477
0
      i = map->numlayers; /* to exit loop don't use break, we want to free
4478
                             resources first */
4479
0
    }
4480
0
    msFree(coverageName);
4481
0
  }
4482
4483
  /* throw exception if no Layer was found */
4484
0
  if (layer == NULL) {
4485
0
    msSetError(
4486
0
        MS_WCSERR,
4487
0
        "COVERAGEID=%s not found, not in supported layer list. A layer might be disabled for \
4488
0
this request. Check wcs/ows_enable_request settings.",
4489
0
        "msWCSGetCoverage20()", params->ids[0]);
4490
0
    return msWCSException(map, "NoSuchCoverage", "coverageid", params->version);
4491
0
  }
4492
  /* retrieve coverage metadata  */
4493
0
  status = msWCSGetCoverageMetadata20(layer, &cm);
4494
0
  if (status != MS_SUCCESS) {
4495
0
    msWCSClearCoverageMetadata20(&cm);
4496
0
    return MS_FAILURE;
4497
0
  }
4498
4499
  /* fill in bands rangeset info, if required.  */
4500
  /* msWCSSetDefaultBandsRangeSetInfo(NULL, &cm, layer ); */
4501
4502
  /* set  resolution, size and maximum extent */
4503
0
  layer->extent = map->extent = cm.extent;
4504
0
  map->cellsize = cm.xresolution;
4505
0
  map->width = cm.xsize;
4506
0
  map->height = cm.ysize;
4507
4508
  /************************************************************************/
4509
  /*      finalize the params object. determine subset crs and subset     */
4510
  /*      bbox. Also project the image to the subset crs.                 */
4511
  /************************************************************************/
4512
4513
0
  msInitProjection(&imageProj);
4514
0
  msProjectionInheritContextFrom(&imageProj, &(layer->projection));
4515
0
  if (msLoadProjectionString(&imageProj, cm.srs_epsg) == -1) {
4516
0
    msFreeProjection(&imageProj);
4517
0
    msWCSClearCoverageMetadata20(&cm);
4518
0
    msSetError(MS_WCSERR, "Error loading CRS %s.", "msWCSGetCoverage20()",
4519
0
               cm.srs_epsg);
4520
0
    return msWCSException(map, "InvalidParameterValue", "projection",
4521
0
                          params->version);
4522
0
  }
4523
4524
  /* iterate over all subsets and check if they are valid*/
4525
0
  for (i = 0; i < params->numaxes; ++i) {
4526
0
    if (params->axes[i]->subset != NULL) {
4527
0
      if (params->axes[i]->subset->timeOrScalar == MS_WCS20_TIME_VALUE) {
4528
0
        msFreeProjection(&imageProj);
4529
0
        msWCSClearCoverageMetadata20(&cm);
4530
0
        msSetError(MS_WCSERR, "Time values for subsets are not supported. ",
4531
0
                   "msWCSGetCoverage20()");
4532
0
        return msWCSException(map, "InvalidSubsetting", "subset",
4533
0
                              params->version);
4534
0
      }
4535
0
      if (params->axes[i]->subset->operation == MS_WCS20_SLICE) {
4536
0
        msFreeProjection(&imageProj);
4537
0
        msWCSClearCoverageMetadata20(&cm);
4538
0
        msSetError(MS_WCSERR, "Subset operation 'slice' is not supported.",
4539
0
                   "msWCSGetCoverage20()");
4540
0
        return msWCSException(map, "InvalidSubsetting", "subset",
4541
0
                              params->version);
4542
0
      }
4543
0
    }
4544
0
  }
4545
4546
0
  {
4547
0
    wcs20AxisObjPtr axes[2];
4548
0
    if (msWCSValidateAndFindAxes20(params, axes) == MS_FAILURE) {
4549
0
      msFreeProjection(&imageProj);
4550
0
      msWCSClearCoverageMetadata20(&cm);
4551
0
      return msWCSException(map, "InvalidAxisLabel", "subset", params->version);
4552
0
    }
4553
0
    if (msWCSGetCoverage20_FinalizeParamsObj(params, axes) == MS_FAILURE) {
4554
0
      msFreeProjection(&imageProj);
4555
0
      msWCSClearCoverageMetadata20(&cm);
4556
0
      return msWCSException(map, "InvalidParameterValue", "extent",
4557
0
                            params->version);
4558
0
    }
4559
0
  }
4560
4561
0
  subsets = params->bbox;
4562
4563
  /* if no subsetCRS was specified use the coverages CRS
4564
     (Requirement 27 of the WCS 2.0 specification) */
4565
0
  if (!params->subsetcrs) {
4566
0
    params->subsetcrs = msStrdup(cm.srs_epsg);
4567
0
  }
4568
4569
0
  if (EQUAL(params->subsetcrs, "imageCRS")) {
4570
    /* subsets are in imageCRS; reproject them to real coordinates */
4571
0
    rectObj orig_bbox = subsets;
4572
4573
0
    msFreeProjection(&(map->projection));
4574
0
    map->projection = imageProj;
4575
4576
0
    if (subsets.minx != -DBL_MAX || subsets.maxx != DBL_MAX) {
4577
0
      x_1 = cm.geotransform[0] + orig_bbox.minx * cm.geotransform[1] +
4578
0
            orig_bbox.miny * cm.geotransform[2];
4579
      // below +1 look suspicious to me (E. Rouault) and probably mean that
4580
      // there are still issues with pixel-center vs pixel-corner convention
4581
      // The resolution computed on wcs_20_post_getcov_trim_x_y_both_1px
4582
      // with that is 11, whereas it should be 10
4583
0
      x_2 = cm.geotransform[0] + (orig_bbox.maxx + 1) * cm.geotransform[1] +
4584
0
            (orig_bbox.maxy + 1) * cm.geotransform[2];
4585
4586
0
      subsets.minx = MS_MIN(x_1, x_2);
4587
0
      subsets.maxx = MS_MAX(x_1, x_2);
4588
0
    }
4589
0
    if (subsets.miny != -DBL_MAX || subsets.maxy != DBL_MAX) {
4590
      // below +1 are suspicious. See bove comment in the minx/maxx cases
4591
0
      y_1 = cm.geotransform[3] + (orig_bbox.maxx + 1) * cm.geotransform[4] +
4592
0
            (orig_bbox.maxy + 1) * cm.geotransform[5];
4593
      /*subsets.miny -= cm.geotransform[4]/2 + cm.geotransform[5]/2;*/
4594
0
      y_2 = cm.geotransform[3] + orig_bbox.minx * cm.geotransform[4] +
4595
0
            orig_bbox.miny * cm.geotransform[5];
4596
4597
0
      subsets.miny = MS_MIN(y_1, y_2);
4598
0
      subsets.maxy = MS_MAX(y_1, y_2);
4599
0
    }
4600
0
  } else { /* if crs is not the 'imageCRS' */
4601
0
    projectionObj subsetProj;
4602
4603
    /* if the subsets have a crs given, project the image extent to it */
4604
0
    msInitProjection(&subsetProj);
4605
0
    msProjectionInheritContextFrom(&subsetProj, &(layer->projection));
4606
0
    if (msLoadProjectionString(&subsetProj, params->subsetcrs) != MS_SUCCESS) {
4607
0
      msFreeProjection(&subsetProj);
4608
0
      msFreeProjection(&imageProj);
4609
0
      msWCSClearCoverageMetadata20(&cm);
4610
0
      msSetError(MS_WCSERR, "Error loading CRS %s.", "msWCSGetCoverage20()",
4611
0
                 params->subsetcrs);
4612
0
      return msWCSException(map, "InvalidParameterValue", "projection",
4613
0
                            params->version);
4614
0
    }
4615
4616
0
    if (msProjectionsDiffer(&imageProj, &subsetProj)) {
4617
4618
      /* Reprojection of source raster extent of (-180,-90,180,90) to any */
4619
      /* projected CRS is going to exhibit strong anomalies. So instead */
4620
      /* do the reverse, project the subset extent to the layer CRS, and */
4621
      /* see how much the subset extent takes with respect to the source */
4622
      /* raster extent. This is only used if output width and resolutionX (or */
4623
      /* (height and resolutionY) are unknown. */
4624
0
      if (((params->width == 0 && params->resolutionX == MS_WCS20_UNBOUNDED) ||
4625
0
           (params->height == 0 &&
4626
0
            params->resolutionY == MS_WCS20_UNBOUNDED)) &&
4627
0
          (msProjIsGeographicCRS(&imageProj) &&
4628
0
           !msProjIsGeographicCRS(&subsetProj) &&
4629
0
           fabs(layer->extent.minx - -180.0) < 1e-5 &&
4630
0
           fabs(layer->extent.miny - -90.0) < 1e-5 &&
4631
0
           fabs(layer->extent.maxx - 180.0) < 1e-5 &&
4632
0
           fabs(layer->extent.maxy - 90.0) < 1e-5)) {
4633
0
        rectObj subsetInImageProj = subsets;
4634
0
        if (msProjectRect(&subsetProj, &imageProj, &(subsetInImageProj)) ==
4635
0
            MS_SUCCESS) {
4636
0
          subsetInImageProj.minx =
4637
0
              MS_MAX(subsetInImageProj.minx, layer->extent.minx);
4638
0
          subsetInImageProj.miny =
4639
0
              MS_MAX(subsetInImageProj.miny, layer->extent.miny);
4640
0
          subsetInImageProj.maxx =
4641
0
              MS_MIN(subsetInImageProj.maxx, layer->extent.maxx);
4642
0
          subsetInImageProj.maxy =
4643
0
              MS_MIN(subsetInImageProj.maxy, layer->extent.maxy);
4644
0
          {
4645
0
            double total = std::abs(layer->extent.maxx - layer->extent.minx);
4646
0
            double part =
4647
0
                std::abs(subsetInImageProj.maxx - subsetInImageProj.minx);
4648
0
            widthFromComputationInImageCRS =
4649
0
                MS_NINT((part * map->width) / total);
4650
0
          }
4651
0
          {
4652
0
            double total = std::abs(layer->extent.maxy - layer->extent.miny);
4653
0
            double part =
4654
0
                std::abs(subsetInImageProj.maxy - subsetInImageProj.miny);
4655
0
            heightFromComputationInImageCRS =
4656
0
                MS_NINT((part * map->height) / total);
4657
0
          }
4658
0
        }
4659
0
      }
4660
4661
0
      msProjectRect(&imageProj, &subsetProj, &(layer->extent));
4662
0
      if (msProjIsGeographicCRS(&subsetProj)) {
4663
0
        if (layer->extent.minx > 180 && subsets.maxx <= 180) {
4664
0
          layer->extent.minx -= 360;
4665
0
          layer->extent.maxx -= 360;
4666
0
        } else if (layer->extent.maxx < -180 && subsets.minx >= -180) {
4667
0
          layer->extent.minx += 360;
4668
0
          layer->extent.maxx += 360;
4669
0
        }
4670
0
      }
4671
0
      map->extent = layer->extent;
4672
0
      msFreeProjection(&(map->projection));
4673
0
      map->projection = subsetProj;
4674
0
      msFreeProjection(&imageProj);
4675
0
    } else {
4676
0
      msFreeProjection(&(map->projection));
4677
0
      map->projection = imageProj;
4678
0
      msFreeProjection(&subsetProj);
4679
0
    }
4680
0
  }
4681
4682
  /* create boundings of params subsets and image extent */
4683
0
  if (msRectOverlap(&subsets, &(layer->extent)) == MS_FALSE) {
4684
    /* extent and bbox do not overlap -> exit */
4685
0
    msWCSClearCoverageMetadata20(&cm);
4686
0
    msSetError(MS_WCSERR,
4687
0
               "Image extent does not intersect with desired region.",
4688
0
               "msWCSGetCoverage20()");
4689
0
    return msWCSException(map, "ExtentError", "extent", params->version);
4690
0
  }
4691
4692
  /* write combined bounding box */
4693
0
  bbox.minx = MS_MAX(subsets.minx, map->extent.minx);
4694
0
  bbox.miny = MS_MAX(subsets.miny, map->extent.miny);
4695
0
  bbox.maxx = MS_MIN(subsets.maxx, map->extent.maxx);
4696
0
  bbox.maxy = MS_MIN(subsets.maxy, map->extent.maxy);
4697
4698
  /* check if we are overspecified  */
4699
0
  if ((params->width != 0 && params->resolutionX != MS_WCS20_UNBOUNDED) ||
4700
0
      (params->height != 0 && params->resolutionY != MS_WCS20_UNBOUNDED)) {
4701
0
    msWCSClearCoverageMetadata20(&cm);
4702
0
    msSetError(MS_WCSERR,
4703
0
               "GetCoverage operation supports only one of SIZE or RESOLUTION "
4704
0
               "per axis.",
4705
0
               "msWCSGetCoverage20()");
4706
0
    return msWCSException(map, "TooManyParameterValues", "coverage",
4707
0
                          params->version);
4708
0
  }
4709
4710
  /************************************************************************/
4711
  /* check both axes: see if either size or resolution are given (and     */
4712
  /* calculate the other value). If both are not given, calculate them    */
4713
  /* from the bounding box.                                               */
4714
  /************************************************************************/
4715
4716
  /* check x axis */
4717
0
  if (params->width != 0) {
4718
    /* TODO Unit Of Measure? */
4719
0
    params->resolutionX = (bbox.maxx - bbox.minx) / params->width;
4720
0
  } else if (params->resolutionX != MS_WCS20_UNBOUNDED) {
4721
0
    params->width = MS_NINT((bbox.maxx - bbox.minx) / params->resolutionX);
4722
0
  } else {
4723
0
    if (widthFromComputationInImageCRS != 0) {
4724
0
      params->width = widthFromComputationInImageCRS;
4725
0
    } else if (std::abs(bbox.maxx - bbox.minx) !=
4726
0
               std::abs(map->extent.maxx - map->extent.minx)) {
4727
0
      double total = std::abs(map->extent.maxx - map->extent.minx),
4728
0
             part = std::abs(bbox.maxx - bbox.minx);
4729
0
      params->width = MS_NINT((part * map->width) / total);
4730
0
    } else {
4731
0
      params->width = map->width;
4732
0
    }
4733
4734
0
    params->resolutionX = (bbox.maxx - bbox.minx) / params->width;
4735
4736
0
    if (params->scaleX != MS_WCS20_UNBOUNDED) {
4737
0
      params->resolutionX /= params->scaleX;
4738
0
      params->width = (long)(((double)params->width) * params->scaleX);
4739
0
    }
4740
0
  }
4741
4742
  /* check y axis */
4743
0
  if (params->height != 0) {
4744
0
    params->resolutionY = (bbox.maxy - bbox.miny) / params->height;
4745
0
  } else if (params->resolutionY != MS_WCS20_UNBOUNDED) {
4746
0
    params->height = MS_NINT((bbox.maxy - bbox.miny) / params->resolutionY);
4747
0
  } else {
4748
0
    if (heightFromComputationInImageCRS != 0) {
4749
0
      params->height = heightFromComputationInImageCRS;
4750
0
    } else if (std::abs(bbox.maxy - bbox.miny) !=
4751
0
               std::abs(map->extent.maxy - map->extent.miny)) {
4752
0
      double total = std::abs(map->extent.maxy - map->extent.miny),
4753
0
             part = std::abs(bbox.maxy - bbox.miny);
4754
0
      params->height = MS_NINT((part * map->height) / total);
4755
0
    } else {
4756
0
      params->height = map->height;
4757
0
    }
4758
4759
0
    params->resolutionY = (bbox.maxy - bbox.miny) / params->height;
4760
4761
0
    if (params->scaleY != MS_WCS20_UNBOUNDED) {
4762
0
      params->resolutionY /= params->scaleY;
4763
0
      params->height = (long)(((double)params->height) * params->scaleY);
4764
0
    }
4765
0
  }
4766
4767
  /* WCS 2.0 is center of pixel oriented */
4768
0
  rectObj bboxOriginIsCorner = bbox;
4769
0
  bbox.minx += params->resolutionX * 0.5;
4770
0
  bbox.maxx -= params->resolutionX * 0.5;
4771
0
  bbox.miny += params->resolutionY * 0.5;
4772
0
  bbox.maxy -= params->resolutionY * 0.5;
4773
4774
  /* if parameter 'outputcrs' is given, project the image to this crs */
4775
0
  if (params->outputcrs != NULL) {
4776
0
    projectionObj outputProj;
4777
4778
0
    msInitProjection(&outputProj);
4779
0
    msProjectionInheritContextFrom(&outputProj, &(layer->projection));
4780
0
    if (msLoadProjectionString(&outputProj, params->outputcrs) == -1) {
4781
0
      msFreeProjection(&outputProj);
4782
0
      msWCSClearCoverageMetadata20(&cm);
4783
0
      return msWCSException(map, "InvalidParameterValue", "coverage",
4784
0
                            params->version);
4785
0
    }
4786
0
    if (msProjectionsDiffer(&(map->projection), &outputProj)) {
4787
4788
0
      msDebug("msWCSGetCoverage20(): projecting to outputcrs %s\n",
4789
0
              params->outputcrs);
4790
4791
0
      msProjectRect(&(map->projection), &outputProj, &bbox);
4792
4793
      /* recalculate resolutions, needed if UOM changes (e.g: deg -> m) */
4794
0
      if (params->width == 1 || params->height == 1) {
4795
        // if one of the dimension is 1, we cannot use the center-of-pixel bbox,
4796
        // but must use the origin-is-corner one.
4797
0
        msProjectRect(&(map->projection), &outputProj, &bboxOriginIsCorner);
4798
0
        params->resolutionX =
4799
0
            (bboxOriginIsCorner.maxx - bboxOriginIsCorner.minx) / params->width;
4800
0
        params->resolutionY =
4801
0
            (bboxOriginIsCorner.maxy - bboxOriginIsCorner.miny) /
4802
0
            params->height;
4803
0
      } else {
4804
0
        params->resolutionX = (bbox.maxx - bbox.minx) / (params->width - 1);
4805
0
        params->resolutionY = (bbox.maxy - bbox.miny) / (params->height - 1);
4806
0
      }
4807
4808
0
      msFreeProjection(&(map->projection));
4809
0
      map->projection = outputProj;
4810
0
    } else {
4811
0
      msFreeProjection(&outputProj);
4812
0
    }
4813
0
  }
4814
4815
  /* set the bounding box as new map extent */
4816
0
  map->extent = bbox;
4817
0
  map->width = params->width;
4818
0
  map->height = params->height;
4819
4820
  /* Are we exceeding the MAXSIZE limit on result size? */
4821
0
  if (map->width > map->maxsize || map->height > map->maxsize) {
4822
0
    msWCSClearCoverageMetadata20(&cm);
4823
0
    msSetError(MS_WCSERR,
4824
0
               "Raster size out of range, width and height of "
4825
0
               "resulting coverage must be no more than MAXSIZE=%d.",
4826
0
               "msWCSGetCoverage20()", map->maxsize);
4827
4828
0
    return msWCSException(map, "InvalidParameterValue", "size",
4829
0
                          params->version);
4830
0
  }
4831
4832
  /* Mapserver only supports square cells */
4833
0
  if (params->resolutionX <= params->resolutionY)
4834
0
    map->cellsize = params->resolutionX;
4835
0
  else
4836
0
    map->cellsize = params->resolutionY;
4837
4838
0
  msDebug("msWCSGetCoverage20(): Set parameters from original"
4839
0
          "data. Width: %d, height: %d, cellsize: %f, extent: %f,%f,%f,%f\n",
4840
0
          map->width, map->height, map->cellsize, map->extent.minx,
4841
0
          map->extent.miny, map->extent.maxx, map->extent.maxy);
4842
4843
  /**
4844
   * Which format to use?
4845
   *
4846
   * 1) format parameter
4847
   * 2) native format (from metadata) or GDAL format of the input dataset
4848
   * 3) exception
4849
   **/
4850
4851
0
  if (!params->format) {
4852
0
    if (cm.native_format) {
4853
0
      params->format = msStrdup(cm.native_format);
4854
0
    }
4855
0
  }
4856
4857
0
  if (!params->format) {
4858
0
    msSetError(MS_WCSERR,
4859
0
               "Output format could not be automatically determined. "
4860
0
               "Use the FORMAT parameter to specify a format.",
4861
0
               "msWCSGetCoverage20()");
4862
0
    msWCSClearCoverageMetadata20(&cm);
4863
0
    return msWCSException(map, "MissingParameterValue", "format",
4864
0
                          params->version);
4865
0
  }
4866
4867
  /*    make sure layer is on   */
4868
0
  layer->status = MS_ON;
4869
4870
0
  if (params->width == 1 || params->height == 1)
4871
0
    msMapComputeGeotransformEx(map, params->resolutionX, params->resolutionY);
4872
0
  else
4873
0
    msMapComputeGeotransform(map);
4874
4875
  /*    fill in bands rangeset info, if required.  */
4876
  /* msWCSSetDefaultBandsRangeSetInfo(params, &cm, layer); */
4877
  /* msDebug("Bandcount: %d\n", cm.bandcount); */
4878
4879
0
  msApplyDefaultOutputFormats(map);
4880
4881
0
  if (msGetOutputFormatIndex(map, params->format) == -1) {
4882
0
    msSetError(MS_WCSERR, "Unrecognized value '%s' for the FORMAT parameter.",
4883
0
               "msWCSGetCoverage20()", params->format);
4884
0
    msWCSClearCoverageMetadata20(&cm);
4885
0
    return msWCSException(map, "InvalidParameterValue", "format",
4886
0
                          params->version);
4887
0
  }
4888
4889
  /* create a temporary outputformat (we likely will need to tweak parts) */
4890
0
  format = msCloneOutputFormat(msSelectOutputFormat(map, params->format));
4891
0
  msApplyOutputFormat(&(map->outputformat), format, MS_NOOVERRIDE);
4892
4893
  /* set format specific parameters */
4894
0
  if (msWCSSetFormatParams20(format, params->format_options) != MS_SUCCESS) {
4895
0
    msFree(bandlist);
4896
0
    msWCSClearCoverageMetadata20(&cm);
4897
0
    return msWCSException(map, "InvalidParameterValue", "format",
4898
0
                          params->version);
4899
0
  }
4900
4901
0
  if (msWCSGetCoverage20_GetBands(layer, params, &cm, &bandlist) !=
4902
0
      MS_SUCCESS) {
4903
0
    msFree(bandlist);
4904
0
    msWCSClearCoverageMetadata20(&cm);
4905
0
    return msWCSException(map, "InvalidParameterValue", "rangesubset",
4906
0
                          params->version);
4907
0
  }
4908
0
  msLayerSetProcessingKey(layer, "BANDS", bandlist);
4909
0
  snprintf(numbands, sizeof(numbands), "%d", msCountChars(bandlist, ',') + 1);
4910
0
  msSetOutputFormatOption(map->outputformat, "BAND_COUNT", numbands);
4911
4912
0
  msWCSApplyLayerCreationOptions(layer, map->outputformat, bandlist);
4913
0
  msWCSApplyLayerMetadataItemOptions(layer, map->outputformat, bandlist);
4914
4915
  /* check for the interpolation */
4916
  /* Defaults to NEAREST */
4917
0
  if (params->interpolation != NULL) {
4918
0
    if (EQUALN(params->interpolation, "NEAREST", 7)) {
4919
0
      msLayerSetProcessingKey(layer, "RESAMPLE", "NEAREST");
4920
0
    } else if (EQUAL(params->interpolation, "BILINEAR")) {
4921
0
      msLayerSetProcessingKey(layer, "RESAMPLE", "BILINEAR");
4922
0
    } else if (EQUAL(params->interpolation, "AVERAGE")) {
4923
0
      msLayerSetProcessingKey(layer, "RESAMPLE", "AVERAGE");
4924
0
    } else {
4925
0
      msFree(bandlist);
4926
0
      msSetError(MS_WCSERR,
4927
0
                 "'%s' specifies an unsupported interpolation method.",
4928
0
                 "msWCSGetCoverage20()", params->interpolation);
4929
0
      msWCSClearCoverageMetadata20(&cm);
4930
0
      return msWCSException(map, "InvalidParameterValue", "interpolation",
4931
0
                            params->version);
4932
0
    }
4933
0
  } else {
4934
0
    msLayerSetProcessingKey(layer, "RESAMPLE", "NEAREST");
4935
0
  }
4936
4937
  /* since the dataset is only used in one layer, set it to be    */
4938
  /* closed after drawing the layer. This normally defaults to    */
4939
  /* DEFER and will produce a memory leak, because the dataset    */
4940
  /* will not be closed.                                          */
4941
0
  if (msLayerGetProcessingKey(layer, "CLOSE_CONNECTION") == NULL) {
4942
0
    msLayerSetProcessingKey(layer, "CLOSE_CONNECTION", "NORMAL");
4943
0
  }
4944
4945
0
  if (layer->tileindex == NULL && layer->data != NULL &&
4946
0
      strlen(layer->data) > 0 && layer->connectiontype != MS_KERNELDENSITY) {
4947
0
    if (msDrawRasterLayerLowCheckIfMustDraw(map, layer)) {
4948
0
      char *decrypted_path = NULL;
4949
0
      char szPath[MS_MAXPATHLEN];
4950
0
      hDS = (GDALDatasetH)msDrawRasterLayerLowOpenDataset(
4951
0
          map, layer, layer->data, szPath, &decrypted_path);
4952
0
      msFree(decrypted_path);
4953
0
      if (hDS) {
4954
0
        msWCSApplyDatasetMetadataAsCreationOptions(layer, map->outputformat,
4955
0
                                                   bandlist, hDS);
4956
0
        msWCSApplySourceDatasetMetadata(layer, map->outputformat, bandlist,
4957
0
                                        hDS);
4958
0
      }
4959
0
    } else {
4960
0
      doDrawRasterLayerDraw = MS_FALSE;
4961
0
    }
4962
0
  }
4963
4964
  /* create the image object  */
4965
0
  if (MS_RENDERER_PLUGIN(map->outputformat)) {
4966
0
    image =
4967
0
        msImageCreate(map->width, map->height, map->outputformat,
4968
0
                      map->web.imagepath, map->web.imageurl, map->resolution,
4969
0
                      map->defresolution, &map->imagecolor);
4970
0
  } else if (MS_RENDERER_RAWDATA(map->outputformat)) {
4971
0
    image =
4972
0
        msImageCreate(map->width, map->height, map->outputformat,
4973
0
                      map->web.imagepath, map->web.imageurl, map->resolution,
4974
0
                      map->defresolution, &map->imagecolor);
4975
0
  } else {
4976
0
    msFree(bandlist);
4977
0
    msWCSClearCoverageMetadata20(&cm);
4978
0
    msSetError(MS_WCSERR, "Map outputformat not supported for WCS!",
4979
0
               "msWCSGetCoverage20()");
4980
0
    msDrawRasterLayerLowCloseDataset(layer, hDS);
4981
0
    return msWCSException(map, NULL, NULL, params->version);
4982
0
  }
4983
4984
0
  if (image == NULL) {
4985
0
    msFree(bandlist);
4986
0
    msWCSClearCoverageMetadata20(&cm);
4987
0
    msDrawRasterLayerLowCloseDataset(layer, hDS);
4988
0
    return msWCSException(map, NULL, NULL, params->version);
4989
0
  }
4990
4991
0
  if (layer->mask) {
4992
0
    int maskLayerIdx = msGetLayerIndex(map, layer->mask);
4993
0
    layerObj *maskLayer;
4994
0
    outputFormatObj *altFormat;
4995
0
    if (maskLayerIdx == -1) {
4996
0
      msSetError(MS_MISCERR, "Layer (%s) references unknown mask layer (%s)",
4997
0
                 "msDrawLayer()", layer->name, layer->mask);
4998
0
      msFreeImage(image);
4999
0
      msFree(bandlist);
5000
0
      msWCSClearCoverageMetadata20(&cm);
5001
0
      msDrawRasterLayerLowCloseDataset(layer, hDS);
5002
0
      return msWCSException(map, NULL, NULL, params->version);
5003
0
    }
5004
0
    maskLayer = GET_LAYER(map, maskLayerIdx);
5005
0
    if (!maskLayer->maskimage) {
5006
0
      int i, retcode;
5007
0
      int origstatus, origlabelcache;
5008
0
      char *origImageType = msStrdup(map->imagetype);
5009
0
      altFormat = msSelectOutputFormat(map, "png24");
5010
0
      msInitializeRendererVTable(altFormat);
5011
      /* TODO: check the png24 format hasn't been tampered with, i.e. it's agg
5012
       */
5013
0
      maskLayer->maskimage = msImageCreate(
5014
0
          image->width, image->height, altFormat, image->imagepath,
5015
0
          image->imageurl, map->resolution, map->defresolution, NULL);
5016
0
      if (!maskLayer->maskimage) {
5017
0
        msSetError(MS_MISCERR, "Unable to initialize mask image.",
5018
0
                   "msDrawLayer()");
5019
0
        msFreeImage(image);
5020
0
        msFree(bandlist);
5021
0
        msWCSClearCoverageMetadata20(&cm);
5022
0
        msDrawRasterLayerLowCloseDataset(layer, hDS);
5023
0
        return msWCSException(map, NULL, NULL, params->version);
5024
0
      }
5025
5026
      /*
5027
       * force the masked layer to status on, and turn off the labelcache so
5028
       * that eventual labels are added to the temporary image instead of being
5029
       * added to the labelcache
5030
       */
5031
0
      origstatus = maskLayer->status;
5032
0
      origlabelcache = maskLayer->labelcache;
5033
0
      maskLayer->status = MS_ON;
5034
0
      maskLayer->labelcache = MS_OFF;
5035
5036
      /* draw the mask layer in the temporary image */
5037
0
      retcode = msDrawLayer(map, maskLayer, maskLayer->maskimage);
5038
0
      maskLayer->status = origstatus;
5039
0
      maskLayer->labelcache = origlabelcache;
5040
0
      if (retcode != MS_SUCCESS) {
5041
0
        free(origImageType);
5042
0
        msFreeImage(image);
5043
0
        msFree(bandlist);
5044
0
        msWCSClearCoverageMetadata20(&cm);
5045
0
        msDrawRasterLayerLowCloseDataset(layer, hDS);
5046
0
        return msWCSException(map, NULL, NULL, params->version);
5047
0
      }
5048
      /*
5049
       * hack to work around bug #3834: if we have use an alternate renderer,
5050
       * the symbolset may contain symbols that reference it. We want to remove
5051
       * those references before the altFormat is destroyed to avoid a segfault
5052
       * and/or a leak, and so the the main renderer doesn't pick the cache up
5053
       * thinking it's for him.
5054
       */
5055
0
      for (i = 0; i < map->symbolset.numsymbols; i++) {
5056
0
        if (map->symbolset.symbol[i] != NULL) {
5057
0
          symbolObj *s = map->symbolset.symbol[i];
5058
0
          if (s->renderer == MS_IMAGE_RENDERER(maskLayer->maskimage)) {
5059
0
            MS_IMAGE_RENDERER(maskLayer->maskimage)->freeSymbol(s);
5060
0
            s->renderer = NULL;
5061
0
          }
5062
0
        }
5063
0
      }
5064
      /* set the imagetype from the original outputformat back (it was removed
5065
       * by msSelectOutputFormat() */
5066
0
      msFree(map->imagetype);
5067
0
      map->imagetype = origImageType;
5068
0
    }
5069
0
  }
5070
5071
  /* Actually produce the "grid". */
5072
0
  if (MS_RENDERER_RAWDATA(map->outputformat)) {
5073
0
    if (doDrawRasterLayerDraw) {
5074
0
      status = msDrawRasterLayerLowWithDataset(map, layer, image, NULL, hDS);
5075
0
    } else {
5076
0
      status = MS_SUCCESS;
5077
0
    }
5078
0
  } else {
5079
0
    rasterBufferObj rb;
5080
0
    status = MS_IMAGE_RENDERER(image)->getRasterBufferHandle(image, &rb);
5081
0
    if (MS_LIKELY(status == MS_SUCCESS)) {
5082
0
      if (doDrawRasterLayerDraw) {
5083
0
        status = msDrawRasterLayerLowWithDataset(map, layer, image, &rb, hDS);
5084
0
      } else {
5085
0
        status = MS_SUCCESS;
5086
0
      }
5087
0
    }
5088
0
  }
5089
5090
0
  msDrawRasterLayerLowCloseDataset(layer, hDS);
5091
5092
0
  if (status != MS_SUCCESS) {
5093
0
    msFree(bandlist);
5094
0
    msFreeImage(image);
5095
0
    msWCSClearCoverageMetadata20(&cm);
5096
0
    return msWCSException(map, NULL, NULL, params->version);
5097
0
  }
5098
5099
  /* GML+Image */
5100
  /* Embed the image into multipart message */
5101
0
  if (params->multipart == MS_TRUE) {
5102
0
    xmlDocPtr psDoc = NULL; /* document pointer */
5103
0
    xmlNodePtr psRootNode, psRangeSet, psFile, psRangeParameters;
5104
0
    xmlNsPtr psGmlNs = NULL, psGmlcovNs = NULL, psSweNs = NULL,
5105
0
             psXLinkNs = NULL;
5106
0
    wcs20coverageMetadataObj tmpCm;
5107
0
    char *srs_uri, *default_filename;
5108
0
    const char *filename;
5109
0
    int swapAxes;
5110
5111
    /* Create Document  */
5112
0
    psDoc = xmlNewDoc(BAD_CAST "1.0");
5113
0
    psRootNode = xmlNewNode(
5114
0
        NULL, BAD_CAST MS_WCS_GML_COVERAGETYPE_RECTIFIED_GRID_COVERAGE);
5115
0
    xmlDocSetRootElement(psDoc, psRootNode);
5116
5117
0
    msWCSPrepareNamespaces20(psDoc, psRootNode, map, MS_FALSE);
5118
5119
0
    psGmlNs = xmlSearchNs(psDoc, psRootNode,
5120
0
                          BAD_CAST MS_OWSCOMMON_GML_NAMESPACE_PREFIX);
5121
0
    psGmlcovNs = xmlSearchNs(psDoc, psRootNode,
5122
0
                             BAD_CAST MS_OWSCOMMON_GMLCOV_NAMESPACE_PREFIX);
5123
0
    psSweNs = xmlSearchNs(psDoc, psRootNode,
5124
0
                          BAD_CAST MS_OWSCOMMON_SWE_NAMESPACE_PREFIX);
5125
0
    xmlSearchNs(psDoc, psRootNode, BAD_CAST MS_OWSCOMMON_WCS_NAMESPACE_PREFIX);
5126
0
    psXLinkNs = xmlSearchNs(psDoc, psRootNode,
5127
0
                            BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_PREFIX);
5128
5129
0
    xmlNewNsProp(psRootNode, psGmlNs, BAD_CAST "id", BAD_CAST layer->name);
5130
5131
0
    xmlSetNs(psRootNode, psGmlcovNs);
5132
5133
0
    srs_uri = msOWSGetProjURI(&map->projection, NULL, "CO", 1);
5134
5135
0
    tmpCm = cm;
5136
0
    tmpCm.extent = map->extent;
5137
0
    tmpCm.xsize = map->width;
5138
0
    tmpCm.ysize = map->height;
5139
0
    strlcpy(tmpCm.srs_uri, srs_uri, sizeof(tmpCm.srs_uri));
5140
5141
0
    tmpCm.xresolution = map->gt.geotransform[1];
5142
0
    tmpCm.yresolution = map->gt.geotransform[5];
5143
5144
0
    tmpCm.extent.minx =
5145
0
        MS_MIN(map->gt.geotransform[0],
5146
0
               map->gt.geotransform[0] + map->width * tmpCm.xresolution);
5147
0
    tmpCm.extent.miny =
5148
0
        MS_MIN(map->gt.geotransform[3],
5149
0
               map->gt.geotransform[3] + map->height * tmpCm.yresolution);
5150
0
    tmpCm.extent.maxx =
5151
0
        MS_MAX(map->gt.geotransform[0],
5152
0
               map->gt.geotransform[0] + map->width * tmpCm.xresolution);
5153
0
    tmpCm.extent.maxy =
5154
0
        MS_MAX(map->gt.geotransform[3],
5155
0
               map->gt.geotransform[3] + map->height * tmpCm.yresolution);
5156
5157
0
    swapAxes = msWCSSwapAxes20(srs_uri);
5158
0
    msFree(srs_uri);
5159
5160
    /* Setup layer information  */
5161
0
    msWCSCommon20_CreateBoundedBy(&tmpCm, psGmlNs, psRootNode,
5162
0
                                  &(map->projection), swapAxes);
5163
0
    msWCSCommon20_CreateDomainSet(layer, &tmpCm, psGmlNs, psRootNode,
5164
0
                                  &(map->projection), swapAxes);
5165
5166
0
    psRangeSet = xmlNewChild(psRootNode, psGmlNs, BAD_CAST "rangeSet", NULL);
5167
0
    psFile = xmlNewChild(psRangeSet, psGmlNs, BAD_CAST "File", NULL);
5168
5169
    /* TODO: wait for updated specifications */
5170
0
    psRangeParameters =
5171
0
        xmlNewChild(psFile, psGmlNs, BAD_CAST "rangeParameters", NULL);
5172
5173
0
    default_filename = msStrdup("out.");
5174
0
    default_filename = msStringConcatenate(default_filename,
5175
0
                                           MS_IMAGE_EXTENSION(image->format));
5176
5177
0
    filename =
5178
0
        msGetOutputFormatOption(image->format, "FILENAME", default_filename);
5179
0
    std::string file_ref("cid:coverage/");
5180
0
    file_ref += filename;
5181
5182
0
    msFree(default_filename);
5183
5184
0
    std::string role;
5185
0
    if (EQUAL(MS_IMAGE_MIME_TYPE(map->outputformat), "image/tiff")) {
5186
0
      role = MS_WCS_20_PROFILE_GML_GEOTIFF;
5187
0
    } else {
5188
0
      role = MS_IMAGE_MIME_TYPE(map->outputformat);
5189
0
    }
5190
5191
0
    xmlNewNsProp(psRangeParameters, psXLinkNs, BAD_CAST "href",
5192
0
                 BAD_CAST file_ref.c_str());
5193
0
    xmlNewNsProp(psRangeParameters, psXLinkNs, BAD_CAST "role",
5194
0
                 BAD_CAST role.c_str());
5195
0
    xmlNewNsProp(psRangeParameters, psXLinkNs, BAD_CAST "arcrole",
5196
0
                 BAD_CAST "fileReference");
5197
5198
0
    xmlNewChild(psFile, psGmlNs, BAD_CAST "fileReference",
5199
0
                BAD_CAST file_ref.c_str());
5200
0
    xmlNewChild(psFile, psGmlNs, BAD_CAST "fileStructure", NULL);
5201
0
    xmlNewChild(psFile, psGmlNs, BAD_CAST "mimeType",
5202
0
                BAD_CAST MS_IMAGE_MIME_TYPE(map->outputformat));
5203
5204
0
    msWCSCommon20_CreateRangeType(&cm, bandlist, psGmlcovNs, psSweNs,
5205
0
                                  psRootNode);
5206
5207
0
    msIO_setHeader("Content-Type", "multipart/related; boundary=wcs");
5208
0
    msIO_sendHeaders();
5209
0
    msIO_printf("\r\n--wcs\r\n");
5210
5211
0
    msWCSWriteDocument20(psDoc);
5212
0
    msWCSWriteFile20(map, image, params, 1);
5213
5214
0
    xmlFreeDoc(psDoc);
5215
0
    xmlCleanupParser();
5216
    /* just print out the file without gml */
5217
0
  } else {
5218
0
    msWCSWriteFile20(map, image, params, 0);
5219
0
  }
5220
5221
0
  msFree(bandlist);
5222
0
  msWCSClearCoverageMetadata20(&cm);
5223
0
  msFreeImage(image);
5224
0
  return MS_SUCCESS;
5225
0
}
5226
5227
#endif /* defined(USE_LIBXML2) */
5228
5229
#endif /* defined(USE_WCS_SVR) */