/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) */ |