741 строка
28 KiB
C++
741 строка
28 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: WCS Client Driver
|
|
* Purpose: Implementation of Dataset class for WCS 1.0.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2006, Frank Warmerdam
|
|
* Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
|
|
* Copyright (c) 2017, Ari Jolma
|
|
* Copyright (c) 2017, Finnish Environment Institute
|
|
*
|
|
* 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 or substantial portions of the 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.
|
|
****************************************************************************/
|
|
|
|
#include "cpl_string.h"
|
|
#include "cpl_minixml.h"
|
|
#include "cpl_http.h"
|
|
#include "gmlutils.h"
|
|
#include "gdal_frmts.h"
|
|
#include "gdal_pam.h"
|
|
#include "ogr_spatialref.h"
|
|
#include "gmlcoverage.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "wcsdataset.h"
|
|
#include "wcsutils.h"
|
|
|
|
using namespace WCSUtils;
|
|
|
|
/************************************************************************/
|
|
/* GetExtent() */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
std::vector<double> WCSDataset100::GetExtent(int nXOff, int nYOff, int nXSize,
|
|
int nYSize, CPL_UNUSED int,
|
|
CPL_UNUSED int)
|
|
{
|
|
std::vector<double> extent;
|
|
// WCS 1.0 extents are the outer edges of outer pixels.
|
|
extent.push_back(adfGeoTransform[0] + (nXOff)*adfGeoTransform[1]);
|
|
extent.push_back(adfGeoTransform[3] +
|
|
(nYOff + nYSize) * adfGeoTransform[5]);
|
|
extent.push_back(adfGeoTransform[0] +
|
|
(nXOff + nXSize) * adfGeoTransform[1]);
|
|
extent.push_back(adfGeoTransform[3] + (nYOff)*adfGeoTransform[5]);
|
|
return extent;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetCoverageRequest() */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
CPLString WCSDataset100::GetCoverageRequest(CPL_UNUSED bool scaled,
|
|
int nBufXSize, int nBufYSize,
|
|
const std::vector<double> &extent,
|
|
CPLString osBandList)
|
|
{
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* URL encode strings that could have questionable characters. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString osCoverage = CPLGetXMLValue(psService, "CoverageName", "");
|
|
|
|
char *pszEncoded = CPLEscapeString(osCoverage, -1, CPLES_URL);
|
|
osCoverage = pszEncoded;
|
|
CPLFree(pszEncoded);
|
|
|
|
CPLString osFormat = CPLGetXMLValue(psService, "PreferredFormat", "");
|
|
|
|
pszEncoded = CPLEscapeString(osFormat, -1, CPLES_URL);
|
|
osFormat = pszEncoded;
|
|
CPLFree(pszEncoded);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a time we want to use? */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString osTime;
|
|
|
|
osTime = CSLFetchNameValueDef(papszSDSModifiers, "time", osDefaultTime);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Construct a "simple" GetCoverage request (WCS 1.0). */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString request = CPLGetXMLValue(psService, "ServiceURL", "");
|
|
request = CPLURLAddKVP(request, "SERVICE", "WCS");
|
|
request = CPLURLAddKVP(request, "REQUEST", "GetCoverage");
|
|
request = CPLURLAddKVP(request, "VERSION",
|
|
CPLGetXMLValue(psService, "Version", "1.0.0"));
|
|
request = CPLURLAddKVP(request, "COVERAGE", osCoverage.c_str());
|
|
request = CPLURLAddKVP(request, "FORMAT", osFormat.c_str());
|
|
request += CPLString().Printf(
|
|
"&BBOX=%.15g,%.15g,%.15g,%.15g&WIDTH=%d&HEIGHT=%d&CRS=%s", extent[0],
|
|
extent[1], extent[2], extent[3], nBufXSize, nBufYSize, osCRS.c_str());
|
|
CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
|
|
if (extra != "")
|
|
{
|
|
std::vector<CPLString> pairs = Split(extra, "&");
|
|
for (unsigned int i = 0; i < pairs.size(); ++i)
|
|
{
|
|
std::vector<CPLString> pair = Split(pairs[i], "=");
|
|
request = CPLURLAddKVP(request, pair[0], pair[1]);
|
|
}
|
|
}
|
|
extra = CPLGetXMLValue(psService, "GetCoverageExtra", "");
|
|
if (extra != "")
|
|
{
|
|
std::vector<CPLString> pairs = Split(extra, "&");
|
|
for (unsigned int i = 0; i < pairs.size(); ++i)
|
|
{
|
|
std::vector<CPLString> pair = Split(pairs[i], "=");
|
|
request = CPLURLAddKVP(request, pair[0], pair[1]);
|
|
}
|
|
}
|
|
|
|
CPLString interpolation = CPLGetXMLValue(psService, "Interpolation", "");
|
|
if (interpolation == "")
|
|
{
|
|
// old undocumented key for interpolation in service
|
|
interpolation = CPLGetXMLValue(psService, "Resample", "");
|
|
}
|
|
if (interpolation != "")
|
|
{
|
|
request += "&INTERPOLATION=" + interpolation;
|
|
}
|
|
|
|
if (osTime != "")
|
|
{
|
|
request += "&time=";
|
|
request += osTime;
|
|
}
|
|
|
|
if (osBandList != "")
|
|
{
|
|
request += CPLString().Printf("&%s=%s", osBandIdentifier.c_str(),
|
|
osBandList.c_str());
|
|
}
|
|
return request;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* DescribeCoverageRequest() */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
CPLString WCSDataset100::DescribeCoverageRequest()
|
|
{
|
|
CPLString request = CPLGetXMLValue(psService, "ServiceURL", "");
|
|
request = CPLURLAddKVP(request, "SERVICE", "WCS");
|
|
request = CPLURLAddKVP(request, "REQUEST", "DescribeCoverage");
|
|
request = CPLURLAddKVP(request, "VERSION",
|
|
CPLGetXMLValue(psService, "Version", "1.0.0"));
|
|
request = CPLURLAddKVP(request, "COVERAGE",
|
|
CPLGetXMLValue(psService, "CoverageName", ""));
|
|
CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
|
|
if (extra != "")
|
|
{
|
|
std::vector<CPLString> pairs = Split(extra, "&");
|
|
for (unsigned int i = 0; i < pairs.size(); ++i)
|
|
{
|
|
std::vector<CPLString> pair = Split(pairs[i], "=");
|
|
request = CPLURLAddKVP(request, pair[0], pair[1]);
|
|
}
|
|
}
|
|
extra = CPLGetXMLValue(psService, "DescribeCoverageExtra", "");
|
|
if (extra != "")
|
|
{
|
|
std::vector<CPLString> pairs = Split(extra, "&");
|
|
for (unsigned int i = 0; i < pairs.size(); ++i)
|
|
{
|
|
std::vector<CPLString> pair = Split(pairs[i], "=");
|
|
request = CPLURLAddKVP(request, pair[0], pair[1]);
|
|
}
|
|
}
|
|
return request;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CoverageOffering() */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
CPLXMLNode *WCSDataset100::CoverageOffering(CPLXMLNode *psDC)
|
|
{
|
|
return CPLGetXMLNode(psDC, "=CoverageDescription.CoverageOffering");
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ExtractGridInfo() */
|
|
/* */
|
|
/* Collect info about grid from describe coverage for WCS 1.0.0 */
|
|
/* and above. */
|
|
/************************************************************************/
|
|
|
|
bool WCSDataset100::ExtractGridInfo()
|
|
|
|
{
|
|
CPLXMLNode *psCO = CPLGetXMLNode(psService, "CoverageOffering");
|
|
|
|
if (psCO == nullptr)
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We need to strip off name spaces so it is easier to */
|
|
/* searchfor plain gml names. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLStripXMLNamespace(psCO, nullptr, TRUE);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify we have a Rectified Grid. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psRG =
|
|
CPLGetXMLNode(psCO, "domainSet.spatialDomain.RectifiedGrid");
|
|
|
|
if (psRG == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Unable to find RectifiedGrid in CoverageOffering,\n"
|
|
"unable to process WCS Coverage.");
|
|
return FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Extract size, geotransform and coordinate system. */
|
|
/* Projection is, if it is, from Point.srsName */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszProjection = nullptr;
|
|
if (WCSParseGMLCoverage(psRG, &nRasterXSize, &nRasterYSize, adfGeoTransform,
|
|
&pszProjection) != CE_None)
|
|
{
|
|
CPLFree(pszProjection);
|
|
return FALSE;
|
|
}
|
|
if (pszProjection)
|
|
m_oSRS.SetFromUserInput(
|
|
pszProjection,
|
|
OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
|
|
CPLFree(pszProjection);
|
|
|
|
// MapServer have origin at pixel boundary
|
|
if (CPLGetXMLBoolean(psService, "OriginAtBoundary"))
|
|
{
|
|
adfGeoTransform[0] += adfGeoTransform[1] * 0.5;
|
|
adfGeoTransform[0] += adfGeoTransform[2] * 0.5;
|
|
adfGeoTransform[3] += adfGeoTransform[4] * 0.5;
|
|
adfGeoTransform[3] += adfGeoTransform[5] * 0.5;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fallback to nativeCRSs declaration. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszNativeCRSs =
|
|
CPLGetXMLValue(psCO, "supportedCRSs.nativeCRSs", nullptr);
|
|
|
|
if (pszNativeCRSs == nullptr)
|
|
pszNativeCRSs =
|
|
CPLGetXMLValue(psCO, "supportedCRSs.requestResponseCRSs", nullptr);
|
|
|
|
if (pszNativeCRSs == nullptr)
|
|
pszNativeCRSs =
|
|
CPLGetXMLValue(psCO, "supportedCRSs.requestCRSs", nullptr);
|
|
|
|
if (pszNativeCRSs == nullptr)
|
|
pszNativeCRSs =
|
|
CPLGetXMLValue(psCO, "supportedCRSs.responseCRSs", nullptr);
|
|
|
|
if (pszNativeCRSs != nullptr && m_oSRS.IsEmpty())
|
|
{
|
|
if (m_oSRS.SetFromUserInput(
|
|
pszNativeCRSs,
|
|
OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
|
|
OGRERR_NONE)
|
|
{
|
|
CPLDebug("WCS", "<nativeCRSs> element contents not parsable:\n%s",
|
|
pszNativeCRSs);
|
|
}
|
|
}
|
|
|
|
// We should try to use the services name for the CRS if possible.
|
|
if (pszNativeCRSs != nullptr &&
|
|
(STARTS_WITH_CI(pszNativeCRSs, "EPSG:") ||
|
|
STARTS_WITH_CI(pszNativeCRSs, "AUTO:") ||
|
|
STARTS_WITH_CI(pszNativeCRSs, "Image ") ||
|
|
STARTS_WITH_CI(pszNativeCRSs, "Engineering ") ||
|
|
STARTS_WITH_CI(pszNativeCRSs, "OGC:")))
|
|
{
|
|
osCRS = pszNativeCRSs;
|
|
|
|
size_t nDivider = osCRS.find(" ");
|
|
|
|
if (nDivider != std::string::npos)
|
|
osCRS.resize(nDivider - 1);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a coordinate system override? */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszProjOverride = CPLGetXMLValue(psService, "SRS", nullptr);
|
|
|
|
if (pszProjOverride)
|
|
{
|
|
if (m_oSRS.SetFromUserInput(
|
|
pszProjOverride,
|
|
OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
|
|
OGRERR_NONE)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"<SRS> element contents not parsable:\n%s",
|
|
pszProjOverride);
|
|
return FALSE;
|
|
}
|
|
|
|
if (STARTS_WITH_CI(pszProjOverride, "EPSG:") ||
|
|
STARTS_WITH_CI(pszProjOverride, "AUTO:") ||
|
|
STARTS_WITH_CI(pszProjOverride, "OGC:") ||
|
|
STARTS_WITH_CI(pszProjOverride, "Image ") ||
|
|
STARTS_WITH_CI(pszProjOverride, "Engineering "))
|
|
osCRS = pszProjOverride;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Build CRS name to use. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (!m_oSRS.IsEmpty() && osCRS == "")
|
|
{
|
|
const char *pszAuth = m_oSRS.GetAuthorityName(nullptr);
|
|
if (pszAuth != nullptr && EQUAL(pszAuth, "EPSG"))
|
|
{
|
|
pszAuth = m_oSRS.GetAuthorityCode(nullptr);
|
|
if (pszAuth)
|
|
{
|
|
osCRS = "EPSG:";
|
|
osCRS += pszAuth;
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Unable to define CRS to use.");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Pick a format type if we don't already have one selected. */
|
|
/* */
|
|
/* We will prefer anything that sounds like TIFF, otherwise */
|
|
/* falling back to the first supported format. Should we */
|
|
/* consider preferring the nativeFormat if available? */
|
|
/* -------------------------------------------------------------------- */
|
|
if (CPLGetXMLValue(psService, "PreferredFormat", nullptr) == nullptr)
|
|
{
|
|
CPLXMLNode *psSF = CPLGetXMLNode(psCO, "supportedFormats");
|
|
CPLXMLNode *psNode;
|
|
char **papszFormatList = nullptr;
|
|
CPLString osPreferredFormat;
|
|
int iFormat;
|
|
|
|
if (psSF == nullptr)
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_AppDefined,
|
|
"No <PreferredFormat> tag in service definition file, and no\n"
|
|
"<supportedFormats> in coverageOffering.");
|
|
return FALSE;
|
|
}
|
|
|
|
for (psNode = psSF->psChild; psNode != nullptr; psNode = psNode->psNext)
|
|
{
|
|
if (psNode->eType == CXT_Element &&
|
|
EQUAL(psNode->pszValue, "formats") &&
|
|
psNode->psChild != nullptr &&
|
|
psNode->psChild->eType == CXT_Text)
|
|
{
|
|
// This check is looking for deprecated WCS 1.0 capabilities
|
|
// with multiple formats space delimited in a single <formats>
|
|
// element per GDAL ticket 1748 (done by MapServer 4.10 and
|
|
// earlier for instance).
|
|
if (papszFormatList == nullptr && psNode->psNext == nullptr &&
|
|
strstr(psNode->psChild->pszValue, " ") != nullptr &&
|
|
strstr(psNode->psChild->pszValue, ";") == nullptr)
|
|
{
|
|
char **papszSubList =
|
|
CSLTokenizeString(psNode->psChild->pszValue);
|
|
papszFormatList =
|
|
CSLInsertStrings(papszFormatList, -1, papszSubList);
|
|
CSLDestroy(papszSubList);
|
|
}
|
|
else
|
|
{
|
|
papszFormatList = CSLAddString(papszFormatList,
|
|
psNode->psChild->pszValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (iFormat = 0;
|
|
papszFormatList != nullptr && papszFormatList[iFormat] != nullptr;
|
|
iFormat++)
|
|
{
|
|
if (osPreferredFormat.empty())
|
|
osPreferredFormat = papszFormatList[iFormat];
|
|
|
|
if (strstr(papszFormatList[iFormat], "tiff") != nullptr ||
|
|
strstr(papszFormatList[iFormat], "TIFF") != nullptr ||
|
|
strstr(papszFormatList[iFormat], "Tiff") != nullptr)
|
|
{
|
|
osPreferredFormat = papszFormatList[iFormat];
|
|
break;
|
|
}
|
|
}
|
|
|
|
CSLDestroy(papszFormatList);
|
|
|
|
if (!osPreferredFormat.empty())
|
|
{
|
|
bServiceDirty = true;
|
|
CPLCreateXMLElementAndValue(psService, "PreferredFormat",
|
|
osPreferredFormat);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to identify a nodata value. For now we only support the */
|
|
/* singleValue mechanism. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (CPLGetXMLValue(psService, "NoDataValue", nullptr) == nullptr)
|
|
{
|
|
const char *pszSV = CPLGetXMLValue(
|
|
psCO, "rangeSet.RangeSet.nullValues.singleValue", nullptr);
|
|
|
|
if (pszSV != nullptr && (CPLAtof(pszSV) != 0.0 || *pszSV == DIGIT_ZERO))
|
|
{
|
|
bServiceDirty = true;
|
|
CPLCreateXMLElementAndValue(psService, "NoDataValue", pszSV);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a Band range type. For now we look for a fairly */
|
|
/* specific configuration. The rangeset my have one axis named */
|
|
/* "Band", with a set of ascending numerical values. */
|
|
/* -------------------------------------------------------------------- */
|
|
osBandIdentifier = CPLGetXMLValue(psService, "BandIdentifier", "");
|
|
CPLXMLNode *psAD = CPLGetXMLNode(
|
|
psService,
|
|
"CoverageOffering.rangeSet.RangeSet.axisDescription.AxisDescription");
|
|
CPLXMLNode *psValues;
|
|
|
|
if (osBandIdentifier.empty() && psAD != nullptr &&
|
|
(EQUAL(CPLGetXMLValue(psAD, "name", ""), "Band") ||
|
|
EQUAL(CPLGetXMLValue(psAD, "name", ""), "Bands")) &&
|
|
((psValues = CPLGetXMLNode(psAD, "values")) != nullptr))
|
|
{
|
|
CPLXMLNode *psSV;
|
|
int iBand;
|
|
|
|
osBandIdentifier = CPLGetXMLValue(psAD, "name", "");
|
|
|
|
for (psSV = psValues->psChild, iBand = 1; psSV != nullptr;
|
|
psSV = psSV->psNext, iBand++)
|
|
{
|
|
if (psSV->eType != CXT_Element ||
|
|
!EQUAL(psSV->pszValue, "singleValue") ||
|
|
psSV->psChild == nullptr || psSV->psChild->eType != CXT_Text ||
|
|
atoi(psSV->psChild->pszValue) != iBand)
|
|
{
|
|
osBandIdentifier = "";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!osBandIdentifier.empty())
|
|
{
|
|
bServiceDirty = true;
|
|
CPLSetXMLValue(psService, "BandIdentifier", osBandIdentifier);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a temporal domain? If so, try to identify a */
|
|
/* default time value. */
|
|
/* -------------------------------------------------------------------- */
|
|
osDefaultTime = CPLGetXMLValue(psService, "DefaultTime", "");
|
|
CPLXMLNode *psTD =
|
|
CPLGetXMLNode(psService, "CoverageOffering.domainSet.temporalDomain");
|
|
CPLString osServiceURL = CPLGetXMLValue(psService, "ServiceURL", "");
|
|
CPLString osCoverageExtra =
|
|
CPLGetXMLValue(psService, "GetCoverageExtra", "");
|
|
|
|
if (psTD != nullptr)
|
|
{
|
|
CPLXMLNode *psTime;
|
|
|
|
// collect all the allowed time positions.
|
|
|
|
for (psTime = psTD->psChild; psTime != nullptr; psTime = psTime->psNext)
|
|
{
|
|
if (psTime->eType == CXT_Element &&
|
|
EQUAL(psTime->pszValue, "timePosition") &&
|
|
psTime->psChild != nullptr &&
|
|
psTime->psChild->eType == CXT_Text)
|
|
aosTimePositions.push_back(psTime->psChild->pszValue);
|
|
}
|
|
|
|
// we will default to the last - likely the most recent - entry.
|
|
|
|
if (!aosTimePositions.empty() && osDefaultTime.empty() &&
|
|
osServiceURL.ifind("time=") == std::string::npos &&
|
|
osCoverageExtra.ifind("time=") == std::string::npos)
|
|
{
|
|
osDefaultTime = aosTimePositions.back();
|
|
bServiceDirty = true;
|
|
CPLCreateXMLElementAndValue(psService, "DefaultTime",
|
|
osDefaultTime);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ParseCapabilities() */
|
|
/************************************************************************/
|
|
|
|
CPLErr WCSDataset100::ParseCapabilities(CPLXMLNode *Capabilities,
|
|
CPL_UNUSED CPLString url)
|
|
{
|
|
|
|
CPLStripXMLNamespace(Capabilities, nullptr, TRUE);
|
|
|
|
if (strcmp(Capabilities->pszValue, "WCS_Capabilities") != 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Error in capabilities document.\n");
|
|
return CE_Failure;
|
|
}
|
|
|
|
char **metadata = nullptr;
|
|
CPLString path = "WCS_GLOBAL#";
|
|
|
|
CPLString key = path + "version";
|
|
metadata = CSLSetNameValue(metadata, key, Version());
|
|
|
|
for (CPLXMLNode *node = Capabilities->psChild; node != nullptr;
|
|
node = node->psNext)
|
|
{
|
|
const char *attr = node->pszValue;
|
|
if (node->eType == CXT_Attribute && EQUAL(attr, "updateSequence"))
|
|
{
|
|
key = path + "updateSequence";
|
|
CPLString value = CPLGetXMLValue(node, nullptr, "");
|
|
metadata = CSLSetNameValue(metadata, key, value);
|
|
}
|
|
}
|
|
|
|
// identification metadata
|
|
CPLString path2 = path;
|
|
std::vector<CPLString> keys2;
|
|
keys2.push_back("description");
|
|
keys2.push_back("name");
|
|
keys2.push_back("label");
|
|
keys2.push_back("fees");
|
|
keys2.push_back("accessConstraints");
|
|
CPLXMLNode *service =
|
|
AddSimpleMetaData(&metadata, Capabilities, path2, "Service", keys2);
|
|
if (service)
|
|
{
|
|
CPLString path3 = path2;
|
|
std::vector<CPLString> keys3;
|
|
keys3.push_back("individualName");
|
|
keys3.push_back("organisationName");
|
|
keys3.push_back("positionName");
|
|
CPLString kw = GetKeywords(service, "keywords", "keyword");
|
|
if (kw != "")
|
|
{
|
|
CPLString name = path + "keywords";
|
|
metadata = CSLSetNameValue(metadata, name, kw);
|
|
}
|
|
CPLXMLNode *party = AddSimpleMetaData(&metadata, service, path3,
|
|
"responsibleParty", keys3);
|
|
CPLXMLNode *info = CPLGetXMLNode(party, "contactInfo");
|
|
if (party && info)
|
|
{
|
|
CPLString path4 = path3 + "contactInfo.";
|
|
std::vector<CPLString> keys4;
|
|
keys4.push_back("deliveryPoint");
|
|
keys4.push_back("city");
|
|
keys4.push_back("administrativeArea");
|
|
keys4.push_back("postalCode");
|
|
keys4.push_back("country");
|
|
keys4.push_back("electronicMailAddress");
|
|
CPLString path5 = path4;
|
|
std::vector<CPLString> keys5;
|
|
keys5.push_back("voice");
|
|
keys5.push_back("facsimile");
|
|
AddSimpleMetaData(&metadata, info, path4, "address", keys4);
|
|
AddSimpleMetaData(&metadata, info, path5, "phone", keys5);
|
|
}
|
|
}
|
|
|
|
// provider metadata
|
|
// operations metadata
|
|
CPLString DescribeCoverageURL;
|
|
DescribeCoverageURL = CPLGetXMLValue(
|
|
CPLGetXMLNode(
|
|
CPLGetXMLNode(
|
|
CPLSearchXMLNode(
|
|
CPLSearchXMLNode(Capabilities, "DescribeCoverage"), "Get"),
|
|
"OnlineResource"),
|
|
"href"),
|
|
nullptr, "");
|
|
// if DescribeCoverageURL looks wrong (i.e. has localhost) should we change
|
|
// it?
|
|
|
|
this->SetMetadata(metadata, "");
|
|
CSLDestroy(metadata);
|
|
metadata = nullptr;
|
|
|
|
if (CPLXMLNode *contents = CPLGetXMLNode(Capabilities, "ContentMetadata"))
|
|
{
|
|
int index = 1;
|
|
for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
|
|
summary = summary->psNext)
|
|
{
|
|
if (summary->eType != CXT_Element ||
|
|
!EQUAL(summary->pszValue, "CoverageOfferingBrief"))
|
|
{
|
|
continue;
|
|
}
|
|
CPLString path3;
|
|
path3.Printf("SUBDATASET_%d_", index);
|
|
index += 1;
|
|
|
|
// the name and description of the subdataset:
|
|
// GDAL Data Model:
|
|
// The value of the _NAME is a string that can be passed to
|
|
// GDALOpen() to access the file.
|
|
|
|
CPLXMLNode *node = CPLGetXMLNode(summary, "name");
|
|
if (node)
|
|
{
|
|
CPLString key2 = path3 + "NAME";
|
|
CPLString name = CPLGetXMLValue(node, nullptr, "");
|
|
CPLString value = DescribeCoverageURL;
|
|
value = CPLURLAddKVP(value, "VERSION", this->Version());
|
|
value = CPLURLAddKVP(value, "COVERAGE", name);
|
|
metadata = CSLSetNameValue(metadata, key2, value);
|
|
}
|
|
else
|
|
{
|
|
CSLDestroy(metadata);
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Error in capabilities document.\n");
|
|
return CE_Failure;
|
|
}
|
|
|
|
node = CPLGetXMLNode(summary, "label");
|
|
if (node)
|
|
{
|
|
CPLString key2 = path3 + "DESC";
|
|
metadata = CSLSetNameValue(metadata, key2,
|
|
CPLGetXMLValue(node, nullptr, ""));
|
|
}
|
|
else
|
|
{
|
|
CSLDestroy(metadata);
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Error in capabilities document.\n");
|
|
return CE_Failure;
|
|
}
|
|
|
|
// todo: compose global bounding box from lonLatEnvelope
|
|
|
|
// further subdataset (coverage) parameters are parsed in
|
|
// ParseCoverageCapabilities
|
|
}
|
|
}
|
|
this->SetMetadata(metadata, "SUBDATASETS");
|
|
CSLDestroy(metadata);
|
|
return CE_None;
|
|
}
|
|
|
|
void WCSDataset100::ParseCoverageCapabilities(CPLXMLNode *capabilities,
|
|
const CPLString &coverage,
|
|
CPLXMLNode *metadata)
|
|
{
|
|
CPLStripXMLNamespace(capabilities, nullptr, TRUE);
|
|
if (CPLXMLNode *contents = CPLGetXMLNode(capabilities, "ContentMetadata"))
|
|
{
|
|
for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
|
|
summary = summary->psNext)
|
|
{
|
|
if (summary->eType != CXT_Element ||
|
|
!EQUAL(summary->pszValue, "CoverageOfferingBrief"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CPLXMLNode *node = CPLGetXMLNode(summary, "name");
|
|
if (node)
|
|
{
|
|
CPLString name = CPLGetXMLValue(node, nullptr, "");
|
|
if (name != coverage)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
XMLCopyMetadata(summary, metadata, "label");
|
|
XMLCopyMetadata(summary, metadata, "description");
|
|
|
|
CPLString kw = GetKeywords(summary, "keywords", "keyword");
|
|
CPLAddXMLAttributeAndValue(
|
|
CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
|
|
"keywords");
|
|
|
|
// skip metadataLink
|
|
}
|
|
}
|
|
}
|