1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: OpenGIS Web Coverage Server (WCS) 1.1.0 Implementation. This
6 * file holds some WCS 1.1.0 specific functions but other parts
7 * are still implemented in mapwcs.c.
8 * Author: Frank Warmerdam and the MapServer team.
9 *
10 ******************************************************************************
11 * Copyright (c) 2007, Frank Warmerdam
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 * and/or sell copies of the Software, and to permit persons to whom the
18 * Software is furnished to do so, subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be included in
21 * all copies of this Software or works derived from this Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 * DEALINGS IN THE SOFTWARE.
30 *****************************************************************************/
31
32 #include <assert.h>
33 #include "mapserver.h"
34 #include "maperror.h"
35 #include "mapthread.h"
36 #include "mapows.h"
37 #include "mapwcs.h"
38 #include "mapgdal.h"
39
40
41 #if defined(USE_WCS_SVR)
42 #include "mapwcs.h"
43 #include "gdal.h"
44 #include "cpl_string.h" /* GDAL string handling */
45 #endif
46
47 #if defined(USE_LIBXML2)
48 #include "maplibxml2.h"
49 #endif
50
51 #if defined(USE_WCS_SVR) && defined(USE_LIBXML2)
52 /*
53 ** msWCSException11()
54 **
55 ** Report current MapServer error in XML exception format.
56 ** Wrapper function around msOWSCommonExceptionReport. Merely
57 ** passes WCS specific info.
58 **
59 */
60
msWCSException11(mapObj * map,const char * exceptionCode,const char * locator,const char * version)61 int msWCSException11(mapObj *map, const char *exceptionCode,
62 const char *locator, const char *version)
63 {
64 int size = 0;
65 char *errorString = NULL;
66 char *schemasLocation = NULL;
67
68 xmlDocPtr psDoc = NULL;
69 xmlNodePtr psRootNode = NULL;
70 xmlNsPtr psNsOws = NULL;
71 xmlChar *buffer = NULL;
72
73 psNsOws = xmlNewNs(NULL, BAD_CAST "http://www.opengis.net/ows/1.1", BAD_CAST "ows");
74
75 errorString = msGetErrorString("\n");
76 schemasLocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
77
78 psDoc = xmlNewDoc(BAD_CAST "1.0");
79
80 psRootNode = msOWSCommonExceptionReport(psNsOws, OWS_1_1_0, schemasLocation, version, msOWSGetLanguage(map, "exception"), exceptionCode, locator, errorString);
81
82 xmlDocSetRootElement(psDoc, psRootNode);
83
84 xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/ows/1.1", BAD_CAST "ows");
85
86 msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
87 msIO_sendHeaders();
88
89 xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, "UTF-8", 1);
90
91 msIO_printf("%s", buffer);
92
93 /*free buffer and the document */
94 free(errorString);
95 free(schemasLocation);
96 xmlFree(buffer);
97 xmlFreeDoc(psDoc);
98 xmlFreeNs(psNsOws);
99
100 /* clear error since we have already reported it */
101 msResetErrorList();
102
103 return MS_FAILURE;
104 }
105
106 /************************************************************************/
107 /* msWCSGetFormatsList11() */
108 /* */
109 /* Fetch back a comma delimited formats list for the past layer */
110 /* if one is supplied, otherwise for all formats supported by */
111 /* the server. Formats should be identified by mime type. */
112 /************************************************************************/
113
msWCSGetFormatsList11(mapObj * map,layerObj * layer)114 static char *msWCSGetFormatsList11( mapObj *map, layerObj *layer )
115
116 {
117 char *format_list = msStrdup("");
118 char **tokens = NULL, **formats = NULL;
119 int i, numtokens = 0, numformats;
120 char *value;
121
122 msApplyDefaultOutputFormats(map);
123
124 /* -------------------------------------------------------------------- */
125 /* Parse from layer metadata. */
126 /* -------------------------------------------------------------------- */
127 if( layer != NULL
128 && (value = msOWSGetEncodeMetadata( &(layer->metadata),"CO","formats",
129 "GTiff" )) != NULL ) {
130 tokens = msStringSplit(value, ' ', &numtokens);
131 msFree(value);
132 }
133
134 /* -------------------------------------------------------------------- */
135 /* Parse from map.web metadata. */
136 /* -------------------------------------------------------------------- */
137 else if((value = msOWSGetEncodeMetadata( &(map->web.metadata), "CO", "formats",
138 NULL)) != NULL ) {
139 tokens = msStringSplit(value, ' ', &numtokens);
140 msFree(value);
141 }
142
143 /* -------------------------------------------------------------------- */
144 /* Or generate from all configured raster output formats that */
145 /* look plausible. */
146 /* -------------------------------------------------------------------- */
147 else {
148 tokens = (char **) calloc(map->numoutputformats,sizeof(char*));
149 for( i = 0; i < map->numoutputformats; i++ ) {
150 switch( map->outputformatlist[i]->renderer ) {
151 /* seeminly normal raster format */
152 case MS_RENDER_WITH_AGG:
153 case MS_RENDER_WITH_RAWDATA:
154 tokens[numtokens++] = msStrdup(map->outputformatlist[i]->name);
155 break;
156
157 /* rest of formats aren't really WCS compatible */
158 default:
159 break;
160
161 }
162 }
163 }
164
165 /* -------------------------------------------------------------------- */
166 /* Convert outputFormatObj names into mime types and remove */
167 /* duplicates. */
168 /* -------------------------------------------------------------------- */
169 numformats = 0;
170 formats = (char **) calloc(sizeof(char*),numtokens);
171
172 for( i = 0; i < numtokens; i++ ) {
173 int format_i, j;
174 const char *mimetype;
175
176 for( format_i = 0; format_i < map->numoutputformats; format_i++ ) {
177 if( strcasecmp(map->outputformatlist[format_i]->name,
178 tokens[i]) == 0 )
179 break;
180 }
181
182
183 if( format_i == map->numoutputformats ) {
184 msDebug("Failed to find outputformat info on format '%s', ignore.\n",
185 tokens[i] );
186 continue;
187 }
188
189 mimetype = map->outputformatlist[format_i]->mimetype;
190 if( mimetype == NULL || strlen(mimetype) == 0 ) {
191 msDebug("No mimetime for format '%s', ignoring.\n",
192 tokens[i] );
193 continue;
194 }
195
196 for( j = 0; j < numformats; j++ ) {
197 if( strcasecmp(mimetype,formats[j]) == 0 )
198 break;
199 }
200
201 if( j < numformats ) {
202 msDebug( "Format '%s' ignored since mimetype '%s' duplicates another outputFormatObj.\n",
203 tokens[i], mimetype );
204 continue;
205 }
206
207 formats[numformats++] = msStrdup(mimetype);
208 }
209
210 msFreeCharArray(tokens,numtokens);
211
212 /* -------------------------------------------------------------------- */
213 /* Turn mimetype list into comma delimited form for easy use */
214 /* with xml functions. */
215 /* -------------------------------------------------------------------- */
216 for(i=0; i<numformats; i++) {
217 if(i > 0) {
218 format_list = msStringConcatenate(format_list, (char *) ",");
219 }
220 format_list = msStringConcatenate(format_list, formats[i]);
221 }
222 msFreeCharArray(formats,numformats);
223
224 return format_list;
225 }
226
227 /************************************************************************/
228 /* msWCSGetCapabilities11_CoverageSummary() */
229 /* */
230 /* Generate a WCS 1.1 CoverageSummary. */
231 /************************************************************************/
232
msWCSGetCapabilities11_CoverageSummary(mapObj * map,wcsParamsObj * params,cgiRequestObj * req,xmlDocPtr doc,xmlNodePtr psContents,layerObj * layer)233 static int msWCSGetCapabilities11_CoverageSummary(
234 mapObj *map, wcsParamsObj *params, cgiRequestObj *req,
235 xmlDocPtr doc, xmlNodePtr psContents, layerObj *layer )
236
237 {
238 coverageMetadataObj cm;
239 int status;
240 const char *value;
241 char *owned_value;
242 char *format_list;
243 xmlNodePtr psCSummary;
244 xmlNsPtr psOwsNs = xmlSearchNs( doc, psContents, BAD_CAST "ows" );
245 char **tokens = NULL;
246 int i = 0;
247 int n = 0;
248
249 status = msWCSGetCoverageMetadata(layer, &cm);
250 if(status != MS_SUCCESS) return MS_FAILURE;
251
252 psCSummary = xmlNewChild( psContents, NULL, BAD_CAST "CoverageSummary", NULL );
253
254 /* -------------------------------------------------------------------- */
255 /* Title (from description) */
256 /* -------------------------------------------------------------------- */
257 value = msOWSLookupMetadata( &(layer->metadata), "CO", "description");
258 if( value == NULL )
259 value = msOWSLookupMetadata( &(layer->metadata), "CO", "title");
260 if( value == NULL )
261 value = layer->name;
262 xmlNewChild( psCSummary, psOwsNs, BAD_CAST "Title", BAD_CAST value );
263
264 /* -------------------------------------------------------------------- */
265 /* Abstract */
266 /* -------------------------------------------------------------------- */
267 value = msOWSLookupMetadata( &(layer->metadata), "CO", "abstract");
268 xmlNewChild( psCSummary, psOwsNs, BAD_CAST "Abstract", BAD_CAST value );
269
270 /* -------------------------------------------------------------------- */
271 /* Keywords */
272 /* -------------------------------------------------------------------- */
273 value = msOWSLookupMetadata(&(layer->metadata), "CO", "keywordlist");
274
275 if (value) {
276 xmlNodePtr psNode;
277
278 psNode = xmlNewChild(psCSummary, psOwsNs, BAD_CAST "Keywords", NULL);
279
280 tokens = msStringSplit(value, ',', &n);
281 if (tokens && n > 0) {
282 for (i=0; i<n; i++) {
283 xmlNewChild(psNode, NULL, BAD_CAST "Keyword", BAD_CAST tokens[i] );
284 }
285 msFreeCharArray(tokens, n);
286 }
287 }
288
289 /* -------------------------------------------------------------------- */
290 /* Metadata Link */
291 /* -------------------------------------------------------------------- */
292 value = msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_href");
293
294 if (value) {
295 xmlNodePtr psMetadata = xmlNewChild(psCSummary, psOwsNs, BAD_CAST "Metadata", NULL);
296 xmlNsPtr psXlinkNs = xmlSearchNs( doc, xmlDocGetRootElement(doc), BAD_CAST "xlink" );
297 const char *metadatalink_type = msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_type");
298 const char *metadatalink_format = msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_format");
299
300 xmlNewNsProp(psMetadata, psXlinkNs, BAD_CAST "type", BAD_CAST "simple");
301 xmlNewNsProp(psMetadata, psXlinkNs, BAD_CAST "href", BAD_CAST value);
302 if (metadatalink_type != NULL) {
303 xmlNewProp(psMetadata, BAD_CAST "about", BAD_CAST metadatalink_type);
304 }
305 if (metadatalink_format != NULL) {
306 xmlNewNsProp(psMetadata, psXlinkNs, BAD_CAST "role", BAD_CAST metadatalink_format);
307 }
308 }
309
310 /* -------------------------------------------------------------------- */
311 /* WGS84 bounding box. */
312 /* -------------------------------------------------------------------- */
313 xmlAddChild(
314 psCSummary,
315 msOWSCommonWGS84BoundingBox( psOwsNs, 2,
316 cm.llextent.minx, cm.llextent.miny,
317 cm.llextent.maxx, cm.llextent.maxy ));
318
319 /* -------------------------------------------------------------------- */
320 /* Supported CRSes. */
321 /* -------------------------------------------------------------------- */
322 if( (owned_value =
323 msOWSGetProjURN( &(layer->projection), &(layer->metadata),
324 "CO", MS_FALSE)) != NULL ) {
325 /* ok */
326 } else if((owned_value =
327 msOWSGetProjURN( &(layer->map->projection),
328 &(layer->map->web.metadata),
329 "CO", MS_FALSE)) != NULL ) {
330 /* ok */
331 } else
332 msDebug( "mapwcs.c: missing required information, no SRSs defined.\n");
333
334 if( owned_value != NULL && strlen(owned_value) > 0 )
335 msLibXml2GenerateList( psCSummary, NULL, "SupportedCRS",
336 owned_value, ' ' );
337
338 msFree( owned_value );
339
340 /* -------------------------------------------------------------------- */
341 /* SupportedFormats */
342 /* -------------------------------------------------------------------- */
343 format_list = msWCSGetFormatsList11( map, layer );
344
345 if (strlen(format_list) > 0 )
346 msLibXml2GenerateList( psCSummary, NULL, "SupportedFormat",
347 format_list, ',' );
348
349 msFree( format_list );
350 msWCSFreeCoverageMetadata(&cm);
351
352 /* -------------------------------------------------------------------- */
353 /* Identifier (layer name) */
354 /* -------------------------------------------------------------------- */
355 xmlNewChild( psCSummary, NULL, BAD_CAST "Identifier", BAD_CAST layer->name );
356
357 return MS_SUCCESS;
358 }
359
360 /************************************************************************/
361 /* msWCSGetCapabilities11() */
362 /************************************************************************/
msWCSGetCapabilities11(mapObj * map,wcsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)363 int msWCSGetCapabilities11(mapObj *map, wcsParamsObj *params,
364 cgiRequestObj *req, owsRequestObj *ows_request)
365 {
366 xmlDocPtr psDoc = NULL; /* document pointer */
367 xmlNodePtr psRootNode, psMainNode, psNode;
368 char *identifier_list = NULL, *format_list = NULL;
369 const char *updatesequence=NULL;
370 xmlNsPtr psOwsNs, psXLinkNs;
371 char *schemaLocation = NULL;
372 char *xsi_schemaLocation = NULL;
373 char *script_url=NULL, *script_url_encoded=NULL;
374
375 xmlChar *buffer = NULL;
376 int size = 0, i;
377 msIOContext *context = NULL;
378
379 int ows_version = OWS_1_1_0;
380
381 /* -------------------------------------------------------------------- */
382 /* Handle updatesequence */
383 /* -------------------------------------------------------------------- */
384
385 updatesequence = msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
386
387 if (params->updatesequence != NULL) {
388 i = msOWSNegotiateUpdateSequence(params->updatesequence, updatesequence);
389 if (i == 0) { /* current */
390 msSetError(MS_WCSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)", "msWCSGetCapabilities11()", params->updatesequence, updatesequence);
391 return msWCSException11(map, "CurrentUpdateSequence", "updatesequence", params->version);
392 }
393 if (i > 0) { /* invalid */
394 msSetError(MS_WCSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)", "msWCSGetCapabilities11()", params->updatesequence, updatesequence);
395 return msWCSException11(map, "InvalidUpdateSequence", "updatesequence", params->version);
396 }
397 }
398
399 /* -------------------------------------------------------------------- */
400 /* Build list of layer identifiers available. */
401 /* -------------------------------------------------------------------- */
402 identifier_list = msStrdup("");
403 for(i=0; i<map->numlayers; i++) {
404 layerObj *layer = map->layers[i];
405 int new_length;
406
407 if(!msWCSIsLayerSupported(layer))
408 continue;
409
410 new_length = strlen(identifier_list) + strlen(layer->name) + 2;
411 identifier_list = (char *) realloc(identifier_list,new_length);
412
413 if( strlen(identifier_list) > 0 )
414 strcat( identifier_list, "," );
415 strcat( identifier_list, layer->name );
416 }
417
418 /* -------------------------------------------------------------------- */
419 /* Create document. */
420 /* -------------------------------------------------------------------- */
421 psDoc = xmlNewDoc(BAD_CAST "1.0");
422
423 psRootNode = xmlNewNode(NULL, BAD_CAST "Capabilities");
424
425 xmlDocSetRootElement(psDoc, psRootNode);
426
427 /* -------------------------------------------------------------------- */
428 /* Name spaces */
429 /* -------------------------------------------------------------------- */
430 xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/wcs/1.1", NULL));
431 psOwsNs = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_110_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
432 psXLinkNs = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_PREFIX);
433 xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
434 xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_PREFIX );
435
436 /*xmlNewProp(psRootNode, BAD_CAST " */
437 xmlNewProp(psRootNode, BAD_CAST "version", BAD_CAST params->version );
438
439 updatesequence = msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
440
441 if (updatesequence)
442 xmlNewProp(psRootNode, BAD_CAST "updateSequence", BAD_CAST updatesequence);
443
444 schemaLocation = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) );
445 xsi_schemaLocation = msStrdup("http://www.opengis.net/wcs/1.1");
446 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
447 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, schemaLocation);
448 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/wcs/1.1/wcsGetCapabilities.xsd ");
449 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_OWSCOMMON_OWS_110_NAMESPACE_URI);
450 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
451 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, schemaLocation);
452 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/ows/1.1.0/owsAll.xsd");
453 xmlNewNsProp(psRootNode, NULL, BAD_CAST "xsi:schemaLocation", BAD_CAST xsi_schemaLocation);
454 msFree(schemaLocation);
455 msFree(xsi_schemaLocation);
456
457 /* -------------------------------------------------------------------- */
458 /* Service metadata. */
459 /* -------------------------------------------------------------------- */
460 if( params->section == NULL
461 || strstr(params->section,"All") != NULL
462 || strstr(params->section,"ServiceIdentification") != NULL ) {
463 xmlAddChild(psRootNode, msOWSCommonServiceIdentification(
464 psOwsNs, map, "OGC WCS", "2.0.1,1.1.1,1.0.0", "CO", NULL));
465 }
466
467 /*service provider*/
468 if( params->section == NULL
469 || strstr(params->section,"All") != NULL
470 || strstr(params->section,"ServiceProvider") != NULL ) {
471 xmlAddChild(psRootNode, msOWSCommonServiceProvider(
472 psOwsNs, psXLinkNs, map, "CO", NULL));
473 }
474
475 /* -------------------------------------------------------------------- */
476 /* Operations metadata. */
477 /* -------------------------------------------------------------------- */
478 /*operation metadata */
479 if ((script_url=msOWSGetOnlineResource(map, "CO", "onlineresource", req)) == NULL
480 || (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
481 msSetError(MS_WCSERR, "Server URL not found", "msWCSGetCapabilities11()");
482 return msWCSException11(map, "NoApplicableCode", "mapserv", params->version);
483 }
484 free( script_url );
485
486 if( params->section == NULL
487 || strstr(params->section,"All") != NULL
488 || strstr(params->section,"OperationsMetadata") != NULL ) {
489 psMainNode= xmlAddChild(psRootNode,msOWSCommonOperationsMetadata(psOwsNs));
490
491 /* -------------------------------------------------------------------- */
492 /* GetCapabilities - add Sections and AcceptVersions? */
493 /* -------------------------------------------------------------------- */
494 psNode = msOWSCommonOperationsMetadataOperation(
495 psOwsNs, psXLinkNs,
496 "GetCapabilities", OWS_METHOD_GETPOST, script_url_encoded);
497
498 xmlAddChild(psMainNode, psNode);
499 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
500 ows_version, psOwsNs, "Parameter", "service", "WCS"));
501 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
502 ows_version, psOwsNs, "Parameter", "version", (char *)params->version));
503
504 /* -------------------------------------------------------------------- */
505 /* DescribeCoverage */
506 /* -------------------------------------------------------------------- */
507 if (msOWSRequestIsEnabled(map, NULL, "C", "DescribeCoverage", MS_FALSE)) {
508 psNode = msOWSCommonOperationsMetadataOperation(
509 psOwsNs, psXLinkNs,
510 "DescribeCoverage", OWS_METHOD_GETPOST, script_url_encoded);
511
512 xmlAddChild(psMainNode, psNode);
513 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
514 ows_version, psOwsNs, "Parameter", "service", "WCS"));
515 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
516 ows_version, psOwsNs, "Parameter", "version", (char *)params->version));
517 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
518 ows_version, psOwsNs, "Parameter", "identifiers", identifier_list ));
519 }
520
521 /* -------------------------------------------------------------------- */
522 /* GetCoverage */
523 /* -------------------------------------------------------------------- */
524 if (msOWSRequestIsEnabled(map, NULL, "C", "GetCoverage", MS_FALSE)) {
525
526 psNode = msOWSCommonOperationsMetadataOperation(
527 psOwsNs, psXLinkNs,
528 "GetCoverage", OWS_METHOD_GETPOST, script_url_encoded);
529
530 format_list = msWCSGetFormatsList11( map, NULL );
531
532 xmlAddChild(psMainNode, psNode);
533 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
534 ows_version, psOwsNs, "Parameter", "service", "WCS"));
535 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
536 ows_version, psOwsNs, "Parameter", "version", (char *)params->version));
537 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
538 ows_version, psOwsNs, "Parameter", "Identifier", identifier_list ));
539 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
540 ows_version, psOwsNs, "Parameter", "InterpolationType",
541 "NEAREST_NEIGHBOUR,BILINEAR" ));
542 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
543 ows_version, psOwsNs, "Parameter", "format", format_list ));
544 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
545 ows_version, psOwsNs, "Parameter", "store", "false" ));
546 xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(
547 ows_version, psOwsNs, "Parameter", "GridBaseCRS",
548 "urn:ogc:def:crs:epsg::4326" ));
549
550 msFree( format_list );
551 }
552 }
553
554 /* -------------------------------------------------------------------- */
555 /* Contents section. */
556 /* -------------------------------------------------------------------- */
557 if( params->section == NULL
558 || strstr(params->section,"All") != NULL
559 || strstr(params->section,"Contents") != NULL ) {
560 psMainNode = xmlNewChild( psRootNode, NULL, BAD_CAST "Contents", NULL );
561
562 if(ows_request->numlayers == 0) {
563 xmlAddChild(psMainNode,
564 xmlNewComment(BAD_CAST "WARNING: No WCS layers are enabled. "
565 "Check wcs/ows_enable_request settings."));
566 } else {
567 for(i=0; i<map->numlayers; i++) {
568 layerObj *layer = map->layers[i];
569 int status;
570
571 if(!msWCSIsLayerSupported(layer))
572 continue;
573
574 if (!msIntegerInArray(layer->index, ows_request->enabled_layers, ows_request->numlayers))
575 continue;
576
577 status = msWCSGetCapabilities11_CoverageSummary(
578 map, params, req, psDoc, psMainNode, layer );
579 if(status != MS_SUCCESS) {
580 msFree(identifier_list);
581 return MS_FAILURE;
582 }
583 }
584 }
585 }
586
587 /* -------------------------------------------------------------------- */
588 /* Write out the document. */
589 /* -------------------------------------------------------------------- */
590
591 if( msIO_needBinaryStdout() == MS_FAILURE )
592 return MS_FAILURE;
593
594 msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
595 msIO_sendHeaders();
596
597 context = msIO_getHandler(stdout);
598
599 xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, "UTF-8", 1);
600 msIO_contextWrite(context, buffer, size);
601 xmlFree(buffer);
602
603 /*free buffer and the document */
604 /*xmlFree(buffer);*/
605 xmlFreeDoc(psDoc);
606
607 xmlCleanupParser();
608
609 /* clean up */
610 free( script_url_encoded );
611 free( identifier_list );
612
613 return(MS_SUCCESS);
614 }
615
616 /************************************************************************/
617 /* msWCSDescribeCoverage_CoverageDescription11() */
618 /************************************************************************/
619
620 static int
msWCSDescribeCoverage_CoverageDescription11(layerObj * layer,wcsParamsObj * params,xmlNodePtr psRootNode,xmlNsPtr psOwsNs)621 msWCSDescribeCoverage_CoverageDescription11(
622 layerObj *layer, wcsParamsObj *params, xmlNodePtr psRootNode,
623 xmlNsPtr psOwsNs )
624
625 {
626 int status;
627 coverageMetadataObj cm;
628 xmlNodePtr psCD, psDomain, psSD, psGridCRS;
629 const char *value;
630
631 /* -------------------------------------------------------------------- */
632 /* Verify layer is processable. */
633 /* -------------------------------------------------------------------- */
634 if( msCheckParentPointer(layer->map,"map") == MS_FAILURE )
635 return MS_FAILURE;
636
637 if(!msWCSIsLayerSupported(layer))
638 return MS_SUCCESS;
639
640 /* -------------------------------------------------------------------- */
641 /* Setup coverage metadata. */
642 /* -------------------------------------------------------------------- */
643 status = msWCSGetCoverageMetadata(layer, &cm);
644 if(status != MS_SUCCESS) return status;
645
646 /* fill in bands rangeset info, if required. */
647 msWCSSetDefaultBandsRangeSetInfo( params, &cm, layer );
648
649 /* -------------------------------------------------------------------- */
650 /* Create CoverageDescription node. */
651 /* -------------------------------------------------------------------- */
652 psCD = xmlNewChild( psRootNode, NULL, BAD_CAST "CoverageDescription", NULL );
653
654 /* -------------------------------------------------------------------- */
655 /* Title (from description) */
656 /* -------------------------------------------------------------------- */
657 value = msOWSLookupMetadata( &(layer->metadata), "CO", "description");
658 if( value == NULL )
659 value = layer->name;
660 xmlNewChild( psCD, psOwsNs, BAD_CAST "Title", BAD_CAST value );
661
662 /* -------------------------------------------------------------------- */
663 /* Abstract */
664 /* -------------------------------------------------------------------- */
665 value = msOWSLookupMetadata( &(layer->metadata), "CO", "abstract");
666 xmlNewChild( psCD, psOwsNs, BAD_CAST "Abstract", BAD_CAST value );
667
668 /* -------------------------------------------------------------------- */
669 /* Keywords */
670 /* -------------------------------------------------------------------- */
671 value = msOWSLookupMetadata(&(layer->metadata), "CO", "keywordlist");
672
673 if (value)
674 msLibXml2GenerateList(
675 xmlNewChild(psCD, psOwsNs, BAD_CAST "Keywords", NULL),
676 NULL, "Keyword", value, ',' );
677
678 /* -------------------------------------------------------------------- */
679 /* Identifier (layer name) */
680 /* -------------------------------------------------------------------- */
681 xmlNewChild( psCD, NULL, BAD_CAST "Identifier", BAD_CAST layer->name);
682
683 /* -------------------------------------------------------------------- */
684 /* Domain */
685 /* -------------------------------------------------------------------- */
686 psDomain = xmlNewChild( psCD, NULL, BAD_CAST "Domain", NULL );
687
688 /* -------------------------------------------------------------------- */
689 /* SpatialDomain */
690 /* -------------------------------------------------------------------- */
691 psSD = xmlNewChild( psDomain, NULL, BAD_CAST "SpatialDomain", NULL );
692
693 /* -------------------------------------------------------------------- */
694 /* imageCRS bounding box. */
695 /* -------------------------------------------------------------------- */
696 xmlAddChild(
697 psSD,
698 msOWSCommonBoundingBox( psOwsNs, "urn:ogc:def:crs:OGC::imageCRS",
699 2, 0, 0, cm.xsize-1, cm.ysize-1 ));
700
701 /* -------------------------------------------------------------------- */
702 /* native CRS bounding box. */
703 /* -------------------------------------------------------------------- */
704 xmlAddChild(
705 psSD,
706 msOWSCommonBoundingBox( psOwsNs, cm.srs_urn, 2,
707 cm.extent.minx, cm.extent.miny,
708 cm.extent.maxx, cm.extent.maxy ));
709
710 /* -------------------------------------------------------------------- */
711 /* WGS84 bounding box. */
712 /* -------------------------------------------------------------------- */
713 xmlAddChild(
714 psSD,
715 msOWSCommonWGS84BoundingBox( psOwsNs, 2,
716 cm.llextent.minx, cm.llextent.miny,
717 cm.llextent.maxx, cm.llextent.maxy ));
718
719 /* -------------------------------------------------------------------- */
720 /* GridCRS */
721 /* -------------------------------------------------------------------- */
722 {
723 char format_buf[500];
724 projectionObj proj;
725 double x0 = cm.geotransform[0]+cm.geotransform[1]/2+cm.geotransform[2]/2;
726 double y0 = cm.geotransform[3]+cm.geotransform[4]/2+cm.geotransform[5]/2;
727 double resx = cm.geotransform[1];
728 double resy = cm.geotransform[5];
729
730 msInitProjection( &proj );
731 msProjectionInheritContextFrom(&proj, &(layer->projection));
732 if( msLoadProjectionString( &proj, cm.srs_urn ) == 0 ) {
733 msAxisNormalizePoints( &proj, 1, &x0, &y0 );
734 msAxisNormalizePoints( &proj, 1, &resx, &resy );
735 }
736 msFreeProjection( &proj );
737
738 psGridCRS = xmlNewChild( psSD, NULL, BAD_CAST "GridCRS", NULL );
739
740 xmlNewChild( psGridCRS, NULL, BAD_CAST "GridBaseCRS", BAD_CAST cm.srs_urn );
741 xmlNewChild( psGridCRS, NULL, BAD_CAST "GridType",
742 BAD_CAST "urn:ogc:def:method:WCS:1.1:2dSimpleGrid" );
743
744 sprintf( format_buf, "%.15g %.15g", x0, y0 );
745 xmlNewChild( psGridCRS, NULL, BAD_CAST "GridOrigin", BAD_CAST format_buf );
746
747 sprintf( format_buf, "%.15g %.15g", resx, resy );
748 xmlNewChild( psGridCRS, NULL, BAD_CAST "GridOffsets", BAD_CAST format_buf );
749
750 xmlNewChild( psGridCRS, NULL, BAD_CAST "GridCS",
751 BAD_CAST "urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS" );
752 }
753
754
755
756 #ifdef notdef
757 /* TemporalDomain */
758
759 /* TODO: figure out when a temporal domain is valid, for example only tiled rasters support time as a domain, plus we need a timeitem */
760 if(msOWSLookupMetadata(&(layer->metadata), "CO", "timeposition") || msOWSLookupMetadata(&(layer->metadata), "CO", "timeperiod")) {
761 msIO_printf(" <temporalDomain>\n");
762
763 /* TimePosition (should support a value AUTO, then we could mine positions from the timeitem) */
764 msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL, " <gml:timePosition>%s</gml:timePosition>\n", NULL);
765
766 /* TODO: add TimePeriod (only one per layer) */
767
768 msIO_printf(" </temporalDomain>\n");
769 }
770
771 msIO_printf(" </domainSet>\n");
772 #endif
773
774 /* -------------------------------------------------------------------- */
775 /* Range */
776 /* -------------------------------------------------------------------- */
777 {
778 xmlNodePtr psField, psInterpMethods, psAxis;
779 char *value;
780
781 psField =
782 xmlNewChild(
783 xmlNewChild( psCD, NULL, BAD_CAST "Range", NULL ),
784 NULL, BAD_CAST "Field", NULL );
785
786 value = msOWSGetEncodeMetadata( &(layer->metadata), "CO",
787 "rangeset_label", NULL );
788 if( value )
789 xmlNewChild( psField, psOwsNs, BAD_CAST "Title", BAD_CAST value );
790 msFree(value);
791
792 /* ows:Abstract? TODO */
793
794 value = msOWSGetEncodeMetadata( &(layer->metadata), "CO",
795 "rangeset_name", "raster" );
796 xmlNewChild( psField, NULL, BAD_CAST "Identifier", BAD_CAST value );
797 msFree(value);
798
799 xmlNewChild(
800 xmlNewChild( psField, NULL, BAD_CAST "Definition", NULL ),
801 psOwsNs, BAD_CAST "AnyValue", NULL );
802
803 /* NullValue */
804 value = msOWSGetEncodeMetadata( &(layer->metadata), "CO",
805 "rangeset_nullvalue", NULL);
806 if( value )
807 xmlNewChild( psField, NULL, BAD_CAST "NullValue",
808 BAD_CAST value );
809 msFree(value);
810
811 /* InterpolationMethods */
812 psInterpMethods =
813 xmlNewChild( psField, NULL, BAD_CAST "InterpolationMethods", NULL );
814
815 xmlNewChild( psInterpMethods, NULL, BAD_CAST "InterpolationMethod", BAD_CAST "bilinear" );
816 xmlNewChild( psInterpMethods, NULL,
817 BAD_CAST "Default", BAD_CAST "nearest neighbor" );
818
819 /* -------------------------------------------------------------------- */
820 /* Bands axis. */
821 /* -------------------------------------------------------------------- */
822 {
823 xmlNodePtr psKeys;
824 int iBand;
825
826 value = msOWSGetEncodeMetadata( &(layer->metadata), "CO",
827 "bands_name", "bands" );
828 psAxis = xmlNewChild( psField, NULL, BAD_CAST "Axis", NULL );
829 xmlNewProp( psAxis, BAD_CAST "identifier", BAD_CAST value );
830 msFree(value);
831
832 psKeys = xmlNewChild( psAxis, NULL, BAD_CAST
833 "AvailableKeys", NULL );
834
835 for( iBand = 0; iBand < cm.bandcount; iBand++ ) {
836 char szBandName[32];
837
838 snprintf( szBandName, sizeof(szBandName), "%d", iBand+1 );
839 xmlNewChild( psKeys, NULL, BAD_CAST "Key",
840 BAD_CAST szBandName );
841 }
842 }
843 }
844
845 /* -------------------------------------------------------------------- */
846 /* SupportedCRS */
847 /* -------------------------------------------------------------------- */
848 {
849 char *owned_value;
850
851 if( (owned_value =
852 msOWSGetProjURN( &(layer->projection), &(layer->metadata),
853 "CO", MS_FALSE)) != NULL ) {
854 /* ok */
855 } else if((owned_value =
856 msOWSGetProjURN( &(layer->map->projection),
857 &(layer->map->web.metadata),
858 "CO", MS_FALSE)) != NULL ) {
859 /* ok */
860 } else
861 msDebug( "mapwcs.c: missing required information, no SRSs defined.\n");
862
863 if( owned_value != NULL && strlen(owned_value) > 0 )
864 msLibXml2GenerateList( psCD, NULL, "SupportedCRS",
865 owned_value, ' ' );
866
867 msFree( owned_value );
868 }
869
870 /* -------------------------------------------------------------------- */
871 /* SupportedFormats */
872 /* -------------------------------------------------------------------- */
873 {
874 char *format_list;
875
876 format_list = msWCSGetFormatsList11( layer->map, layer );
877
878 if (strlen(format_list) > 0 )
879 msLibXml2GenerateList( psCD, NULL, "SupportedFormat",
880 format_list, ',' );
881
882 msFree( format_list );
883 }
884 msWCSFreeCoverageMetadata(&cm);
885
886 return MS_SUCCESS;
887 }
888
889 /************************************************************************/
890 /* msWCSDescribeCoverage11() */
891 /************************************************************************/
892
msWCSDescribeCoverage11(mapObj * map,wcsParamsObj * params,owsRequestObj * ows_request)893 int msWCSDescribeCoverage11(mapObj *map, wcsParamsObj *params, owsRequestObj *ows_request)
894 {
895 xmlDocPtr psDoc = NULL; /* document pointer */
896 xmlNodePtr psRootNode;
897 xmlNsPtr psOwsNs;
898 char *schemaLocation = NULL;
899 char *xsi_schemaLocation = NULL;
900
901 int i,j;
902
903 /* -------------------------------------------------------------------- */
904 /* We will actually get the coverages list as a single item in */
905 /* a string list with that item having the comma delimited */
906 /* coverage names. Split it up now, and assign back in place */
907 /* of the old coverages list. */
908 /* -------------------------------------------------------------------- */
909 if( CSLCount(params->coverages) == 1 ) {
910 char **old_coverages = params->coverages;
911 params->coverages = CSLTokenizeStringComplex( old_coverages[0], ",",
912 FALSE, FALSE );
913 CSLDestroy( old_coverages );
914 }
915
916 /* -------------------------------------------------------------------- */
917 /* Validate that the requested coverages exist as named layers. */
918 /* -------------------------------------------------------------------- */
919 if(params->coverages) { /* use the list */
920 for( j = 0; params->coverages[j]; j++ ) {
921 i = msGetLayerIndex(map, params->coverages[j]);
922 if ( (i == -1) ||
923 (!msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers)) ) {
924 msSetError( MS_WCSERR,
925 "COVERAGE %s cannot be opened / does not exist",
926 "msWCSDescribeCoverage()", params->coverages[j]);
927 return msWCSException11(map, "CoverageNotDefined", "coverage", params->version);
928 }
929 }
930 }
931
932 /* -------------------------------------------------------------------- */
933 /* Create document. */
934 /* -------------------------------------------------------------------- */
935 psDoc = xmlNewDoc(BAD_CAST "1.0");
936
937 psRootNode = xmlNewNode(NULL, BAD_CAST "CoverageDescriptions");
938
939 xmlDocSetRootElement(psDoc, psRootNode);
940
941 /* -------------------------------------------------------------------- */
942 /* Name spaces */
943 /* -------------------------------------------------------------------- */
944 xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/wcs/1.1", NULL));
945 psOwsNs = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_110_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
946 xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_PREFIX);
947 xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
948 xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_PREFIX );
949
950 schemaLocation = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) );
951 xsi_schemaLocation = msStrdup("http://www.opengis.net/wcs/1.1");
952 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
953 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, schemaLocation);
954 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/wcs/1.1/wcsDescribeCoverage.xsd ");
955 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_OWSCOMMON_OWS_110_NAMESPACE_URI);
956 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
957 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, schemaLocation);
958 xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/ows/1.1.0/owsAll.xsd");
959 xmlNewNsProp(psRootNode, NULL, BAD_CAST "xsi:schemaLocation", BAD_CAST xsi_schemaLocation);
960 msFree(schemaLocation);
961 msFree(xsi_schemaLocation);
962
963 /* -------------------------------------------------------------------- */
964 /* Generate a CoverageDescription for each requested coverage. */
965 /* -------------------------------------------------------------------- */
966
967 if(params->coverages) { /* use the list */
968 for( j = 0; params->coverages[j]; j++ ) {
969 i = msGetLayerIndex(map, params->coverages[j]);
970 msWCSDescribeCoverage_CoverageDescription11((GET_LAYER(map, i)),
971 params, psRootNode,
972 psOwsNs );
973 }
974 } else { /* return all layers */
975 for(i=0; i<map->numlayers; i++) {
976
977 if (!msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers))
978 continue;
979
980 msWCSDescribeCoverage_CoverageDescription11((GET_LAYER(map, i)),
981 params, psRootNode,
982 psOwsNs );
983 }
984 }
985
986 /* -------------------------------------------------------------------- */
987 /* Write out the document. */
988 /* -------------------------------------------------------------------- */
989 {
990 xmlChar *buffer = NULL;
991 int size = 0;
992 msIOContext *context = NULL;
993
994 if( msIO_needBinaryStdout() == MS_FAILURE )
995 return MS_FAILURE;
996
997 msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
998 msIO_sendHeaders();
999
1000 context = msIO_getHandler(stdout);
1001
1002 xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, "UTF-8", 1);
1003 msIO_contextWrite(context, buffer, size);
1004 xmlFree(buffer);
1005 }
1006
1007 /* -------------------------------------------------------------------- */
1008 /* Cleanup */
1009 /* -------------------------------------------------------------------- */
1010 xmlFreeDoc(psDoc);
1011 xmlCleanupParser();
1012
1013 return MS_SUCCESS;
1014 }
1015
1016 #endif /* defined(USE_WCS_SVR) && defined(USE_LIBXML2) */
1017
1018 /************************************************************************/
1019 /* msWCSGetCoverageBands11() */
1020 /* */
1021 /* We expect input to be of the form: */
1022 /* RangeSubset=raster:interpolation[bands[1]]. */
1023 /* */
1024 /* RangeSet=raster:[bands[1,2]] */
1025 /* or */
1026 /* RangeSet=raster:bilinear */
1027 /* */
1028 /* This function tries to return a bandlist if found, and will */
1029 /* also push an INTERPOLATION keyword into the parameters list */
1030 /* if found in the RangeSubset. */
1031 /************************************************************************/
1032
1033 #if defined(USE_WCS_SVR)
msWCSGetCoverageBands11(mapObj * map,cgiRequestObj * request,wcsParamsObj * params,layerObj * lp,char ** p_bandlist)1034 int msWCSGetCoverageBands11( mapObj *map, cgiRequestObj *request,
1035 wcsParamsObj *params, layerObj *lp,
1036 char **p_bandlist )
1037
1038 {
1039 char *rangesubset, *field_id;
1040 const char *axis_id, *value;
1041 int i;
1042
1043 /* -------------------------------------------------------------------- */
1044 /* Fetch the RangeSubset from the parameters, skip building a */
1045 /* bands list if not found. */
1046 /* -------------------------------------------------------------------- */
1047 value = msWCSGetRequestParameter(request, "RangeSubset");
1048 if( value == NULL )
1049 return MS_SUCCESS;
1050
1051 rangesubset = msStrdup(value);
1052
1053 /* -------------------------------------------------------------------- */
1054 /* What is the <Field identifier=...> (rangeset_name)? */
1055 /* -------------------------------------------------------------------- */
1056 value = msOWSLookupMetadata( &(lp->metadata), "CO", "rangeset_name" );
1057 if( value == NULL )
1058 value = "raster";
1059 field_id = msStrdup(value);
1060
1061 /* -------------------------------------------------------------------- */
1062 /* What is the <Axis identifier=...> (bands_name)? */
1063 /* -------------------------------------------------------------------- */
1064 axis_id = msOWSLookupMetadata( &(lp->metadata), "CO", "bands_name" );
1065 if( axis_id == NULL )
1066 axis_id = "bands";
1067
1068 /* -------------------------------------------------------------------- */
1069 /* Parse out the field identifier from the request and verify. */
1070 /* -------------------------------------------------------------------- */
1071 value = rangesubset + strlen(field_id);
1072
1073 if( strcasecmp(rangesubset,field_id) == 0 ) {
1074 free(rangesubset);
1075 free(field_id);
1076 return MS_SUCCESS; /* we only got field ... default options */
1077 }
1078
1079 if( strlen(rangesubset) <= strlen(field_id)+1
1080 || strncasecmp(rangesubset,field_id,strlen(field_id)) != 0
1081 || (*value != '[' && *value != ':') ) {
1082 msSetError( MS_WCSERR,
1083 "RangeSubset field name malformed, expected '%s', got RangeSubset=%s",
1084 "msWCSGetCoverageBands11()",
1085 field_id, rangesubset );
1086 free(rangesubset);
1087 free(field_id);
1088 return msWCSException11(map, "NoApplicableCode", "mapserv", params->version);
1089 }
1090
1091 free( field_id );
1092 field_id = NULL;
1093
1094 /* -------------------------------------------------------------------- */
1095 /* Parse out the interpolation, if found. */
1096 /* -------------------------------------------------------------------- */
1097 if( *value == ':' ) {
1098 assert( params->interpolation == NULL );
1099 params->interpolation = msStrdup(value+1);
1100 for( i = 0; params->interpolation[i] != '\0'; i++ ) {
1101 if( params->interpolation[i] == '[' ) {
1102 params->interpolation[i] = '\0';
1103 break;
1104 }
1105 }
1106
1107 value += strlen(params->interpolation) + 1;
1108 }
1109
1110 /* -------------------------------------------------------------------- */
1111 /* Parse out the axis name, and verify. */
1112 /* -------------------------------------------------------------------- */
1113 if( *value != '[' ) {
1114 free(rangesubset);
1115 return MS_SUCCESS;
1116 }
1117
1118 value++;
1119
1120 if( strlen(value) <= strlen(axis_id)+1
1121 || strncasecmp(value,axis_id,strlen(axis_id)) != 0
1122 || value[strlen(axis_id)] != '[' ) {
1123 msSetError( MS_WCSERR,
1124 "RangeSubset axis name malformed, expected '%s', got RangeSubset=%s",
1125 "msWCSGetCoverageBands11()",
1126 axis_id, rangesubset );
1127 free(rangesubset);
1128 return msWCSException11(map, "NoApplicableCode", "mapserv", params->version);
1129 }
1130
1131 /* -------------------------------------------------------------------- */
1132 /* Parse the band list. Basically assuming the band list is */
1133 /* everything from here to a close ';'. */
1134 /* -------------------------------------------------------------------- */
1135 value += strlen(axis_id) + 1;
1136
1137 *p_bandlist = msStrdup(value);
1138
1139 for( i = 0; (*p_bandlist)[i] != '\0'; i++ ) {
1140 if( (*p_bandlist)[i] == '[' ) {
1141 (*p_bandlist)[i] = '\0';
1142 break;
1143 }
1144 }
1145 free(rangesubset);
1146 return MS_SUCCESS;
1147 }
1148 #endif
1149
1150 /************************************************************************/
1151 /* msWCSReturnCoverage11() */
1152 /* */
1153 /* Return a render image as a coverage to the caller with WCS */
1154 /* 1.1 "mime" wrapping. */
1155 /************************************************************************/
1156
1157 #if defined(USE_WCS_SVR)
msWCSReturnCoverage11(wcsParamsObj * params,mapObj * map,imageObj * image)1158 int msWCSReturnCoverage11( wcsParamsObj *params, mapObj *map,
1159 imageObj *image )
1160 {
1161 int status, i;
1162 char *filename = NULL;
1163 char *base_dir = NULL;
1164 const char *fo_filename;
1165
1166 fo_filename = msGetOutputFormatOption( image->format, "FILENAME", NULL );
1167
1168 /* -------------------------------------------------------------------- */
1169 /* Fetch the driver we will be using and check if it supports */
1170 /* VSIL IO. */
1171 /* -------------------------------------------------------------------- */
1172 if( EQUALN(image->format->driver,"GDAL/",5) ) {
1173 GDALDriverH hDriver;
1174 const char *pszExtension = image->format->extension;
1175
1176 msAcquireLock( TLOCK_GDAL );
1177 hDriver = GDALGetDriverByName( image->format->driver+5 );
1178 if( hDriver == NULL ) {
1179 msReleaseLock( TLOCK_GDAL );
1180 msSetError( MS_MISCERR,
1181 "Failed to find %s driver.",
1182 "msWCSReturnCoverage11()",
1183 image->format->driver+5 );
1184 return msWCSException11(map, "NoApplicableCode", "mapserv",
1185 params->version);
1186 }
1187
1188 if( pszExtension == NULL )
1189 pszExtension = "img.tmp";
1190
1191 if( msGDALDriverSupportsVirtualIOOutput(hDriver) ) {
1192 base_dir = msTmpFile(map, map->mappath, "/vsimem/wcsout", NULL);
1193 if( fo_filename )
1194 filename = msStrdup(CPLFormFilename(base_dir,
1195 fo_filename,NULL));
1196 else
1197 filename = msStrdup(CPLFormFilename(base_dir,
1198 "out", pszExtension ));
1199
1200 /* CleanVSIDir( "/vsimem/wcsout" ); */
1201
1202 msReleaseLock( TLOCK_GDAL );
1203 status = msSaveImage(map, image, filename);
1204 if( status != MS_SUCCESS ) {
1205 msFree(filename);
1206 msSetError(MS_MISCERR, "msSaveImage() failed",
1207 "msWCSReturnCoverage11()");
1208 return msWCSException11(map, "NoApplicableCode", "mapserv",
1209 params->version);
1210 }
1211 }
1212 msReleaseLock( TLOCK_GDAL );
1213 }
1214
1215 /* -------------------------------------------------------------------- */
1216 /* Output stock header. */
1217 /* -------------------------------------------------------------------- */
1218 msIO_setHeader("Content-Type","multipart/mixed; boundary=wcs");
1219 msIO_sendHeaders();
1220 msIO_printf(
1221 "\r\n--wcs\r\n"
1222 "Content-Type: text/xml; charset=UTF-8\r\n"
1223 "Content-ID: wcs.xml\r\n\r\n"
1224 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1225 "<Coverages\n"
1226 " xmlns=\"http://www.opengis.net/wcs/1.1\"\n"
1227 " xmlns:ows=\"http://www.opengis.net/ows/1.1\"\n"
1228 " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
1229 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1230 " xsi:schemaLocation=\"http://www.opengis.net/ows/1.1 ../owsCoverages.xsd\">\n"
1231 " <Coverage>\n");
1232
1233 /* -------------------------------------------------------------------- */
1234 /* If we weren't able to write data under /vsimem, then we just */
1235 /* output a single "stock" filename. */
1236 /* -------------------------------------------------------------------- */
1237 if( filename == NULL ) {
1238 msOutputFormatResolveFromImage( map, image );
1239 msIO_fprintf(
1240 stdout,
1241 " <ows:Reference xlink:href=\"cid:coverage/wcs.%s\"/>\n"
1242 " </Coverage>\n"
1243 "</Coverages>\n"
1244 "\r\n--wcs\r\n"
1245 "Content-Type: %s\r\n"
1246 "Content-Description: coverage data\r\n"
1247 "Content-Transfer-Encoding: binary\r\n"
1248 "Content-ID: coverage/wcs.%s\r\n"
1249 "Content-Disposition: INLINE\r\n\r\n",
1250 MS_IMAGE_EXTENSION(map->outputformat),
1251 MS_IMAGE_MIME_TYPE(map->outputformat),
1252 MS_IMAGE_EXTENSION(map->outputformat));
1253
1254 status = msSaveImage(map, image, NULL);
1255 if( status != MS_SUCCESS ) {
1256 msSetError( MS_MISCERR, "msSaveImage() failed", "msWCSReturnCoverage11()");
1257 return msWCSException11(map, "NoApplicableCode", "mapserv", params->version);
1258 }
1259
1260 msIO_fprintf( stdout, "\r\n--wcs--\r\n" );
1261 return MS_SUCCESS;
1262 }
1263
1264 /* -------------------------------------------------------------------- */
1265 /* When potentially listing multiple files, we take great care */
1266 /* to identify the "primary" file and list it first. In fact */
1267 /* it is the only file listed in the coverages document. */
1268 /* -------------------------------------------------------------------- */
1269 {
1270 char **all_files = CPLReadDir( base_dir );
1271 int count = CSLCount(all_files);
1272
1273 if( msIO_needBinaryStdout() == MS_FAILURE )
1274 return MS_FAILURE;
1275
1276 msAcquireLock( TLOCK_GDAL );
1277 for( i = count-1; i >= 0; i-- ) {
1278 const char *this_file = all_files[i];
1279
1280 if( EQUAL(this_file,".") || EQUAL(this_file,"..") ) {
1281 all_files = CSLRemoveStrings( all_files, i, 1, NULL );
1282 continue;
1283 }
1284
1285 if( i > 0 && EQUAL(this_file,CPLGetFilename(filename)) ) {
1286 all_files = CSLRemoveStrings( all_files, i, 1, NULL );
1287 all_files = CSLInsertString(all_files,0,CPLGetFilename(filename));
1288 i++;
1289 }
1290 }
1291
1292 msIO_fprintf(
1293 stdout,
1294 " <ows:Reference xlink:href=\"cid:coverage/%s\"/>\n"
1295 " </Coverage>\n"
1296 "</Coverages>\n",
1297 CPLGetFilename(filename) );
1298
1299 /* -------------------------------------------------------------------- */
1300 /* Dump all the files in the memory directory as mime sections. */
1301 /* -------------------------------------------------------------------- */
1302 count = CSLCount(all_files);
1303
1304 for( i = 0; i < count; i++ ) {
1305 const char *mimetype = NULL;
1306 FILE *fp;
1307 unsigned char block[4000];
1308 int bytes_read;
1309
1310 if( i == 0 )
1311 mimetype = MS_IMAGE_MIME_TYPE(map->outputformat);
1312
1313 if( mimetype == NULL )
1314 mimetype = "application/octet-stream";
1315
1316 msIO_fprintf(
1317 stdout,
1318 "\r\n--wcs\r\n"
1319 "Content-Type: %s\r\n"
1320 "Content-Description: coverage data\r\n"
1321 "Content-Transfer-Encoding: binary\r\n"
1322 "Content-ID: coverage/%s\r\n"
1323 "Content-Disposition: INLINE\r\n\r\n",
1324 mimetype,
1325 all_files[i]);
1326
1327 fp = VSIFOpenL(
1328 CPLFormFilename(base_dir, all_files[i], NULL),
1329 "rb" );
1330 if( fp == NULL ) {
1331 msReleaseLock( TLOCK_GDAL );
1332 msSetError( MS_MISCERR,
1333 "Failed to open %s for streaming to stdout.",
1334 "msWCSReturnCoverage11()", all_files[i] );
1335 return MS_FAILURE;
1336 }
1337
1338 while( (bytes_read = VSIFReadL(block, 1, sizeof(block), fp)) > 0 )
1339 msIO_fwrite( block, 1, bytes_read, stdout );
1340
1341 VSIFCloseL( fp );
1342
1343 VSIUnlink( CPLFormFilename(base_dir, all_files[i], NULL) );
1344 }
1345
1346 msFree(base_dir);
1347 msFree(filename);
1348 CSLDestroy( all_files );
1349 msReleaseLock( TLOCK_GDAL );
1350
1351 msIO_fprintf( stdout, "\r\n--wcs--\r\n" );
1352 return MS_SUCCESS;
1353 }
1354 }
1355 #endif /* defined(USE_WCS_SVR) && defined(USE_LIBXML2) */
1356
1357 /************************************************************************/
1358 /* ==================================================================== */
1359 /* If we don't have libxml2 but WCS SVR was selected, then */
1360 /* report WCS 1.1 requests as unsupported. */
1361 /* ==================================================================== */
1362 /************************************************************************/
1363
1364 #if defined(USE_WCS_SVR) && !defined(USE_LIBXML2)
1365
1366 #include "mapwcs.h"
1367
1368 /* ==================================================================== */
1369
msWCSDescribeCoverage11(mapObj * map,wcsParamsObj * params,owsRequestObj * ows_request)1370 int msWCSDescribeCoverage11(mapObj *map, wcsParamsObj *params,
1371 owsRequestObj *ows_request)
1372 {
1373 msSetError( MS_WCSERR,
1374 "WCS 1.1 request made, but mapserver requires libxml2 for WCS 1.1 services and this is not configured.",
1375 "msWCSDescribeCoverage11()" );
1376 return msWCSException11(map, "NoApplicableCode", "mapserv", params->version);
1377 }
1378
1379 /* ==================================================================== */
1380
msWCSGetCapabilities11(mapObj * map,wcsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)1381 int msWCSGetCapabilities11(mapObj *map, wcsParamsObj *params,
1382 cgiRequestObj *req, owsRequestObj *ows_request)
1383 {
1384 msSetError( MS_WCSERR,
1385 "WCS 1.1 request made, but mapserver requires libxml2 for WCS 1.1 services and this is not configured.",
1386 "msWCSGetCapabilities11()" );
1387
1388 return msWCSException11(map, "NoApplicableCode", "mapserv", params->version);
1389 }
1390
msWCSException11(mapObj * map,const char * exceptionCode,const char * locator,const char * version)1391 int msWCSException11(mapObj *map, const char *exceptionCode, const char *locator, const char *version)
1392 {
1393 /* fallback to reporting using 1.0 style exceptions. */
1394 return msWCSException( map, exceptionCode, locator, "1.0.0" );
1395 }
1396
1397 #endif /* defined(USE_WCS_SVR) && !defined(USE_LIBXML2) */
1398