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