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