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