/****************************************************************************** * * Project: MapServer * Purpose: OpenGIS Web Coverage Server (WCS) Implementation. * Author: Steve Lime and the MapServer team. * ****************************************************************************** * Copyright (c) 1996-2005 Regents of the University of Minnesota. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies of this Software or works derived from this Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************** * * $Log: mapwcs.c,v $ * Revision 1.71 2005/09/23 04:37:32 frank * added preliminary WCS INTERPOLATION support * * Revision 1.70 2005/06/27 19:58:30 frank * Fixed some small WCS params related memory leaks. * * Revision 1.69 2005/06/14 16:03:35 dan * Updated copyright date to 2005 * * Revision 1.68 2005/05/12 18:14:52 frank * another missing WCS exception - if msSaveImage() fails * * Revision 1.67 2005/05/12 17:41:49 frank * If msDrawRasterLayerLow() fails, ensure that the error message is posted as a WCS exception. * * Revision 1.66 2005/02/18 18:14:11 dan * New patch to fix url strings broken by previous patch to bug 1238 * * Revision 1.65 2005/02/18 16:09:18 hobu * patch up msvc errors related to the comment commit * * Revision 1.64 2005/02/18 03:06:47 dan * Turned all C++ (//) comments into C comments (bug 1238) * * Revision 1.63 2005/02/12 04:41:32 sdlime * Changed %g to %.15g for WCS output (bug 1232). * * Revision 1.62 2004/11/29 14:36:22 sdlime * Last of the typos for bug 954, also use ows_service_onlineresource if using WMS metadata. * * Revision 1.61 2004/11/29 04:30:14 sdlime * Fixed typo in ResponsibleParty, positionName tag. * * Revision 1.60 2004/11/29 04:09:23 sdlime * Fixed bug 954, added ResponsibleParty output to the service block. Supports new wcs_responsibleparty_* metadata tags or tries to use WMS contact metadata. * * Revision 1.59 2004/11/16 21:57:49 dan * Final pass at updating WMS/WFS client/server interfaces to lookup "ows_*" * metadata in addition to default "wms_*"/"wfs_*" metadata (bug 568) * * Revision 1.58 2004/11/08 00:33:00 frank * Fixed support for non-square pixels in WCS. (bug 1014) * * Revision 1.57 2004/11/04 21:47:07 frank * Removed unused variable. * * Revision 1.56 2004/10/29 22:48:03 assefa * Use of metadata ows_schema_location (Bug 1013). * * Revision 1.55 2004/10/28 01:27:32 frank * Generate an error if a GetCoverage lacks a COVERAGE specification. * Must nicer than just segfaulting. * * Revision 1.54 2004/10/26 15:20:19 julien * msOWSPrintURLType: metadata:Link had a %s missing in it's format. (Bug 944) * * Revision 1.53 2004/10/26 04:26:28 sdlime * Fixed bug 989, metadataLink for the CoverageOffering section of the WCS capabilities. * * Revision 1.52 2004/10/25 17:30:38 julien * Print function for OGC URLs components. msOWSPrintURLType() (Bug 944) * * Revision 1.51 2004/10/21 04:30:56 frank * Added standardized headers. Added MS_CVSID(). * */ #include "map.h" #include "maperror.h" #include "mapthread.h" MS_CVSID("$Id: mapwcs.c,v 1.71 2005/09/23 04:37:32 frank Exp $") #ifdef USE_WCS_SVR #include "gdal.h" #include "cpl_string.h" /* GDAL string handling */ /* ** Structure to hold metadata taken from the image or image tile index */ typedef struct { const char *srs; rectObj extent, llextent; double geotransform[6]; int xsize, ysize; double xresolution, yresolution; int bandcount; int imagemode; } coverageMetadataObj; typedef struct { char *version; /* 1.0.0 for now */ char *request; /* GetCapabilities|DescribeCoverage|GetCoverage */ char *service; /* MUST be WCS */ char *section; /* of capabilities document: /WCS_Capabilities/Service|/WCS_Capabilities/Capability|/WCS_Capabilities/ContentMetadata */ char **coverages; /* NULL terminated list of coverages (in the case of a GetCoverage there will only be 1) */ char *crs; /* request coordinate reference system */ char *response_crs; /* response coordinate reference system */ rectObj bbox; /* subset bounding box (3D), although we'll only use 2D */ char *time; long width, height, depth; /* image dimensions */ double resx, resy, resz; /* resolution */ char *interpolation; /* interpolationMethod */ char *format; char *exceptions; /* exception MIME type, (default application=vnd.ogc.se_xml) */ } wcsParamsObj; /* functions defined later in this source file */ static int msWCSGetCoverageMetadata( layerObj *layer, coverageMetadataObj *cm ); #ifdef notdef /* currently unused */ /* value in a list (eg. is format valid) */ static int msWCSValidateParam(hashTableObj *metadata, char *name, const char *namespace, const char *value) { return MS_SUCCESS; /* take their word for it at the moment */ } #endif /* RangeSets can be quite complex */ static int msWCSValidateRangeSetParam(hashTableObj *metadata, char *name, const char *namespace, const char *value) { return MS_SUCCESS; /* take their word for it at the moment */ } /************************************************************************/ /* msWCSConvertRangeSetToString() */ /************************************************************************/ static char *msWCSConvertRangeSetToString(const char *value) { char **tokens; int numtokens; double min, max, res; double val; char buf1[128], *buf2=NULL; if(strchr(value, '/')) { /* value is min/max/res */ tokens = split(value, '/', &numtokens); if(tokens==NULL || numtokens != 3) { msFreeCharArray(tokens, numtokens); return NULL; /* not a set of equally spaced intervals */ } min = atof(tokens[0]); max = atof(tokens[1]); res = atof(tokens[2]); msFreeCharArray(tokens, numtokens); for(val=min; val<=max; val+=res) { if(val == min) snprintf(buf1, 128, "%g", val); else snprintf(buf1, 128, ",%g", val); buf2 = strcatalloc(buf2, buf1); } return buf2; } else return strdup(value); } /************************************************************************/ /* msWCSException() */ /************************************************************************/ static int msWCSException(mapObj *map, const char *version) { char *pszEncodedVal = NULL; /* msIO_printf("Content-type: application/vnd.ogc.se_xml%c%c",10,10); */ msIO_printf("Content-type: text/xml%c%c",10,10); /* TODO: see WCS specific exception schema (Appendix 6), this is a copy of Dan's for WFS */ msIO_printf("\n", pszEncodedVal); msFree(pszEncodedVal); msIO_printf(" \n"); msWriteErrorXML(stdout); msIO_printf(" \n"); msIO_printf("\n"); return MS_FAILURE; } /************************************************************************/ /* msWCSPrintRequestCapability() */ /************************************************************************/ static void msWCSPrintRequestCapability(const char *version, const char *request_tag, const char *script_url) { msIO_printf(" <%s>\n", request_tag); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" \n", script_url); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" \n", script_url); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" \n", request_tag); } /************************************************************************/ /* msWCSCreateParams() */ /************************************************************************/ static wcsParamsObj *msWCSCreateParams() { wcsParamsObj *params; params = (wcsParamsObj *) calloc(1, sizeof(wcsParamsObj)); if(params) { /* initialize a few things to default values */ params->version = strdup("1.0.0"); } return params; } /************************************************************************/ /* msWCSFreeParams() */ /************************************************************************/ static void msWCSFreeParams(wcsParamsObj *params) { if(params) { /* TODO */ if(params->version) free(params->version); if(params->request) free(params->request); if(params->service) free(params->service); if(params->section) free(params->section); if(params->crs) free(params->crs); if(params->response_crs) free(params->response_crs); if(params->format) free(params->format); if(params->exceptions) free(params->exceptions); if(params->time) free(params->time); if(params->interpolation) free(params->interpolation); } } /************************************************************************/ /* msWCSIsLayerSupported() */ /************************************************************************/ static int msWCSIsLayerSupported(layerObj *layer) { /* only raster layers, with 'DUMP TRUE' explicitly defined, are elligible to be served via WCS, WMS rasters are not ok */ if(layer->dump && (layer->type == MS_LAYER_RASTER) && layer->connectiontype != MS_WMS) return MS_TRUE; return MS_FALSE; } /************************************************************************/ /* msWCSGetRequestParameter() */ /************************************************************************/ static const char *msWCSGetRequestParameter(cgiRequestObj *request, char *name) { int i; if(!request || !name) /* nothing to do */ return NULL; if(request->NumParams > 0) { for(i=0; iNumParams; i++) { if(strcasecmp(request->ParamNames[i], name) == 0) return request->ParamValues[i]; } } return NULL; } /************************************************************************/ /* msWCSSetDefaultBandsRangeSetInfo() */ /************************************************************************/ static void msWCSSetDefaultBandsRangeSetInfo( wcsParamsObj *params, coverageMetadataObj *cm, layerObj *lp ) { /* This function will provide default rangeset information for the "special" */ /* "bands" rangeset if it appears in the axes list but has no specifics provided */ /* in the metadata. */ const char *value; char *bandlist; int i; /* Does this item exist in the axes list? */ value = msOWSLookupMetadata(&(lp->metadata), "COM", "rangeset_axes"); if( value == NULL ) return; value = strstr(value,"bands"); if( value[5] != '\0' && value[5] != ' ' ) return; /* Are there any w*s_bands_ metadata already? If so, skip out. */ if( msOWSLookupMetadata(&(lp->metadata), "COM", "bands_description") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_name") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_label") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_values") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_values_semantic") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_values_type") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_rangeitem") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_semantic") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_refsys") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_refsyslabel") != NULL || msOWSLookupMetadata(&(lp->metadata), "COM", "bands_interval") != NULL ) return; /* OK, we have decided to fill in the information. */ msInsertHashTable( &(lp->metadata), "wcs_bands_name", "bands" ); msInsertHashTable( &(lp->metadata), "wcs_bands_label", "Bands/Channels/Samples" ); msInsertHashTable( &(lp->metadata), "wcs_bands_rangeitem", "_bands" ); /* ? */ bandlist = (char *) malloc(cm->bandcount*30+30 ); strcpy( bandlist, "1" ); for( i = 1; i < cm->bandcount; i++ ) sprintf( bandlist+strlen(bandlist),",%d", i+1 ); msInsertHashTable( &(lp->metadata), "wcs_bands_values", bandlist ); free( bandlist ); } /************************************************************************/ /* msWCSParseRequest() */ /************************************************************************/ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params, mapObj *map) { int i; if(!request || !params) /* nothing to do */ return MS_SUCCESS; if(request->NumParams > 0) { for(i=0; iNumParams; i++) { if(strcasecmp(request->ParamNames[i], "VERSION") == 0) params->version = strdup(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "REQUEST") == 0) params->request = strdup(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "INTERPOLATION") == 0) params->interpolation = strdup(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "SERVICE") == 0) params->service = strdup(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "SECTION") == 0) params->section = strdup(request->ParamValues[i]); /* TODO: validate value here */ /* GetCoverage parameters. */ else if(strcasecmp(request->ParamNames[i], "BBOX") == 0) { char **tokens; int n; tokens = split(request->ParamValues[i], ',', &n); if(tokens==NULL || n != 4) { msSetError(MS_WMSERR, "Wrong number of arguments for BBOX.", "msWCSParseRequest()"); return msWCSException(map, params->version); } params->bbox.minx = atof(tokens[0]); params->bbox.miny = atof(tokens[1]); params->bbox.maxx = atof(tokens[2]); params->bbox.maxy = atof(tokens[3]); msFreeCharArray(tokens, n); } else if(strcasecmp(request->ParamNames[i], "RESX") == 0) params->resx = atof(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "RESY") == 0) params->resy = atof(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "WIDTH") == 0) params->width = atoi(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "HEIGHT") == 0) params->height = atoi(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "COVERAGE") == 0) params->coverages = CSLAddString(params->coverages, request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "TIME") == 0) params->time = strdup(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "FORMAT") == 0) params->format = strdup(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "CRS") == 0) params->crs = strdup(request->ParamValues[i]); else if(strcasecmp(request->ParamNames[i], "RESPONSE_CRS") == 0) params->response_crs = strdup(request->ParamValues[i]); /* and so on... */ } } /* we are not dealing with an XML encoded request at this point */ return MS_SUCCESS; } /************************************************************************/ /* msWCSGetCapabilities_Service_ResponsibleParty() */ /************************************************************************/ static void msWCSGetCapabilities_Service_ResponsibleParty(mapObj *map) { int bEnableTelephone=MS_FALSE, bEnableAddress=MS_FALSE, bEnableOnlineResource=MS_FALSE; /* the WCS-specific way */ if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_individualname") || msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_organizationname")) { msIO_printf("\n"); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_individualname", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_organizationname", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_positionname", OWS_NOERR, " %s\n", NULL); if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_phone_voice") || msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_phone_facsimile")) bEnableTelephone = MS_TRUE; if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_deliverypoint") || msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_city") || msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_administrativearea") || msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_postalcode") || msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_country") || msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_electronicmailaddress")) bEnableAddress = MS_TRUE; if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_onlineresource")) bEnableOnlineResource = MS_TRUE; if(bEnableTelephone || bEnableAddress || bEnableOnlineResource) { msIO_printf(" \n"); if(bEnableTelephone) { msIO_printf(" \n"); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_phone_voice", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_phone_facsimile", OWS_NOERR, " %s\n", NULL); msIO_printf(" \n"); } if(bEnableAddress) { msIO_printf("
\n"); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_deliverypoint", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_city", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_administrativearea", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_postalcode", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_country", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_electronicmailaddress", OWS_NOERR, " %s\n", NULL); msIO_printf("
\n"); } msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_onlineresource", OWS_NOERR, " \n", NULL); msIO_printf("
\n"); } msIO_printf("
\n"); } else if(msOWSLookupMetadata(&(map->web.metadata), "COM", "contactperson") || msOWSLookupMetadata(&(map->web.metadata), "COM", "contactorganization")) { /* leverage WMS contact information */ msIO_printf("\n"); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "contactperson", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "contactorganization", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "contactposition", OWS_NOERR, " %s\n", NULL); if(msOWSLookupMetadata(&(map->web.metadata), "COM", "contactvoicetelephone") || msOWSLookupMetadata(&(map->web.metadata), "COM", "contactfacsimiletelephone")) bEnableTelephone = MS_TRUE; if(msOWSLookupMetadata(&(map->web.metadata), "COM", "address") || msOWSLookupMetadata(&(map->web.metadata), "COM", "city") || msOWSLookupMetadata(&(map->web.metadata), "COM", "stateorprovince") || msOWSLookupMetadata(&(map->web.metadata), "COM", "postcode") || msOWSLookupMetadata(&(map->web.metadata), "COM", "country") || msOWSLookupMetadata(&(map->web.metadata), "COM", "contactelectronicmailaddress")) bEnableAddress = MS_TRUE; if(msOWSLookupMetadata(&(map->web.metadata), "COM", "service_onlineresource")) bEnableOnlineResource = MS_TRUE; if(bEnableTelephone || bEnableAddress || bEnableOnlineResource) { msIO_printf(" \n"); if(bEnableTelephone) { msIO_printf(" \n"); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "contactvoicetelephone", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "contactfacsimiletelephone", OWS_NOERR, " %s\n", NULL); msIO_printf(" \n"); } if(bEnableAddress) { msIO_printf("
\n"); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "address", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "city", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "stateorprovince", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "postcode", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "country", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "contactelectronicmailaddress", OWS_NOERR, " %s\n", NULL); msIO_printf("
\n"); } msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "service_onlineresource", OWS_NOERR, " \n", NULL); msIO_printf("
\n"); } msIO_printf("
\n"); } return; } /************************************************************************/ /* msWCSGetCapabilities_Service() */ /************************************************************************/ static int msWCSGetCapabilities_Service(mapObj *map, wcsParamsObj *params) { /* start the Service section, only need the full start tag if this is the only section requested */ if(!params->section) msIO_printf("\n"); else msIO_printf("\n", params->version, msOWSGetSchemasLocation(map), params->version); /* optional metadataLink */ msOWSPrintURLType(stdout, &(map->web.metadata), "COM", "metadatalink", OWS_NOERR, " ", NULL, " metadataType=\"%s\"", NULL, NULL, NULL, " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE, MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "description", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "name", OWS_NOERR, " %s\n", "MapServer WCS"); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "label", OWS_WARN, " \n", NULL); /* we are not supporting the optional keyword type, at least not yet */ msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "COM", "keywordlist", " \n", " \n", " %s\n", NULL); msWCSGetCapabilities_Service_ResponsibleParty(map); msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "COM", "fees", OWS_NOERR, " %s\n", "NONE"); msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "COM", "accessconstraints", " \n", " \n", " %s\n", "NONE"); /* done */ msIO_printf("\n"); return MS_SUCCESS; } /************************************************************************/ /* msWCSGetCapabilities_Capability() */ /************************************************************************/ static int msWCSGetCapabilities_Capability(mapObj *map, wcsParamsObj *params, cgiRequestObj *req) { char *script_url=NULL, *script_url_encoded; /* we need this server's onlineresource for the request section */ if((script_url=msOWSGetOnlineResource(map, "CO", "onlineresource", req)) == NULL || (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) { return msWCSException(map, params->version); } /* start the Capabilty section, only need the full start tag if this is the only section requested */ if(!params->section) msIO_printf("\n"); else msIO_printf("\n", params->version, msOWSGetSchemasLocation(map), params->version); /* describe the types of requests the server can handle */ msIO_printf(" \n"); msWCSPrintRequestCapability(params->version, "GetCapabilities", script_url_encoded); msWCSPrintRequestCapability(params->version, "DescribeCoverage", script_url_encoded); msWCSPrintRequestCapability(params->version, "GetCoverage", script_url_encoded); msIO_printf(" \n"); /* describe the exception formats the server can produce */ msIO_printf(" \n"); msIO_printf(" application/vnd.ogc.se_xml\n"); msIO_printf(" \n"); /* describe any vendor specific capabilities */ msIO_printf(" \n"); /* none yet */ /* done */ msIO_printf("\n"); return MS_SUCCESS; } /************************************************************************/ /* msWCSGetCapabilities_CoverageOfferingBrief() */ /************************************************************************/ static int msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer, wcsParamsObj *params) { coverageMetadataObj cm; int status; if(!msWCSIsLayerSupported(layer)) return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */ status = msWCSGetCoverageMetadata(layer, &cm); if(status != MS_SUCCESS) return MS_FAILURE; /* start the CoverageOfferingBrief section */ msIO_printf(" \n"); /* is this tag right? (I hate schemas without ANY examples) */ /* optional metadataLink */ msOWSPrintURLType(stdout, &(layer->metadata), "COM", "metadatalink", OWS_NOERR, " ", NULL, " metadataType=\"%s\"", NULL, NULL, NULL, " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE, MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "description", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "name", OWS_NOERR, " %s\n", layer->name); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "label", OWS_WARN, " \n", NULL); /* TODO: add elevation and temporal ranges to lonLatEnvelope (optional) */ msIO_printf(" \n"); msIO_printf(" %.15g %.15g\n", cm.llextent.minx, cm.llextent.miny); /* TODO: don't know if this is right */ msIO_printf(" %.15g %.15g\n", cm.llextent.maxx, cm.llextent.maxy); msIO_printf(" \n"); /* we are not supporting the optional keyword type, at least not yet */ msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "COM", "keywordlist", " \n", " \n", " %s\n", NULL); /* done */ msIO_printf(" \n"); return MS_SUCCESS; } /************************************************************************/ /* msWCSGetCapabilities_ContentMetadata() */ /************************************************************************/ static int msWCSGetCapabilities_ContentMetadata(mapObj *map, wcsParamsObj *params) { int i; /* start the ContentMetadata section, only need the full start tag if this is the only section requested */ /* TODO: add Xlink attributes for other sources of this information */ if(!params->section) msIO_printf("\n"); else msIO_printf("\n", params->version, msOWSGetSchemasLocation(map), params->version); for(i=0; inumlayers; i++) msWCSGetCapabilities_CoverageOfferingBrief(&(map->layers[i]), params); /* done */ msIO_printf("\n"); return MS_SUCCESS; } /************************************************************************/ /* msWCSGetCapabilities() */ /************************************************************************/ static int msWCSGetCapabilities(mapObj *map, wcsParamsObj *params, cgiRequestObj *req) { /* msIO_printf("Content-type: application/vnd.ogc.se_xml%c%c",10,10); */ msIO_printf("Content-type: text/xml%c%c",10,10); /* print common capability elements */ /* TODO: DocType? */ msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), NULL, "wcs_encoding", OWS_NOERR, "\n", "ISO-8859-1"); if(!params->section) msIO_printf("\n", params->version, msOWSGetSchemasLocation(map), params->version); /* print the various capability sections */ if(!params->section || strcasecmp(params->section, "/WCS_Capabilities/Service") == 0) msWCSGetCapabilities_Service(map, params); if(!params->section || strcasecmp(params->section, "/WCS_Capabilities/Capability") == 0) msWCSGetCapabilities_Capability(map, params, req); if(!params->section || strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata") == 0) msWCSGetCapabilities_ContentMetadata(map, params); /* done */ if(!params->section) msIO_printf("\n"); /* clean up */ /* msWCSFreeParams(params); */ return MS_SUCCESS; } /************************************************************************/ /* msWCSDescribeCoverage_AxisDescription() */ /************************************************************************/ static int msWCSDescribeCoverage_AxisDescription(layerObj *layer, char *name) { const char *value; char tag[100]; /* should be plenty of space */ msIO_printf(" \n"); msIO_printf(" metadata), "COM", tag, OWS_NOERR, " semantic=\"%s\"", NULL); snprintf(tag, 100, "%s_refsys", name); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", tag, OWS_NOERR, " refSys=\"%s\"", NULL); snprintf(tag, 100, "%s_refsyslabel", name); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", tag, OWS_NOERR, " refSysLabel=\"%s\"", NULL); msIO_printf(">\n"); /* TODO: add metadataLink (optional) */ snprintf(tag, 100, "%s_description", name); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", tag, OWS_NOERR, " %s\n", NULL); /* snprintf(tag, 100, "%s_name", name); */ /* msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", tag, OWS_WARN, " %s\n", NULL); */ msIO_printf(" %s\n", name); snprintf(tag, 100, "%s_label", name); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", tag, OWS_WARN, " \n", NULL); /* Values */ msIO_printf(" metadata), "COM", tag, OWS_NOERR, " semantic=\"%s\"", NULL); snprintf(tag, 100, "%s_values_type", name); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", tag, OWS_NOERR, " type=\"%s\"", NULL); msIO_printf(">\n"); /* single values, we do not support optional type and semantic attributes */ snprintf(tag, 100, "%s_values", name); if(msOWSLookupMetadata(&(layer->metadata), "COM", tag)) msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "COM", tag, NULL, NULL, " %s\n", NULL); /* intervals, only one per axis for now, we do not support optional type, atomic and semantic attributes */ snprintf(tag, 100, "%s_interval", name); if((value = msOWSLookupMetadata(&(layer->metadata), "COM", tag)) != NULL) { char **tokens; int numtokens; tokens = split(value, '/', &numtokens); if(tokens && numtokens > 0) { msIO_printf(" \n"); if(numtokens >= 1) msIO_printf(" %s\n", tokens[0]); /* TODO: handle closure */ if(numtokens >= 2) msIO_printf(" %s\n", tokens[1]); if(numtokens >= 3) msIO_printf(" %s\n", tokens[2]); msIO_printf(" \n"); } } /* TODO: add default (optional) */ msIO_printf(" \n"); /* TODO: add NullValues (optional) */ msIO_printf(" \n"); msIO_printf(" \n"); return MS_SUCCESS; } /************************************************************************/ /* msWCSDescribeCoverage_CoverageOffering() */ /************************************************************************/ static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer, wcsParamsObj *params) { const char *value; coverageMetadataObj cm; int status; if(!msWCSIsLayerSupported(layer)) return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */ status = msWCSGetCoverageMetadata(layer, &cm); if(status != MS_SUCCESS) return MS_FAILURE; /* fill in bands rangeset info, if required. */ msWCSSetDefaultBandsRangeSetInfo( params, &cm, layer ); /* start the Coverage section */ msIO_printf(" \n"); /* optional metadataLink */ msOWSPrintURLType(stdout, &(layer->metadata), "COM", "metadatalink", OWS_NOERR, " ", NULL, " metadataType=\"%s\"", NULL, NULL, NULL, " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE, MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "description", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "name", OWS_NOERR, " %s\n", layer->name); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "label", OWS_WARN, " \n", NULL); /* TODO: add elevation and temporal ranges to lonLatEnvelope (optional) */ msIO_printf(" \n"); msIO_printf(" %.15g %.15g\n", cm.llextent.minx, cm.llextent.miny); msIO_printf(" %.15g %.15g\n", cm.llextent.maxx, cm.llextent.maxy); msIO_printf(" \n"); /* we are not supporting the optional keyword type, at least not yet */ msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "COM", "keywordlist", " \n", " \n", " %s\n", NULL); /* DomainSet: starting simple, just a spatial domain (gml:envelope) and optionally a temporal domain */ msIO_printf(" \n"); /* SpatialDomain */ msIO_printf(" \n"); /* envelope in lat/lon */ msIO_printf(" \n"); msIO_printf(" %.15g %.15g\n", cm.llextent.minx, cm.llextent.miny); msIO_printf(" %.15g %.15g\n", cm.llextent.maxx, cm.llextent.maxy); msIO_printf(" \n"); /* envelope in the native srs */ if((value = msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "COM", MS_TRUE)) != NULL) msIO_printf(" \n", value); else if((value = msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "COM", MS_TRUE)) != NULL) msIO_printf(" \n", value); else msIO_printf(" \n"); msIO_printf(" %.15g %.15g\n", cm.extent.minx, cm.extent.miny); msIO_printf(" %.15g %.15g\n", cm.extent.maxx, cm.extent.maxy); msIO_printf(" \n"); /* gml:rectifiedGrid */ msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" 0 0\n"); msIO_printf(" %d %d\n", cm.xsize-1, cm.ysize-1); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" x\n"); msIO_printf(" y\n"); msIO_printf(" \n"); msIO_printf(" %.15g %.15g\n", cm.geotransform[0], cm.geotransform[3]); msIO_printf(" \n"); msIO_printf(" %.15g %.15g\n", cm.geotransform[1], cm.geotransform[2]); /* offset vector in X direction */ msIO_printf(" %.15g %.15g\n", cm.geotransform[4], cm.geotransform[5]); /* offset vector in Y direction */ msIO_printf(" \n"); msIO_printf(" \n"); /* TemporalDomain */ /* TODO: figure out when a temporal domain is valid, for example only tiled rasters support time as a domain, plus we need a timeitem */ if(msOWSLookupMetadata(&(layer->metadata), "COM", "timeposition") || msOWSLookupMetadata(&(layer->metadata), "COM", "timeperiod")) { msIO_printf(" \n"); /* TimePosition (should support a value AUTO, then we could mine positions from the timeitem) */ msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "COM", "timeposition", NULL, NULL, " %s\n", NULL); /* TODO: add TimePeriod (only one per layer) */ msIO_printf(" \n"); } msIO_printf(" \n"); /* rangeSet */ msIO_printf(" \n"); msIO_printf(" \n"); /* TODO: there are some optional attributes */ /* TODO: add metadataLink (optional) */ msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "rangeset_description", OWS_NOERR, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "rangeset_name", OWS_WARN, " %s\n", NULL); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "rangeset_label", OWS_WARN, " \n", NULL); /* compound range sets */ if((value = msOWSLookupMetadata(&(layer->metadata), "COM", "rangeset_axes")) != NULL) { char **tokens; int numtokens; tokens = split(value, ',', &numtokens); if(tokens && numtokens > 0) { int i; for(i=0; i\n"); msIO_printf(" \n"); /* supportedCRSs */ msIO_printf(" \n"); /* requestResposeCRSs: check the layer metadata/projection, and then the map metadata/projection if necessary (should never get to the error message) */ if((value = msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "COM", MS_FALSE)) != NULL) msIO_printf(" %s\n", value); else if((value = msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "COM", MS_FALSE)) != NULL) msIO_printf(" %s\n", value); else msIO_printf(" \n"); /* nativeCRSs (only one in our case) */ if((value = msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "COM", MS_TRUE)) != NULL) msIO_printf(" %s\n", value); else if((value = msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "COM", MS_TRUE)) != NULL) msIO_printf(" %s\n", value); else msIO_printf(" \n"); msIO_printf(" \n"); /* supportedFormats */ msIO_printf(" metadata), "COM", "nativeformat", OWS_NOERR, " nativeFormat=\"%s\"", NULL); msIO_printf(">\n"); msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "COM", "formats", OWS_NOERR, " %s\n", NULL); msIO_printf(" \n"); msIO_printf(" \n"); msIO_printf(" nearest neighbor\n" ); msIO_printf(" bilinear\n" ); /* msIO_printf(" bicubic\n" ); */ msIO_printf(" \n"); /* done */ msIO_printf(" \n"); return MS_SUCCESS; } /************************************************************************/ /* msWCSDescribeCoverage() */ /************************************************************************/ static int msWCSDescribeCoverage(mapObj *map, wcsParamsObj *params) { int i,j; /* printf("Content-type: application/vnd.ogc.se_xml%c%c",10,10); */ msIO_printf("Content-type: text/xml%c%c",10,10); /* print common capability elements */ msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), NULL, "wcs_encoding", OWS_NOERR, "\n", "ISO-8859-1"); /* start the DescribeCoverage section */ msIO_printf("\n", params->version, msOWSGetSchemasLocation(map), params->version); if(params->coverages) { /* use the list */ for( j = 0; params->coverages[j]; j++ ) { i = msGetLayerIndex(map, params->coverages[j]); if(i == -1) continue; /* skip this layer, could/should generate an error */ msWCSDescribeCoverage_CoverageOffering(&(map->layers[i]), params); } } else { /* return all layers */ for(i=0; inumlayers; i++) msWCSDescribeCoverage_CoverageOffering(&(map->layers[i]), params); } /* done */ msIO_printf("\n"); return MS_SUCCESS; } /************************************************************************/ /* msWCSGetCoverage() */ /************************************************************************/ static int msWCSGetCoverage(mapObj *map, cgiRequestObj *request, wcsParamsObj *params) { imageObj *image; layerObj *lp; int status, i; const char *value; outputFormatObj *format; char *bandlist=NULL; char numbands[8]; /* should be large enough to hold the number of bands in the bandlist */ coverageMetadataObj cm; /* make sure all required parameters are available (at least the easy ones) */ if(!params->crs) { msSetError( MS_WCSERR, "Required parameter CRS was not supplied.", "msWCSGetCoverage()"); return msWCSException(map, params->version); } if( params->coverages == NULL || params->coverages[0] == NULL ) { msSetError( MS_WCSERR, "Required parameter COVERAGE was not supplied.", "msWCSGetCoverage()"); return msWCSException(map, params->version); } /* find the layer we are working with. */ lp = NULL; for(i=0; inumlayers; i++) { if( EQUAL(map->layers[i].name, params->coverages[0]) ) { lp = map->layers + i; break; } } if(lp == NULL) { msSetError( MS_WCSERR, "COVERAGE=%s not found, not in supported layer list.", "msWCSGetCoverage()", params->coverages[0] ); return msWCSException(map, params->version); } /* make sure the layer is on */ lp->status = MS_ON; /* we need the coverage metadata, since things like numbands may not be available otherwise */ status = msWCSGetCoverageMetadata(lp, &cm); if(status != MS_SUCCESS) return MS_FAILURE; /* fill in bands rangeset info, if required. */ msWCSSetDefaultBandsRangeSetInfo(params, &cm, lp); /* handle the response CRS, that is set the map object projection */ if(params->response_crs || params->crs ) { const char *crs_to_use = params->response_crs; if( crs_to_use == NULL ) crs_to_use = params->crs; if (strncasecmp(crs_to_use, "EPSG:", 5) == 0) { /* RESPONSE_CRS=EPSG:xxxx */ char buffer[32]; int iUnits; sprintf(buffer, "init=epsg:%.20s", crs_to_use+5); if (msLoadProjectionString(&(map->projection), buffer) != 0) return msWCSException( map, params->version ); iUnits = GetMapserverUnitUsingProj(&(map->projection)); if (iUnits != -1) map->units = iUnits; } else { /* should we support WMS style AUTO: projections? (not for now) */ msSetError(MS_WMSERR, "Unsupported SRS namespace (only EPSG currently supported).", "msWCSGetCoverage()"); return msWCSException(map, params->version); } } /* did we get a TIME value (support only a single value for now) */ if(params->time) { int tli; layerObj *tlp=NULL; /* need to handle NOW case */ /* check format of TIME parameter */ if(strchr(params->time, ',')) { msSetError( MS_WCSERR, "Temporal lists are not supported, only individual values.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } if(strchr(params->time, '/')) { msSetError( MS_WCSERR, "Temporal ranges are not supported, only individual values.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } /* TODO: will need to expand this check if a time period is supported */ value = msOWSLookupMetadata(&(lp->metadata), "COM", "timeposition"); if(!value) { msSetError( MS_WCSERR, "The coverage does not support temporal subsetting.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } if(!strstr(value, params->time)) { /* this is likely too simple a test */ msSetError( MS_WCSERR, "The coverage does not have a time position of %s.", "msWCSGetCoverage()", params->time ); return msWCSException(map, params->version); } /* make sure layer is tiled appropriately */ if(!lp->tileindex) { msSetError( MS_WCSERR, "Underlying layer is not tiled, unable to do temporal subsetting.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } tli = msGetLayerIndex(map, lp->tileindex); if(tli == -1) { msSetError( MS_WCSERR, "Underlying layer does not use appropriate tiling mechanism.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } tlp = &(map->layers[tli]); /* make sure there is enough information to filter */ value = msOWSLookupMetadata(&(lp->metadata), "COM", "timeitem"); if(!tlp->filteritem && !value) { msSetError( MS_WCSERR, "Not enough information available to filter.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } /* override filteritem if specified in metadata */ if(value) { if(tlp->filteritem) free(tlp->filteritem); tlp->filteritem = strdup(value); } /* finally set the filter */ freeExpression(&tlp->filter); msLoadExpressionString(&tlp->filter, params->time); } /* Are there any non-spatio/temporal ranges to do subsetting on (e.g. bands) */ value = msOWSLookupMetadata(&(lp->metadata), "COM", "rangeset_axes"); /* this will get all the compound range sets */ if(value) { char **tokens; int numtokens; char tag[100]; const char *rangeitem; tokens = split(value, ',', &numtokens); for(i=0; imetadata), tokens[i], "COM", value) != MS_SUCCESS) { msSetError( MS_WCSERR, "Error specifying \"%s\" paramter value(s).", "msWCSGetCoverage()", tokens[i]); return msWCSException(map, params->version); } /* xxxxx_rangeitem tells us how to subset */ snprintf(tag, 100, "%s_rangeitem", tokens[i]); if((rangeitem = msOWSLookupMetadata(&(lp->metadata), "COM", tag)) == NULL) { msSetError( MS_WCSERR, "Missing required metadata element \"%s\", unable to process %s=%s.", "msWCSGetCoverage()", tag, tokens[i], value); return msWCSException(map, params->version); } if(strcasecmp(rangeitem, "_bands") == 0) { /* special case, subset bands */ bandlist = msWCSConvertRangeSetToString(value); if(!bandlist) { msSetError( MS_WCSERR, "Error specifying \"%s\" paramter value(s).", "msWCSGetCoverage()", tokens[i]); return msWCSException(map, params->version); } } else if(strcasecmp(rangeitem, "_pixels") == 0) { /* special case, subset pixels */ msSetError( MS_WCSERR, "Arbitrary range sets based on pixel values are not yet supported.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } else { msSetError( MS_WCSERR, "Arbitrary range sets based on tile (i.e. image) attributes are not yet supported.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } } /* clean-up */ msFreeCharArray(tokens, numtokens); } /* did we get BBOX values? if not use the exent stored in the coverageMetadataObj */ if( fabs((params->bbox.maxx - params->bbox.minx)) < 0.000000000001 || fabs(params->bbox.maxy - params->bbox.miny) < 0.000000000001 ) { params->bbox = cm.extent; /* msSetError( MS_WCSERR, "Required parameter BBOX missing or specifies an empty region.", "msWCSGetCoverage()" ); */ /* return msWCSException(params->version); */ } /* if necessary, project the BBOX */ /* compute width/height from BBOX and cellsize. */ if( (params->resx == 0.0 || params->resy == 0.0) && params->width != 0 && params->height != 0 ) { params->resx = (params->bbox.maxx - params->bbox.minx) / params->width; params->resy = (params->bbox.maxy - params->bbox.miny) / params->height; } /* compute cellsize/res from bbox and raster size. */ if( (params->width == 0 || params->height == 0) && params->resx != 0 && params->resy != 0 ) { params->width = (int) ((params->bbox.maxx - params->bbox.minx) / params->resx + 0.5); params->height = (int) ((params->bbox.maxy - params->bbox.miny) / params->resy + 0.5); } /* are we still underspecified? */ if( params->width == 0 || params->height == 0 || params->resx == 0.0 || params->resy == 0.0 ) { msSetError( MS_WCSERR, "A non-zero RESX/RESY or WIDTH/HEIGHT is required but neither was provided.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } map->cellsize = params->resx; /* Do we need to force special handling? */ if( fabs(params->resx/params->resy - 1.0) > 0.001 ) { map->gt.need_geotransform = MS_TRUE; if( map->debug ) msDebug( "RESX and RESY don't match. Using geotransform/resample."); } /* Do we have a specified interpolation method */ if( params->interpolation != NULL ) { if( strncasecmp(params->interpolation,"NEAREST",7) == 0 ) msLayerSetProcessingKey(lp, "RESAMPLE", "NEAREST"); else if( strcasecmp(params->interpolation,"BILINEAR") == 0 ) msLayerSetProcessingKey(lp, "RESAMPLE", "BILINEAR"); else if( strcasecmp(params->interpolation,"AVERAGE") == 0 ) msLayerSetProcessingKey(lp, "RESAMPLE", "AVERAGE"); else { msSetError( MS_WCSERR, "INTERPOLATION=%s specifies an unsupported interpolation method.", "msWCSGetCoverage()", params->interpolation ); return msWCSException(map, params->version); } } /* apply region and size to map object. */ map->width = params->width; map->height = params->height; map->extent = params->bbox; map->cellsize = params->resx; /* pick one, MapServer only supports square cells (what about msAdjustExtent here!) */ msMapComputeGeotransform(map); map->projection.gt = map->gt; /* check and make sure there is a format, and that it's valid (TODO: make sure in the layer metadata) */ if(!params->format) { msSetError( MS_WCSERR, "Missing required FORMAT parameter.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } if(msGetOutputFormatIndex(map,params->format) == -1) { msSetError( MS_WCSERR, "Unrecognized value for the FORMAT parameter.", "msWCSGetCoverage()" ); return msWCSException(map, params->version); } /* create a temporary outputformat (we likely will need to tweak parts) */ format = msCloneOutputFormat(msSelectOutputFormat(map,params->format)); msApplyOutputFormat(&(map->outputformat), format, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE); if(!bandlist) { /* build a bandlist (default is ALL bands) */ bandlist = (char *) malloc(cm.bandcount*30+30 ); strcpy(bandlist, "1"); for(i = 1; i < cm.bandcount; i++) sprintf(bandlist+strlen(bandlist),",%d", i+1); } msLayerSetProcessingKey(lp, "BANDS", bandlist); sprintf(numbands, "%d", countChars(bandlist, ',')+1); msSetOutputFormatOption(map->outputformat, "BAND_COUNT", numbands); /* create the image object */ if(!map->outputformat) { msSetError(MS_WCSERR, "The map outputformat is missing!", "msWCSGetCoverage()"); return msWCSException(map, params->version); } else if( MS_RENDERER_GD(map->outputformat) ) { image = msImageCreateGD(map->width, map->height, map->outputformat, map->web.imagepath, map->web.imageurl); if( image != NULL ) msImageInitGD( image, &map->imagecolor ); } else if( MS_RENDERER_RAWDATA(map->outputformat) ) { image = msImageCreate(map->width, map->height, map->outputformat, map->web.imagepath, map->web.imageurl, map); } else { msSetError(MS_WCSERR, "Map outputformat not supported for WCS!", "msWCSGetCoverage()"); return msWCSException(map, params->version); } /* Actually produce the "grid". */ status = msDrawRasterLayerLow( map, lp, image ); if( status != MS_SUCCESS ) { return msWCSException(map, params->version); } /* Emit back to client. */ msIO_printf("Content-type: %s%c%c", MS_IMAGE_MIME_TYPE(map->outputformat), 10,10); status = msSaveImage(map, image, NULL); if( status != MS_SUCCESS ) { /* unfortunately, the image content type will have already been sent but that is hard for us to avoid. The main error that could happen here is a misconfigured tmp directory or running out of space. */ return msWCSException(map, params->version); } /* Cleanup */ msFreeImage(image); msApplyOutputFormat(&(map->outputformat), NULL, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE); /* msFreeOutputFormat(format); */ return status; } #endif /* def USE_WCS_SVR */ /************************************************************************/ /* msWCSDispatch() */ /* */ /* Entry point for WCS requests */ /************************************************************************/ int msWCSDispatch(mapObj *map, cgiRequestObj *request) { #ifdef USE_WCS_SVR wcsParamsObj *params; /* populate the service parameters */ params = msWCSCreateParams(); if( msWCSParseRequest(request, params, map) == MS_FAILURE ) { msWCSFreeParams(params); /* clean up */ free(params); return MS_FAILURE; } /* If SERVICE is specified then it MUST be "WCS" */ if(params->service && strcasecmp(params->service, "WCS") != 0) { msWCSFreeParams(params); /* clean up */ free(params); return MS_DONE; } /* If SERVICE and REQUEST not included then not a WCS request */ if(!params->service && !params->request) { msWCSFreeParams(params); /* clean up */ free(params); return MS_DONE; } /* ** ok, it's a WCS request, check what we can at a global level and then dispatch to the various request handlers */ /* version is optional, but we do set a default value of 1.0.0, make sure request isn't for something different */ if(strcmp(params->version, "1.0.0") != 0) { msSetError(MS_WCSERR, "WCS Server does not support VERSION %s.", "msWCSDispatch()", params->version); msWCSException(map, params->version); msWCSFreeParams(params); /* clean up */ free(params); params = NULL; return MS_FAILURE; } /* ** Start dispatching requests */ if(strcasecmp(params->request, "GetCapabilities") == 0) return msWCSGetCapabilities(map, params, request); else if(strcasecmp(params->request, "DescribeCoverage") == 0) return msWCSDescribeCoverage(map, params); else if(strcasecmp(params->request, "GetCoverage") == 0) return msWCSGetCoverage(map, request, params); return MS_DONE; /* not a WCS request, let MapServer take it */ #else msSetError(MS_WCSERR, "WCS server support is not available.", "msWCSDispatch()"); return MS_FAILURE; #endif } /************************************************************************/ /* msWCSGetCoverageMetadata() */ /************************************************************************/ #ifdef USE_WCS_SVR static int msWCSGetCoverageMetadata( layerObj *layer, coverageMetadataObj *cm ) { /* get information that is "data" independent */ if((cm->srs = msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "COM", MS_TRUE)) == NULL) { if((cm->srs = msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "COM", MS_TRUE)) == NULL) { msSetError(MS_WCSERR, "Unable to determine the SRS for this layer, no projection defined and no metadata available.", "msWCSGetCoverageMetadata()"); return MS_FAILURE; } } /* -------------------------------------------------------------------- */ /* If we have "virtual dataset" metadata on the layer, then use */ /* that in preference to inspecting the file(s). */ /* -------------------------------------------------------------------- */ if( msOWSLookupMetadata(&(layer->metadata), "COM", "extent") != NULL ) { const char *value; /* get extent */ cm->extent.minx = 0.0; cm->extent.maxx = 0.0; cm->extent.miny = 0.0; cm->extent.maxy = 0.0; if( msOWSGetLayerExtent( layer->map, layer, "CO", &cm->extent ) == MS_FAILURE ) return MS_FAILURE; /* get resolution */ cm->xresolution = 0.0; cm->yresolution = 0.0; if( (value = msOWSLookupMetadata(&(layer->metadata), "COM", "resolution")) != NULL ) { char **tokens; int n; tokens = split(value, ' ', &n); if( tokens == NULL || n != 2 ) { msSetError( MS_WCSERR, "Wrong number of arguments for wcs|ows_resolution metadata.", "msWCSGetCoverageMetadata()"); msFreeCharArray( tokens, n ); return MS_FAILURE; } cm->xresolution = atof(tokens[0]); cm->yresolution = atof(tokens[1]); msFreeCharArray( tokens, n ); } /* get Size (in pixels and lines) */ cm->xsize = 0; cm->ysize = 0; if( (value=msOWSLookupMetadata(&(layer->metadata), "COM", "size")) != NULL ) { char **tokens; int n; tokens = split(value, ' ', &n); if( tokens == NULL || n != 2 ) { msSetError( MS_WCSERR, "Wrong number of arguments for wcs|ows_size metadata.", "msWCSGetCoverageDomain()"); msFreeCharArray( tokens, n ); return MS_FAILURE; } cm->xsize = atoi(tokens[0]); cm->ysize = atoi(tokens[1]); msFreeCharArray( tokens, n ); } /* try to compute raster size */ if( cm->xsize == 0 && cm->ysize == 0 && cm->xresolution != 0.0 && cm->yresolution != 0.0 && cm->extent.minx != cm->extent.maxx && cm->extent.miny != cm->extent.maxy ) { cm->xsize = (int) ((cm->extent.maxx - cm->extent.minx) / cm->xresolution + 0.5); cm->ysize = (int) fabs((cm->extent.maxy - cm->extent.miny) / cm->yresolution + 0.5); } /* try to compute raster resolution */ if( (cm->xresolution == 0.0 || cm->yresolution == 0.0) && cm->xsize != 0 && cm->ysize != 0 ) { cm->xresolution = (cm->extent.maxx - cm->extent.minx) / cm->xsize; cm->yresolution = (cm->extent.maxy - cm->extent.miny) / cm->ysize; } /* do we have information to do anything */ if( cm->xresolution == 0.0 || cm->yresolution == 0.0 || cm->xsize == 0 || cm->ysize == 0 ) { msSetError( MS_WCSERR, "Failed to collect extent and resolution for WCS coverage from metadata. Need value wcs|ows_resolution or wcs|ows_size values.", "msWCSGetCoverageMetadata()"); return MS_FAILURE; } /* compute geotransform */ cm->geotransform[0] = cm->extent.minx; cm->geotransform[1] = cm->xresolution; cm->geotransform[2] = 0.0; cm->geotransform[3] = cm->extent.maxy; cm->geotransform[4] = 0.0; cm->geotransform[5] = -fabs(cm->yresolution); /* get bands count, or assume 1 if not found */ cm->bandcount = 1; if( (value=msOWSLookupMetadata(&(layer->metadata), "COM", "bandcount")) != NULL) { cm->bandcount = atoi(value); } /* get bands type, or assume float if not found */ cm->imagemode = MS_IMAGEMODE_FLOAT32; if( (value=msOWSLookupMetadata(&(layer->metadata), "COM", "imagemode")) != NULL ) { if( EQUAL(value,"INT16") ) cm->imagemode = MS_IMAGEMODE_INT16; else if( EQUAL(value,"FLOAT32") ) cm->imagemode = MS_IMAGEMODE_FLOAT32; else if( EQUAL(value,"BYTE") ) cm->imagemode = MS_IMAGEMODE_BYTE; else { msSetError( MS_WCSERR, "Content of wcs|ows_imagemode (%s) not recognised. Should be one of PC256 (byte), INT16 or FLOAT32.", "msWCSGetCoverageMetadata()", value ); return MS_FAILURE; } } } else if( layer->data == NULL ) { /* no virtual metadata, not ok unless we're talking 1 image, hopefully we can fix that */ msSetError( MS_WMSERR, "RASTER Layer with no DATA statement and no WCS virtual dataset metadata. Tileindexed raster layers not supported for WCS without virtual dataset metadata (cm->extent, wcs_res, wcs_size).", "msWCSGetCoverageDomain()" ); return MS_FAILURE; } else { /* work from the file (e.g. DATA) */ GDALDatasetH hDS; GDALRasterBandH hBand; char szPath[MS_MAXPATHLEN]; msGDALInitialize(); msAcquireLock( TLOCK_GDAL ); hDS = GDALOpen(msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, layer->data) , GA_ReadOnly ); if( hDS == NULL ) { msReleaseLock( TLOCK_GDAL ); msSetError( MS_IOERR, "%s", "msWCSGetCoverageMetadata()", CPLGetLastErrorMsg() ); return MS_FAILURE; } msGetGDALGeoTransform( hDS, layer->map, layer, cm->geotransform ); cm->xsize = GDALGetRasterXSize( hDS ); cm->ysize = GDALGetRasterYSize( hDS ); cm->extent.minx = cm->geotransform[0]; cm->extent.maxx = cm->geotransform[0] + cm->geotransform[1] * cm->xsize + cm->geotransform[2] * cm->ysize; cm->extent.miny = cm->geotransform[3] + cm->geotransform[4] * cm->xsize + cm->geotransform[5] * cm->ysize; cm->extent.maxy = cm->geotransform[3]; /* TODO: need to set resolution */ cm->bandcount = GDALGetRasterCount( hDS ); if( cm->bandcount == 0 ) { msReleaseLock( TLOCK_GDAL ); msSetError( MS_WMSERR, "Raster file %s has no raster bands. This cannot be used in a layer.", "msWCSGetCoverageMetadata()", layer->data ); return MS_FAILURE; } hBand = GDALGetRasterBand( hDS, 1 ); switch( GDALGetRasterDataType( hBand ) ) { case GDT_Byte: cm->imagemode = MS_IMAGEMODE_BYTE; break; case GDT_Int16: cm->imagemode = MS_IMAGEMODE_INT16; break; default: cm->imagemode = MS_IMAGEMODE_FLOAT32; break; } GDALClose( hDS ); msReleaseLock( TLOCK_GDAL ); } /* we must have the bounding box in lat/lon [WGS84(DD)/EPSG:4326] */ cm->llextent = cm->extent; /* Already in latlong .. use directly. */ if( layer->projection.proj != NULL && pj_is_latlong(layer->projection.proj)) { /* no change */ } else if (layer->projection.numargs > 0 && !pj_is_latlong(layer->projection.proj)) /* check the layer projection */ msProjectRect(&(layer->projection), NULL, &(cm->llextent)); else if (layer->map->projection.numargs > 0 && !pj_is_latlong(layer->map->projection.proj)) /* check the map projection */ msProjectRect(&(layer->map->projection), NULL, &(cm->llextent)); else { /* projection was specified in the metadata only (EPSG:... only at the moment) */ projectionObj proj; char projstring[32]; msInitProjection(&proj); /* or bad things happen */ sprintf(projstring, "init=epsg:%.20s", cm->srs+5); if (msLoadProjectionString(&proj, projstring) != 0) return MS_FAILURE; msProjectRect(&proj, NULL, &(cm->llextent)); } return MS_SUCCESS; } #endif /* def USE_WCS_SVR */