diff --git a/CMakeLists.txt b/CMakeLists.txt index a901281f1d..e865a6e873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,7 @@ mapprojhack.c maptree.c mapdebug.c maplexer.c mapquantization.c mapunion.c mapdraw.c maplibxml2.c mapquery.c maputil.c strptime.c mapdrawgdal.c mapraster.c mapuvraster.c mapdummyrenderer.c mapobject.c maprasterquery.c mapwcs.c maperror.c mapogcfilter.c mapregex.c mapwcs11.c mapfile.c -mapogcfiltercommon.c maprendering.c mapwcs20.c mapogcsld.c +mapogcfiltercommon.c maprendering.c mapwcs20.c mapogcsld.c mapmetadata.c mapresample.c mapwfs.c mapgdal.c mapogcsos.c mapscale.c mapwfs11.c mapwfs20.c mapgeomtransform.c mapogroutput.c mapwfslayer.c mapagg.cpp mapkml.cpp mapgeomutil.cpp mapkmlrenderer.cpp fontcache.c textlayout.c maputfgrid.cpp diff --git a/mapmetadata.c b/mapmetadata.c new file mode 100644 index 0000000000..e602f71724 --- /dev/null +++ b/mapmetadata.c @@ -0,0 +1,902 @@ +/********************************************************************** + * $Id$ + * + * Project: MapServer + * Purpose: Metadata implementation + * Author: Tom Kralidis (tomkralidis@gmail.com) + * + ********************************************************************** + * Copyright (c) 2017, Tom Kralidis + * + * 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 + ****************************************************************************/ + +#include "mapserver.h" +#include "mapows.h" +#include "mapowscommon.h" +#include "maplibxml2.h" + + +/************************************************************************/ +/* _msMetadataGetCharacterString */ +/* */ +/* Create a gmd:name/gmd:CharacterString element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetCharacterString(xmlNsPtr namespace, char *name, char *value) { + xmlNsPtr psNsGco = NULL; + xmlNodePtr psNode = NULL; + + psNsGco = xmlNewNs(NULL, BAD_CAST "http://www.isotc211.org/2005/gmd", BAD_CAST "gco"); + + psNode = xmlNewNode(namespace, BAD_CAST name); + + if (!value) + xmlNewNsProp(psNode, psNsGco, BAD_CAST "nilReason", BAD_CAST "missing"); + else + xmlNewChild(psNode, psNsGco, BAD_CAST "CharacterString", BAD_CAST value); + return psNode; + +} + + +/************************************************************************/ +/* _msMetadataGetURL */ +/* */ +/* Create a gmd:name/gmd:URL element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetURL(xmlNsPtr namespace, char *name, char *value) { + xmlNsPtr psNsGco = NULL; + xmlNodePtr psNode = NULL; + + psNsGco = xmlNewNs(NULL, BAD_CAST "http://www.isotc211.org/2005/gmd", BAD_CAST "gco"); + + psNode = xmlNewNode(namespace, BAD_CAST name); + + if (!value) + xmlNewNsProp(psNode, psNsGco, BAD_CAST "nilReason", BAD_CAST "missing"); + else + xmlNewChild(psNode, namespace, BAD_CAST "URL", BAD_CAST value); + return psNode; + +} + +/************************************************************************/ +/* _msMetadataGetOnline */ +/* */ +/* Create a gmd:onLine element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetOnline(xmlNsPtr namespace, layerObj *layer, char *service, char *format, char *desc, char *url_in) { + + int status; + char *url = NULL; + char buffer[32]; + char *epsg_str; + + xmlNodePtr psNode = NULL; + xmlNodePtr psORNode = NULL; + + rectObj rect; + + psNode = xmlNewNode(namespace, BAD_CAST "onLine"); + psORNode = xmlNewChild(psNode, namespace, BAD_CAST "CI_OnlineResource", NULL); + + url = msStrdup(url_in); + + if (strcasecmp(service, "M") == 0) { + url = msStringConcatenate(url, msEncodeHTMLEntities("service=WMS&version=1.3.0&request=GetMap&width=500&height=300&styles=&layers=")); + url = msStringConcatenate(url, msEncodeHTMLEntities(layer->name)); + url = msStringConcatenate(url, msEncodeHTMLEntities("&format=")); + url = msStringConcatenate(url, msEncodeHTMLEntities(format)); + url = msStringConcatenate(url, msEncodeHTMLEntities("&crs=")); + msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "MFCSGO", MS_TRUE, &epsg_str); + url = msStringConcatenate(url, msEncodeHTMLEntities(epsg_str)); + + status = msLayerGetExtent(layer, &rect); + + if (status == 0) { + url = msStringConcatenate(url, msEncodeHTMLEntities("&bbox=")); + sprintf(buffer, "%f", rect.miny); + url = msStringConcatenate(url, buffer); + url = msStringConcatenate(url, ","); + sprintf(buffer, "%f", rect.minx); + url = msStringConcatenate(url, buffer); + url = msStringConcatenate(url, ","); + sprintf(buffer, "%f", rect.maxy); + url = msStringConcatenate(url, buffer); + url = msStringConcatenate(url, ","); + sprintf(buffer, "%f", rect.maxx); + url = msStringConcatenate(url, buffer); + } + } + else if (strcasecmp(service, "F") == 0) { + url = msStringConcatenate(url, msEncodeHTMLEntities("service=WFS&version=1.1.0&request=GetFeature&typename=")); + url = msStringConcatenate(url, msEncodeHTMLEntities(layer->name)); + url = msStringConcatenate(url, msEncodeHTMLEntities("&outputformat=")); + url = msStringConcatenate(url, msEncodeHTMLEntities(format)); + } + else if (strcasecmp(service, "C") == 0) { + url = msStringConcatenate(url, msEncodeHTMLEntities("service=WCS&version=2.0.1&request=GetCoverage&coverageid=")); + url = msStringConcatenate(url, msEncodeHTMLEntities(layer->name)); + url = msStringConcatenate(url, msEncodeHTMLEntities("&format=")); + url = msStringConcatenate(url, msEncodeHTMLEntities(format)); + } + + xmlAddChild(psORNode, _msMetadataGetURL(namespace, "linkage", url)); + + xmlAddChild(psORNode, _msMetadataGetCharacterString(namespace, "protocol", "WWW:DOWNLOAD-1.0-http--download")); + xmlAddChild(psORNode, _msMetadataGetCharacterString(namespace, "name", layer->name)); + + xmlAddChild(psORNode, _msMetadataGetCharacterString(namespace, "description", desc)); + + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetInteger */ +/* */ +/* Create a gmd:name/gmd:Integer element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetInteger(xmlNsPtr namespace, char *name, int value) { + char buffer[8]; + xmlNsPtr psNsGco = NULL; + xmlNodePtr psNode = NULL; + + sprintf(buffer, "%d", value); + + psNsGco = xmlNewNs(NULL, BAD_CAST "http://www.isotc211.org/2005/gco", BAD_CAST "gco"); + + psNode = xmlNewNode(namespace, BAD_CAST name); + + if (!value) + xmlNewNsProp(psNode, psNsGco, BAD_CAST "nilReason", BAD_CAST "missing"); + else + xmlNewChild(psNode, psNsGco, BAD_CAST "Integer", BAD_CAST buffer); + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetDecimal */ +/* */ +/* Create a gmd:name/gmd:Decimal element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetDecimal(xmlNsPtr namespace, char *name, double value) { + char buffer[32]; + xmlNsPtr psNsGco = NULL; + xmlNodePtr psNode = NULL; + + sprintf(buffer, "%f", value); + + psNsGco = xmlNewNs(NULL, BAD_CAST "http://www.isotc211.org/2005/gco", BAD_CAST "gco"); + + psNode = xmlNewNode(namespace, BAD_CAST name); + + if (!value) + xmlNewNsProp(psNode, psNsGco, BAD_CAST "nilReason", BAD_CAST "missing"); + else + xmlNewChild(psNode, psNsGco, BAD_CAST "Decimal", BAD_CAST buffer); + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetCodeList */ +/* */ +/* Create a gmd:name/gmd:* code list element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetCodeList(xmlNsPtr namespace, char *parent_element, char *name, char *value) { + char *codelist = NULL; + xmlNodePtr psNode = NULL; + xmlNodePtr psCodeNode = NULL; + + codelist = msStrdup("http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml#"); + codelist = msStringConcatenate(codelist, name); + + psNode = xmlNewNode(namespace, BAD_CAST parent_element); + psCodeNode = xmlNewChild(psNode, namespace, BAD_CAST name, BAD_CAST value); + xmlNewProp(psCodeNode, BAD_CAST "codeSpace", BAD_CAST "ISOTC211/19115"); + xmlNewProp(psCodeNode, BAD_CAST "codeList", BAD_CAST codelist); + xmlNewProp(psCodeNode, BAD_CAST "codeListValue", BAD_CAST value); + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetDate */ +/* */ +/* Create a gmd:date or gmd:dateStamp element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetDate(xmlNsPtr namespace, char *parent_element, char *date_type, char *value) { + xmlNsPtr psNsGco = NULL; + xmlNodePtr psNode = NULL; + xmlNodePtr psNode2 = NULL; + + psNsGco = xmlNewNs(NULL, BAD_CAST "http://www.isotc211.org/2005/gmd", BAD_CAST "gco"); + + psNode = xmlNewNode(namespace, BAD_CAST parent_element); + + if (date_type == NULL) { /* it's a gmd:dateStamp */ + xmlNewChild(psNode, psNsGco, BAD_CAST "Date", BAD_CAST value); + return psNode; + } + + psNode2 = xmlNewChild(psNode, namespace, BAD_CAST "date", NULL); + xmlNewChild(psNode2, psNsGco, BAD_CAST "Date", BAD_CAST value); + xmlAddChild(psNode, _msMetadataGetCodeList(namespace, "dateType", "CI_DateTypeCode", date_type)); + + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetGMLTimePeriod */ +/* */ +/* Create a gml:TimePeriod element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetGMLTimePeriod(char **temporal) +{ + xmlNsPtr psNsGml = NULL; + xmlNodePtr psNode = NULL; + + psNsGml = xmlNewNs(NULL, BAD_CAST "http://www.opengis.net/gml", BAD_CAST "gml"); + psNode = xmlNewNode(psNsGml, BAD_CAST "TimePeriod"); + xmlNewNsProp(psNode, psNsGml, BAD_CAST "id", BAD_CAST "T001"); + xmlNewNs(psNode, BAD_CAST "http://www.opengis.net/gml", BAD_CAST "gml"); + xmlSetNs(psNode, psNsGml); + xmlNewChild(psNode, psNsGml, BAD_CAST "beginPosition", BAD_CAST temporal[0]); + xmlNewChild(psNode, psNsGml, BAD_CAST "endPosition", BAD_CAST temporal[1]); + + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetExtent */ +/* */ +/* Create a gmd:extent element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetExtent(xmlNsPtr namespace, layerObj *layer) +{ + int n; + int status; + char *value = NULL; + char **temporal = NULL; + xmlNodePtr psNode = NULL; + xmlNodePtr psEXNode = NULL; + xmlNodePtr psGNode = NULL; + xmlNodePtr psGNode2 = NULL; + xmlNodePtr psTNode = NULL; + xmlNodePtr psTNode2 = NULL; + xmlNodePtr psENode = NULL; + + rectObj rect; + + psNode = xmlNewNode(namespace, BAD_CAST "extent"); + psEXNode = xmlNewChild(psNode, namespace, BAD_CAST "EX_Extent", NULL); + + /* scan for geospatial extent */ + status = msLayerGetExtent(layer, &rect); + + if (status == 0) { + /* always project to lat long */ + msOWSProjectToWGS84(&layer->projection, &rect); + + psGNode = xmlNewChild(psEXNode, namespace, BAD_CAST "geographicElement", NULL); + psGNode2 = xmlNewChild(psGNode, namespace, BAD_CAST "EX_GeographicBoundingBox", NULL); + xmlAddChild(psGNode2, _msMetadataGetDecimal(namespace, "westBoundLongitude", rect.minx)); + xmlAddChild(psGNode2, _msMetadataGetDecimal(namespace, "eastBoundLongitude", rect.maxx)); + xmlAddChild(psGNode2, _msMetadataGetDecimal(namespace, "southBoundLatitude", rect.miny)); + xmlAddChild(psGNode2, _msMetadataGetDecimal(namespace, "northBoundLatitude", rect.maxy)); + } + + /* scan for temporal extent */ + value = (char *)msOWSLookupMetadata(&(layer->metadata), "MO", "timeextent"); + if (value) { /* WMS */ + temporal = msStringSplit(value, '/', &n); + } + else { /* WCS */ + value = (char *)msOWSLookupMetadata(&(layer->metadata), "CO", "timeposition"); + if (value) { + /* split extent */ + temporal = msStringSplit(value, ',', &n); + } + } + if (!value) { /* SOS */ + value = (char *)msOWSLookupMetadata(&(layer->metadata), "SO", "offering_timeextent"); + if (value) + temporal = msStringSplit(value, '/', &n); + } + if (value) { + if (temporal && n > 0) { + psTNode = xmlNewChild(psEXNode, namespace, BAD_CAST "temporalElement", NULL); + psTNode2 = xmlNewChild(psTNode, namespace, BAD_CAST "EX_TemporalExtent", NULL); + psENode = xmlNewChild(psTNode2, namespace, BAD_CAST "extent", NULL); + xmlAddChild(psENode, _msMetadataGetGMLTimePeriod(temporal)); + + msFreeCharArray(temporal, n); + } + } + + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetReferenceSystemInfo */ +/* */ +/* Create a gmd:referenceSystemInfo element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetReferenceSystemInfo(xmlNsPtr namespace, layerObj *layer) +{ + char *epsg_str; + xmlNodePtr psNode = NULL; + xmlNodePtr psRSNode = NULL; + xmlNodePtr psRSINode = NULL; + xmlNodePtr psRSINode2 = NULL; + + psNode = xmlNewNode(namespace, BAD_CAST "referenceSystemInfo"); + psRSNode = xmlNewChild(psNode, namespace, BAD_CAST "MD_ReferenceSystem", NULL); + psRSINode = xmlNewChild(psRSNode, namespace, BAD_CAST "referenceSystemIdentifier", NULL); + psRSINode2 = xmlNewChild(psRSINode, namespace, BAD_CAST "RS_Identifier", NULL); + + msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "MFCSGO", MS_TRUE, &epsg_str); + xmlAddChild(psRSINode2, _msMetadataGetCharacterString(namespace, "code", epsg_str)); + xmlAddChild(psRSINode2, _msMetadataGetCharacterString(namespace, "codeSpace", "http://www.epsg-registry.org")); + xmlAddChild(psRSINode2, _msMetadataGetCharacterString(namespace, "version", "6.14")); + + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetContact */ +/* */ +/* Create a gmd:contact element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetContact(xmlNsPtr namespace, char *contact_element, mapObj *map) +{ + char *value = NULL; + xmlNodePtr psNode = NULL; + xmlNodePtr psCNode = NULL; + xmlNodePtr psCINode = NULL; + xmlNodePtr psCINode2 = NULL; + xmlNodePtr psPhoneNode = NULL; + xmlNodePtr psCIPhoneNode = NULL; + xmlNodePtr psAddressNode = NULL; + xmlNodePtr psCIAddressNode = NULL; + xmlNodePtr psORNode = NULL; + xmlNodePtr psORNode2 = NULL; + + psNode = xmlNewNode(namespace, BAD_CAST contact_element); + psCNode = xmlNewChild(psNode, namespace, BAD_CAST "CI_ResponsibleParty", NULL); + xmlNewProp(psCNode, BAD_CAST "id", BAD_CAST contact_element); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "contactperson"); + if (value) + xmlAddChild(psCNode, _msMetadataGetCharacterString(namespace, "individualName", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "contactorganization"); + if (value) + xmlAddChild(psCNode, _msMetadataGetCharacterString(namespace, "organisationName", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "contactposition"); + if (value) + xmlAddChild(psCNode, _msMetadataGetCharacterString(namespace, "positionName", value)); + + psCINode = xmlNewChild(psCNode, namespace, BAD_CAST "contactInfo", NULL); + psCINode2 = xmlNewChild(psCINode, namespace, BAD_CAST "CI_Contact", NULL); + psPhoneNode = xmlNewChild(psCINode2, namespace, BAD_CAST "phone", NULL); + psCIPhoneNode = xmlNewChild(psPhoneNode, namespace, BAD_CAST "CI_Telephone", NULL); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "contactvoicetelephone"); + if (value) + xmlAddChild(psCIPhoneNode, _msMetadataGetCharacterString(namespace, "voice", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "contactfacsimiletelephone"); + if (value) + xmlAddChild(psCIPhoneNode, _msMetadataGetCharacterString(namespace, "facsimile", value)); + + psAddressNode = xmlNewChild(psCINode2, namespace, BAD_CAST "address", NULL); + psCIAddressNode = xmlNewChild(psAddressNode, namespace, BAD_CAST "CI_Address", NULL); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "address"); + if (value) + xmlAddChild(psCIAddressNode, _msMetadataGetCharacterString(namespace, "deliveryPoint", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "city"); + if (value) + xmlAddChild(psCIAddressNode, _msMetadataGetCharacterString(namespace, "city", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "stateorprovince"); + if (value) + xmlAddChild(psCIAddressNode, _msMetadataGetCharacterString(namespace, "administrativeArea", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "postcode"); + if (value) + xmlAddChild(psCIAddressNode, _msMetadataGetCharacterString(namespace, "postalCode", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "country"); + if (value) + xmlAddChild(psCIAddressNode, _msMetadataGetCharacterString(namespace, "country", value)); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "contactelectronicmailaddress"); + if (value) + xmlAddChild(psCIAddressNode, _msMetadataGetCharacterString(namespace, "electronicMailAddress", value)); + + psORNode = xmlNewChild(psCINode2, namespace, BAD_CAST "onlineResource", NULL); + psORNode2 = xmlNewChild(psORNode, namespace, BAD_CAST "CI_OnlineResource", NULL); + + value = (char *)msOWSLookupMetadata(&(map->web.metadata), "MCFO", "onlineresource"); + if (value) + xmlAddChild(psORNode2, _msMetadataGetURL(namespace, "linkage", value)); + + xmlAddChild(psCNode, _msMetadataGetCodeList(namespace, "role", "CI_RoleCode", "pointOfContact")); + + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetIdentificationInfo */ +/* */ +/* Create a gmd:identificationInfo element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetIdentificationInfo(xmlNsPtr namespace, mapObj *map, layerObj *layer) +{ + int i = 0; + int n; + char *value; + char **tokens = NULL; + xmlNodePtr psNode = NULL; + xmlNodePtr psDINode = NULL; + xmlNodePtr psCNode = NULL; + xmlNodePtr psCINode = NULL; + xmlNodePtr psDNode = NULL; + xmlNodePtr psKWNode = NULL; + xmlNodePtr psMDKNode = NULL; + + psNode = xmlNewNode(namespace, BAD_CAST "identificationInfo"); + + psDINode = xmlNewChild(psNode, namespace, BAD_CAST "MD_DataIdentification", NULL); + xmlNewProp(psDINode, BAD_CAST "id", BAD_CAST layer->name); + psCNode = xmlNewChild(psDINode, namespace, BAD_CAST "citation", NULL); + psCINode = xmlNewChild(psCNode, namespace, BAD_CAST "CI_Citation", NULL); + + value = (char *)msOWSLookupMetadata(&(layer->metadata), "MCFGO", "title"); + if (!value) + value = (char *)msOWSLookupMetadata(&(layer->metadata), "S", "offering_name"); + xmlAddChild(psCINode, _msMetadataGetCharacterString(namespace, "title", value)); + + psDNode = xmlNewChild(psCINode, namespace, BAD_CAST "date", NULL); + + xmlAddChild(psDNode, _msMetadataGetDate(namespace, "CI_Date", "publication", "2011")); + + value = (char *)msOWSLookupMetadata(&(layer->metadata), "MCFGO", "abstract"); + if (!value) + value = (char *)msOWSLookupMetadata(&(layer->metadata), "S", "offering_description"); + xmlAddChild(psDINode, _msMetadataGetCharacterString(namespace, "abstract", value)); + + value = (char *)msOWSLookupMetadata(&(layer->metadata), "MCFSGO", "keywordlist"); + + if (value) { + psKWNode = xmlNewChild(psDINode, namespace, BAD_CAST "descriptiveKeywords", NULL); + psMDKNode = xmlNewChild(psKWNode, namespace, BAD_CAST "MD_Keywords", NULL); + + tokens = msStringSplit(value, ',', &n); + if (tokens && n > 0) { + for (i=0; itype == MS_LAYER_RASTER) { + psSRNode = xmlNewChild(psNode, namespace, BAD_CAST "MD_GridSpatialRepresentation", NULL); + xmlAddChild(psSRNode, _msMetadataGetInteger(namespace, "numberOfDimensions", 2)); + xmlAddChild(psSRNode, _msMetadataGetCodeList(namespace, "cellGeometry", "MD_CellGeometryCode", "area")); + } + else { + psSRNode = xmlNewChild(psNode, namespace, BAD_CAST "MD_VectorSpatialRepresentation", NULL); + xmlAddChild(psSRNode, _msMetadataGetCodeList(namespace, "topologyLevel", "MD_TopologyLevelCode", "geometryOnly")); + psGONode = xmlNewChild(psSRNode, namespace, BAD_CAST "geometricObjects", NULL); + psGONode2 = xmlNewChild(psGONode, namespace, BAD_CAST "MD_GeometricObjects", NULL); + if (layer->type == MS_LAYER_POINT) + value = "point"; + else if (layer->type == MS_LAYER_LINE) + value = "curve"; + else if (layer->type == MS_LAYER_POLYGON) + value = "surface"; + else + value = "complex"; + xmlAddChild(psGONode2, _msMetadataGetCodeList(namespace, "geometricObjectType", "MD_GeometricObjectTypeCode", value)); + // TODO: find way to get feature count in a fast way + /* xmlAddChild(psGONode2, _msMetadataGetInteger(namespace, "geometricObjectCount", msLayerGetNumFeatures(layer))); */ + } + + return psNode; +} + + +/************************************************************************/ +/* _msMetadataGetDistributionInfo */ +/* */ +/* Create a gmd:identificationInfo element pattern */ +/************************************************************************/ + +xmlNodePtr _msMetadataGetDistributionInfo(xmlNsPtr namespace, mapObj *map, layerObj *layer, cgiRequestObj *cgi_request) +{ + char *url = NULL; + xmlNodePtr psNode = NULL; + xmlNodePtr psMDNode = NULL; + xmlNodePtr psTONode = NULL; + xmlNodePtr psDTONode = NULL; + xmlNodePtr psDNode = NULL; + xmlNodePtr psDNode2 = NULL; + + psNode = xmlNewNode(namespace, BAD_CAST "distributionInfo"); + psMDNode = xmlNewChild(psNode, namespace, BAD_CAST "MD_Distribution", NULL); + + url = msEncodeHTMLEntities(msOWSGetOnlineResource(map, "MFCSGO", "onlineresource", cgi_request)); + + /* gmd:distributor */ + psDNode = xmlNewChild(psMDNode, namespace, BAD_CAST "distributor", NULL); + psDNode2 = xmlNewChild(psDNode, namespace, BAD_CAST "MD_Distributor", NULL); + xmlAddChild(psDNode2, _msMetadataGetContact(namespace, "distributorContact", map)); + + /* gmd:transferOptions */ + psTONode = xmlNewChild(psMDNode, namespace, BAD_CAST "transferOptions", NULL); + psDTONode = xmlNewChild(psTONode, namespace, BAD_CAST "MD_DigitalTransferOptions", NULL); + xmlAddChild(psDTONode, _msMetadataGetCharacterString(namespace, "unitsOfDistribution", "KB")); + + /* links */ + + /* WMS */ + xmlAddChild(psDTONode, _msMetadataGetOnline(namespace, layer, "M", "image/png", "PNG Format", url)); + + xmlAddChild(psDTONode, _msMetadataGetOnline(namespace, layer, "M", "image/jpeg", "JPEG Format", url)); + xmlAddChild(psDTONode, _msMetadataGetOnline(namespace, layer, "M", "image/gif", "GIF Format", url)); + + /* WCS */ + if (layer->type == MS_LAYER_RASTER) { + xmlAddChild(psDTONode, _msMetadataGetOnline(namespace, layer, "C", "image/tiff", "GeoTIFF Format", url)); + } + /* WFS */ + else { + xmlAddChild(psDTONode, _msMetadataGetOnline(namespace, layer, "F", "GML2", "GML2 Format", url)); + xmlAddChild(psDTONode, _msMetadataGetOnline(namespace, layer, "F", "GML3", "GML3 Format", url)); + } + + return psNode; +} + +/************************************************************************/ +/* msMetadataGetExceptionReport */ +/* */ +/* Generate an OWS Common Exception Report */ +/************************************************************************/ + +xmlNodePtr msMetadataGetExceptionReport(mapObj *map, char *code, char *locator, char *message) +{ + char *schemas_location = NULL; + + xmlNsPtr psNsOws = NULL; + xmlNodePtr psRootNode = NULL; + + schemas_location = msEncodeHTMLEntities(msOWSGetSchemasLocation(map)); + psNsOws = xmlNewNs(NULL, BAD_CAST "http://www.opengis.net/ows/1.1", BAD_CAST "ows"); + + psRootNode = msOWSCommonExceptionReport(psNsOws, + OWS_1_1_0, + schemas_location, + "1.1.0", + msOWSGetLanguage(map, "exception"), + code, locator, message); + + xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/ows/1.1", BAD_CAST "ows"); + + return psRootNode; + +} + +/************************************************************************/ +/* msMetadataGetLayerMetadata */ +/* */ +/* Generate an ISO 19139:2007 representation of layer metadata */ +/************************************************************************/ + +xmlNodePtr msMetadataGetLayerMetadata(mapObj *map, metadataParamsObj *paramsObj, cgiRequestObj *cgi_request, owsRequestObj *ows_request) +{ + int i; + int layer_found = MS_FALSE; + + xmlNodePtr psRootNode = NULL; + xmlNsPtr psNsGmd = NULL; + xmlNsPtr psNsXsi = NULL; + + layerObj *layer = NULL; + + psNsXsi = xmlNewNs(NULL, BAD_CAST "http://www.w3.org/2001/XMLSchema-instance", BAD_CAST "xsi"); + + /* Check that layer requested exists in mapfile */ + for (i=0; inumlayers; i++) { + if(strcasecmp(GET_LAYER(map, i)->name, paramsObj->pszLayer) == 0) { + layer_found = MS_TRUE; + layer = GET_LAYER(map, i); + break; + } + } + + if (layer_found == MS_FALSE) { + psRootNode = msMetadataGetExceptionReport(map, "InvalidParameterValue", "layer", "Layer not found"); + } + + /* Check that outputschema is valid */ + else if (paramsObj->pszOutputSchema && strcasecmp(paramsObj->pszOutputSchema, "http://www.isotc211.org/2005/gmd") != 0) { + psRootNode = msMetadataGetExceptionReport(map, "InvalidParameterValue", "outputschema", "OUTPUTSCHEMA must be \"http://www.isotc211.org/2005/gmd\""); + } + + else { + /* root element */ + psNsGmd = xmlNewNs(NULL, BAD_CAST "http://www.isotc211.org/2005/gmd", BAD_CAST "gmd"); + psRootNode = xmlNewNode(NULL, BAD_CAST "MD_Metadata"); + xmlNewNs(psRootNode, BAD_CAST "http://www.isotc211.org/2005/gmd", BAD_CAST "gmd"); + xmlNewNs(psRootNode, BAD_CAST "http://www.isotc211.org/2005/gco", BAD_CAST "gco"); + xmlNewNs(psRootNode, BAD_CAST "http://www.w3.org/2001/XMLSchema-instance", BAD_CAST "xsi"); + xmlSetNs(psRootNode, psNsGmd); + + xmlNewNsProp(psRootNode, psNsXsi, BAD_CAST "schemaLocation", BAD_CAST "http://www.isotc211.org/2005/gmd http://www.isotc211.org/2005/gmd/gmd.xsd"); + + /* gmd:identifier */ + xmlAddChild(psRootNode, _msMetadataGetCharacterString(psNsGmd, "fileIdentifier", layer->name)); + + /* gmd:language */ + xmlAddChild(psRootNode, _msMetadataGetCharacterString(psNsGmd, "language", (char *)msOWSGetLanguage(map, "exception"))); + + /* gmd:hierarchyLevel */ + xmlAddChild(psRootNode, _msMetadataGetCodeList(psNsGmd, "hierarchyLevel", "MD_ScopeCode", "dataset")); + + /* gmd:contact */ + xmlAddChild(psRootNode, _msMetadataGetContact(psNsGmd, "contact", map)); + + /* gmd:dateStamp */ + /* TODO: nil for now, find way to derive this automagically */ + xmlAddChild(psRootNode, _msMetadataGetCharacterString(psNsGmd, "dateStamp", NULL)); + + /* gmd:metadataStandardName */ + xmlAddChild(psRootNode, _msMetadataGetCharacterString(psNsGmd, "metadataStandardName", "ISO 19115:2003 - Geographic information - Metadata")); + + /* gmd:metadataStandardVersion */ + xmlAddChild(psRootNode, _msMetadataGetCharacterString(psNsGmd, "metadataStandardVersion", "ISO 19115:2003")); + + /* gmd:spatialRepresentationInfo */ + xmlAddChild(psRootNode, _msMetadataGetSpatialRepresentationInfo(psNsGmd, layer)); + + /* gmd:referenceSystemInfo */ + xmlAddChild(psRootNode, _msMetadataGetReferenceSystemInfo(psNsGmd, layer)); + + /* gmd:identificationInfo */ + xmlAddChild(psRootNode, _msMetadataGetIdentificationInfo(psNsGmd, map, layer)); + + /* gmd:distributionInfo */ + xmlAddChild(psRootNode, _msMetadataGetDistributionInfo(psNsGmd, map, layer, cgi_request)); + } + + return psRootNode; +} + +/************************************************************************/ +/* msMetadataDispatch */ +/* */ +/* Entry point for metadata requests. */ +/* */ +/* - If this is a valid request then it is processed and MS_SUCCESS */ +/* is returned on success, or MS_FAILURE on failure. */ +/* */ +/* - If this does not appear to be a valid WFS request then MS_DONE */ +/* is returned and MapServer is expected to process this as a regular */ +/* MapServer request. */ +/************************************************************************/ + +int msMetadataDispatch(mapObj *map, cgiRequestObj *cgi_request, owsRequestObj *ows_request) +{ + int i; + int buffersize; + int status = MS_SUCCESS; + xmlNodePtr psRootNode = NULL; + xmlDocPtr xml_document; + xmlChar *xml_buffer; + metadataParamsObj *paramsObj; + layerObj *layer = NULL; + + /* Populate the Params object based on the request */ + + paramsObj = msMetadataCreateParamsObj(); + + xml_document = xmlNewDoc(BAD_CAST "1.0"); + xmlNewNs(NULL, BAD_CAST "http://www.w3.org/2001/XMLSchema-instance", BAD_CAST "xsi"); + + if (msMetadataParseRequest(map, cgi_request, ows_request, paramsObj) == MS_FAILURE) { + psRootNode = msMetadataGetExceptionReport(map, "InvalidRequest", "layer", "Request parsing failed"); + status = MS_FAILURE; + } + + /* if layer= is not specified, */ + if (paramsObj->pszLayer==NULL || strlen(paramsObj->pszLayer)<=0) { + psRootNode = msMetadataGetExceptionReport(map, "MissingParameterValue", "layer", "Missing layer parameter"); + status = MS_FAILURE; + } + + if (status == MS_SUCCESS) { /* Start dispatching request */ + /* Check that layer requested exists in mapfile */ + for (i=0; inumlayers; i++) { + if(strcasecmp(GET_LAYER(map, i)->name, paramsObj->pszLayer) == 0) { + layer = GET_LAYER(map, i); + break; + } + } + if (layer != NULL && msOWSLookupMetadata(&(layer->metadata), "MFCO", "metadataurl_href")) { + msIO_setHeader("Status", "301 Moved Permanently"); + msIO_setHeader("Location", msOWSLookupMetadata(&(layer->metadata), "MFCO", "metadataurl_href")); + msIO_sendHeaders(); + } + else { + psRootNode = msMetadataGetLayerMetadata(map, paramsObj, cgi_request, ows_request); + } + } + + if (psRootNode != NULL) { + xmlDocSetRootElement(xml_document, psRootNode); + + msIO_setHeader("Content-type", "text/xml"); + msIO_sendHeaders(); + + xmlDocDumpFormatMemory(xml_document, &xml_buffer, &buffersize, 1); + msIO_printf((char *) xml_buffer); + + xmlFree(xml_buffer); + xmlFreeDoc(xml_document); + msMetadataFreeParamsObj(paramsObj); + free(paramsObj); + paramsObj = NULL; + } + + return status; +} + +/************************************************************************/ +/* msMetadataCreateParamsObj */ +/* */ +/* Create a parameter object, initialize it. */ +/* The caller should free the object using msMetadataFreeParamsObj. */ +/************************************************************************/ + +metadataParamsObj *msMetadataCreateParamsObj() +{ + metadataParamsObj *paramsObj = (metadataParamsObj *)calloc(1, sizeof(metadataParamsObj)); + MS_CHECK_ALLOC(paramsObj, sizeof(metadataParamsObj), NULL); + + paramsObj->pszLayer = NULL; + paramsObj->pszOutputSchema = NULL; + return paramsObj; +} + + +/************************************************************************/ +/* msMetadataFreeParmsObj */ +/* */ +/* Free params object. */ +/************************************************************************/ + +void msMetadataFreeParamsObj(metadataParamsObj *metadataparams) +{ + if (metadataparams) { + free(metadataparams->pszRequest); + free(metadataparams->pszLayer); + free(metadataparams->pszOutputSchema); + } +} + + +/************************************************************************/ +/* msMetadataParseRequest */ +/* */ +/* Parse request into the params object. */ +/************************************************************************/ + +int msMetadataParseRequest(mapObj *map, cgiRequestObj *request, owsRequestObj *ows_request, + metadataParamsObj *metadataparams) +{ + int i = 0; + + if (!request || !metadataparams) + return MS_FAILURE; + + if (request->NumParams > 0) { + for(i=0; iNumParams; i++) { + if (request->ParamNames[i] && request->ParamValues[i]) { + if (strcasecmp(request->ParamNames[i], "LAYER") == 0) + metadataparams->pszLayer = msStrdup(request->ParamValues[i]); + if (strcasecmp(request->ParamNames[i], "OUTPUTSCHEMA") == 0) + metadataparams->pszOutputSchema = msStrdup(request->ParamValues[i]); + } + } + } + return MS_SUCCESS; +} + +/************************************************************************/ +/* msMetadataSetGetMetadataURL */ +/* */ +/* Parse request into the params object. */ +/************************************************************************/ + +void msMetadataSetGetMetadataURL(layerObj *lp, const char *url) +{ + char *pszMetadataURL=NULL; + + pszMetadataURL = msStrdup(url); + msDecodeHTMLEntities(pszMetadataURL); + pszMetadataURL = msStringConcatenate(pszMetadataURL, "request=GetMetadata&layer="); + pszMetadataURL = msStringConcatenate(pszMetadataURL, lp->name); + + msInsertHashTable(&(lp->metadata), "ows_metadataurl_href", pszMetadataURL); + msInsertHashTable(&(lp->metadata), "ows_metadataurl_type", "ISOTC211/19115"); + msInsertHashTable(&(lp->metadata), "ows_metadataurl_format", "text/xml"); + msInsertHashTable(&(lp->metadata), "ows_metadatalink_href", pszMetadataURL); + msInsertHashTable(&(lp->metadata), "ows_metadatalink_type", "ISOTC211/19115"); + msInsertHashTable(&(lp->metadata), "ows_metadatalink_format", "text/xml"); + msFree(pszMetadataURL); +} + diff --git a/mapows.c b/mapows.c index ed20103bc9..b7ddd5eb75 100644 --- a/mapows.c +++ b/mapows.c @@ -264,7 +264,11 @@ int msOWSDispatch(mapObj *map, cgiRequestObj *request, int ows_mode) } if (ows_request.service == NULL) { - + if (ows_request.request && EQUAL(ows_request.request, "GetMetadata")) { + status = msMetadataDispatch(map, request, &ows_request); + msOWSClearRequestObj(&ows_request); + return status; + } #ifdef USE_WFS_SVR if( msOWSLookupMetadata(&(map->web.metadata), "FO", "cite_wfs2") != NULL ) { status = msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, NULL ); diff --git a/mapows.h b/mapows.h index 1498e1455a..5654cf1831 100644 --- a/mapows.h +++ b/mapows.h @@ -119,6 +119,13 @@ typedef struct { char *httpcookiedata; } wmsParamsObj; +/* metadataParamsObj: Represent a metadata specific request with its enabled layers */ +typedef struct { + char *pszRequest; + char *pszLayer; + char *pszOutputSchema; +} metadataParamsObj; + /* owsRequestObj: Represent a OWS specific request with its enabled layers */ typedef struct { int numlayers; @@ -460,6 +467,16 @@ MS_DLL_EXPORT char *msWMSGetFeatureInfoURL(mapObj *map, layerObj *lp, int msWMSLayerExecuteRequest(mapObj *map, int nOWSLayers, int nClickX, int nClickY, int nFeatureCount, const char *pszInfoFormat, int type); +/*==================================================================== + * mapmetadata.c + *====================================================================*/ +metadataParamsObj *msMetadataCreateParamsObj(void); +void msMetadataFreeParamsObj(metadataParamsObj *metadataparams); +int msMetadataParseRequest(mapObj *map, cgiRequestObj *request, owsRequestObj *ows_request, + metadataParamsObj *metadataparams); +int msMetadataDispatch(mapObj *map, cgiRequestObj *requestobj, owsRequestObj *ows_request); +void msMetadataSetGetMetadataURL(layerObj *lp, const char *url); + /*==================================================================== * mapwfs.c *====================================================================*/ @@ -484,7 +501,8 @@ int msWFSGetCapabilities11(mapObj *map, wfsParamsObj *wfsparams, cgiRequestObj *req, owsRequestObj *ows_request); #ifdef USE_LIBXML2 xmlNodePtr msWFSDumpLayer11(mapObj *map, layerObj *lp, xmlNsPtr psNsOws, - int nWFSVersion, const char* validate_language); + int nWFSVersion, const char* validate_language, + char *script_url); #endif char *msWFSGetOutputFormatList(mapObj *map, layerObj *layer, int nWFSVersion); diff --git a/mapwcs.c b/mapwcs.c index 9f9ec43236..8029cd6b0b 100644 --- a/mapwcs.c +++ b/mapwcs.c @@ -819,7 +819,7 @@ static int msWCSGetCapabilities_Capability(mapObj *map, wcsParamsObj *params, cg /* msWCSGetCapabilities_CoverageOfferingBrief() */ /************************************************************************/ -static int msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer, wcsParamsObj *params) +static int msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer, wcsParamsObj *params, char *script_url_encoded) { coverageMetadataObj cm; int status; @@ -833,6 +833,9 @@ static int msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer, wcsParams msIO_printf(" \n"); /* is this tag right? (I hate schemas without ANY examples) */ /* optional metadataLink */ + if (! msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_href")) + msMetadataSetGetMetadataURL(layer, script_url_encoded); + msOWSPrintURLType(stdout, &(layer->metadata), "CO", "metadatalink", OWS_NOERR, " ", @@ -869,9 +872,12 @@ static int msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer, wcsParams /* msWCSGetCapabilities_ContentMetadata() */ /************************************************************************/ -static int msWCSGetCapabilities_ContentMetadata(mapObj *map, wcsParamsObj *params, owsRequestObj *ows_request) +static int msWCSGetCapabilities_ContentMetadata(mapObj *map, wcsParamsObj *params, owsRequestObj *ows_request, cgiRequestObj *req) { int i; + char *script_url_encoded=NULL; + + script_url_encoded = msEncodeHTMLEntities(msOWSGetOnlineResource(map, "CO", "onlineresource", req)); /* 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 */ @@ -894,7 +900,7 @@ static int msWCSGetCapabilities_ContentMetadata(mapObj *map, wcsParamsObj *param if (!msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers)) continue; - if( msWCSGetCapabilities_CoverageOfferingBrief((GET_LAYER(map, i)), params) != MS_SUCCESS ) { + if(msWCSGetCapabilities_CoverageOfferingBrief((GET_LAYER(map, i)), params, script_url_encoded) != MS_SUCCESS ) { msIO_printf("\n"); return MS_FAILURE; } @@ -1006,12 +1012,12 @@ static int msWCSGetCapabilities(mapObj *map, wcsParamsObj *params, cgiRequestObj msWCSGetCapabilities_Capability(map, params, req); if(!params->section || strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata") == 0) - msWCSGetCapabilities_ContentMetadata(map, params, ows_request); + msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req); if(params->section && strcasecmp(params->section, "/") == 0) { msWCSGetCapabilities_Service(map, params); msWCSGetCapabilities_Capability(map, params, req); - msWCSGetCapabilities_ContentMetadata(map, params, ows_request); + msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req); } /* done */ @@ -1094,7 +1100,7 @@ static int msWCSDescribeCoverage_AxisDescription(layerObj *layer, char *name) /* msWCSDescribeCoverage_CoverageOffering() */ /************************************************************************/ -static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer, wcsParamsObj *params) +static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer, wcsParamsObj *params, char *script_url_encoded) { char **tokens; int numtokens; @@ -1119,6 +1125,9 @@ static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer, wcsParamsObj msIO_printf(" \n"); /* optional metadataLink */ + if (! msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_href")) + msMetadataSetGetMetadataURL(layer, script_url_encoded); + msOWSPrintURLType(stdout, &(layer->metadata), "CO", "metadatalink", OWS_NOERR, " ", @@ -1306,7 +1315,7 @@ static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer, wcsParamsObj /* msWCSDescribeCoverage() */ /************************************************************************/ -static int msWCSDescribeCoverage(mapObj *map, wcsParamsObj *params, owsRequestObj *ows_request) +static int msWCSDescribeCoverage(mapObj *map, wcsParamsObj *params, owsRequestObj *ows_request, cgiRequestObj *req) { int i = 0,j = 0, k = 0; const char *updatesequence=NULL; @@ -1314,6 +1323,9 @@ static int msWCSDescribeCoverage(mapObj *map, wcsParamsObj *params, owsRequestOb int numcoverages=0; char *coverageName=NULL; + char *script_url_encoded=NULL; + + script_url_encoded = msEncodeHTMLEntities(msOWSGetOnlineResource(map, "CO", "onlineresource", req)); /* -------------------------------------------------------------------- */ /* 1.1.x is sufficiently different we have a whole case for */ @@ -1384,7 +1396,7 @@ this request. Check wcs/ows_enable_request settings.", "msWCSDescribeCoverage()" } msFree(coverageName); } - msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params); + msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params, script_url_encoded); } msFreeCharArray(coverages,numcoverages); } @@ -1393,7 +1405,7 @@ this request. Check wcs/ows_enable_request settings.", "msWCSDescribeCoverage()" if (!msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers)) continue; - msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params); + msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params, script_url_encoded); } } @@ -2242,7 +2254,7 @@ int msWCSDispatch(mapObj *map, cgiRequestObj *request, owsRequestObj *ows_reques if (operation == MS_WCS_GET_CAPABILITIES) { retVal = msWCSGetCapabilities(map, params, request, ows_request); } else if (operation == MS_WCS_DESCRIBE_COVERAGE) { - retVal = msWCSDescribeCoverage(map, params, ows_request); + retVal = msWCSDescribeCoverage(map, params, ows_request, request); } else if (operation == MS_WCS_GET_COVERAGE) { retVal = msWCSGetCoverage(map, request, params, ows_request); } diff --git a/mapwfs.c b/mapwfs.c index cb897a9a18..dbacb5bc3b 100644 --- a/mapwfs.c +++ b/mapwfs.c @@ -545,7 +545,7 @@ static layerObj* msWFSGetLayerByName(mapObj* map, owsRequestObj *ows_request, co /* ** msWFSDumpLayer() */ -int msWFSDumpLayer(mapObj *map, layerObj *lp) +int msWFSDumpLayer(mapObj *map, layerObj *lp, const char *script_url_encoded) { rectObj ext; char *pszWfsSrs = NULL; @@ -613,8 +613,11 @@ int msWFSDumpLayer(mapObj *map, layerObj *lp) msIO_printf("\n"); } + if (! msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_href")) + msMetadataSetGetMetadataURL(lp, script_url_encoded); + msOWSPrintURLType(stdout, &(lp->metadata), "FO", "metadataurl", - OWS_NOERR, NULL, "MetadataURL", " type=\"%s\"", + OWS_WARN, NULL, "MetadataURL", " type=\"%s\"", NULL, NULL, " format=\"%s\"", "%s", MS_TRUE, MS_FALSE, MS_FALSE, MS_TRUE, MS_TRUE, NULL, NULL, NULL, NULL, NULL, " "); @@ -869,7 +872,7 @@ int msWFSGetCapabilities(mapObj *map, wfsParamsObj *wfsparams, cgiRequestObj *re continue; if (msWFSIsLayerAllowed(lp, ows_request)) { - msWFSDumpLayer(map, lp); + msWFSDumpLayer(map, lp, script_url_encoded); } } diff --git a/mapwfs11.c b/mapwfs11.c index 16077bb228..2d5011156c 100644 --- a/mapwfs11.c +++ b/mapwfs11.c @@ -94,7 +94,8 @@ int msWFSException11(mapObj *map, const char *locator, /* msWFSDumpLayer11 */ /************************************************************************/ xmlNodePtr msWFSDumpLayer11(mapObj *map, layerObj *lp, xmlNsPtr psNsOws, - int nWFSVersion, const char* validate_language) + int nWFSVersion, const char* validate_language, + char *script_url) { rectObj ext; @@ -222,6 +223,8 @@ xmlNodePtr msWFSDumpLayer11(mapObj *map, layerObj *lp, xmlNsPtr psNsOws, xmlNewComment(BAD_CAST "WARNING: Optional WGS84BoundingBox could not be established for this layer. Consider setting the EXTENT in the LAYER object, or wfs_extent metadata. Also check that your data exists in the DATA statement")); } + if (! msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_href")) + msMetadataSetGetMetadataURL(lp, script_url); value = msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_href"); if (value) { @@ -454,7 +457,7 @@ int msWFSGetCapabilities11(mapObj *map, wfsParamsObj *params, /* List only vector layers in which DUMP=TRUE */ if (msWFSIsLayerSupported(lp)) - xmlAddChild(psFtNode, msWFSDumpLayer11(map, lp, psNsOws, OWS_1_1_0, NULL)); + xmlAddChild(psFtNode, msWFSDumpLayer11(map, lp, psNsOws, OWS_1_1_0, NULL, script_url)); } diff --git a/mapwfs20.c b/mapwfs20.c index 50da8443e6..3a7528eebc 100644 --- a/mapwfs20.c +++ b/mapwfs20.c @@ -711,7 +711,7 @@ int msWFSGetCapabilities20(mapObj *map, wfsParamsObj *params, if (msWFSIsLayerSupported(lp)) { if( psFtNode != NULL ) { - xmlAddChild(psFtNode, msWFSDumpLayer11(map, lp, psNsOws, OWS_2_0_0, validated_language)); + xmlAddChild(psFtNode, msWFSDumpLayer11(map, lp, psNsOws, OWS_2_0_0, validated_language, script_url)); } /* As soon as at least one layer supports sorting, advertize sorting */ diff --git a/mapwms.c b/mapwms.c index 5d92b5608f..163e67a231 100644 --- a/mapwms.c +++ b/mapwms.c @@ -2260,6 +2260,9 @@ int msDumpLayer(mapObj *map, layerObj *lp, int nVersion, const char *script_url_ } if(nVersion >= OWS_1_1_0) + if (! msOWSLookupMetadata(&(lp->metadata), "MO", "metadataurl_href")) + msMetadataSetGetMetadataURL(lp, script_url_encoded); + msOWSPrintURLType(stdout, &(lp->metadata), "MO", "metadataurl", OWS_NOERR, NULL, "MetadataURL", " type=\"%s\"", NULL, NULL, ">\n %s layer1 + + text/xml + + layer2 layer2 + + text/xml + + layer3 layer3 + + text/xml + + layer5 @@ -76,6 +88,10 @@ Content-Type: text/xml; charset=UTF-8 + + text/xml + + layer6 @@ -89,6 +105,10 @@ Content-Type: text/xml; charset=UTF-8 + + text/xml + + layer7 @@ -102,6 +122,10 @@ Content-Type: text/xml; charset=UTF-8 + + text/xml + + layer8 @@ -115,6 +139,10 @@ Content-Type: text/xml; charset=UTF-8 + + text/xml + + diff --git a/msautotest/wxs/expected/ows_all_wms_capabilities.xml b/msautotest/wxs/expected/ows_all_wms_capabilities.xml index 72b75a61a4..860307e145 100644 --- a/msautotest/wxs/expected/ows_all_wms_capabilities.xml +++ b/msautotest/wxs/expected/ows_all_wms_capabilities.xml @@ -148,6 +148,10 @@ Content-Type: text/xml; charset=UTF-8 + + text/xml + + + + + province + province + province + + + text/xml + + + + + + toronto + Toronto + Toronto + EPSG:32611 + + + + text/xml + + + + + + diff --git a/msautotest/wxs/expected/ows_metadata_wms_capabilities130.xml b/msautotest/wxs/expected/ows_metadata_wms_capabilities130.xml new file mode 100644 index 0000000000..a63eb51e53 --- /dev/null +++ b/msautotest/wxs/expected/ows_metadata_wms_capabilities130.xml @@ -0,0 +1,208 @@ +Content-Type: text/xml; charset=UTF-8 + + + + + + WMS + Test simple OWS + Test OWS Abstract + + ogc + wms + wfs + wcs + metadata + + + + + Tom Kralidis + MapServer + + self + + postal +
123 SomeRoad Road
+ Toronto + Ontario + xxx-xxx + Canada +
+ +xx-xxx-xxx-xxxx + +xx-xxx-xxx-xxxx + tomkralidis@xxxxxxx.xxx +
+ none + none + 4096 + 4096 +
+ + + + + text/xml + + + + + + + + + image/tiff + image/png + image/jpeg + image/png; mode=8bit + image/vnd.jpeg-png + image/vnd.jpeg-png8 + application/x-pdf + image/svg+xml + application/vnd.google-earth.kml+xml + application/vnd.google-earth.kmz + + + + + + + + + text/plain + application/vnd.ogc.gml + + + + + + + + + text/xml + + + + + + + + + image/png + image/jpeg + image/png; mode=8bit + image/vnd.jpeg-png + image/vnd.jpeg-png8 + + + + + + + + + text/xml + + + + + + + + + + XML + INIMAGE + BLANK + + + + OWS_METADATA_TEST + Test simple OWS + Test OWS Abstract + + ogc + wms + wfs + wcs + metadata + + EPSG:4326 + + -180 + 180 + -90 + 90 + + + + road + road + Roads of I.P.E. + EPSG:43204 + + -66.6333 + -59.2921 + 42.3821 + 48.2955 + + + + text/xml + + + + + + province + province + province + + -66.7243 + -57.7217 + 41.7705 + 48.4773 + + + text/xml + + + + + + toronto + Toronto + Toronto + EPSG:32611 + + -117.546 + -117.387 + 44.6656 + 44.7671 + + + + text/xml + + + + + +
diff --git a/msautotest/wxs/expected/ows_wms_capabilities.xml b/msautotest/wxs/expected/ows_wms_capabilities.xml index a8eb08df8c..0ffa25b592 100644 --- a/msautotest/wxs/expected/ows_wms_capabilities.xml +++ b/msautotest/wxs/expected/ows_wms_capabilities.xml @@ -87,6 +87,10 @@ Content-Type: text/xml; charset=UTF-8 + + text/xml + +