LCOV - code coverage report
Current view: top level - frmts/wcs - wcsdataset100.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 234 305 76.7 %
Date: 2024-05-04 12:52:34 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WCS Client Driver
       4             :  * Purpose:  Implementation of Dataset class for WCS 1.0.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2006, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  * Copyright (c) 2017, Ari Jolma
      11             :  * Copyright (c) 2017, Finnish Environment Institute
      12             :  *
      13             :  * Permission is hereby granted, free of charge, to any person obtaining a
      14             :  * copy of this software and associated documentation files (the "Software"),
      15             :  * to deal in the Software without restriction, including without limitation
      16             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      17             :  * and/or sell copies of the Software, and to permit persons to whom the
      18             :  * Software is furnished to do so, subject to the following conditions:
      19             :  *
      20             :  * The above copyright notice and this permission notice shall be included
      21             :  * in all copies or substantial portions of the Software.
      22             :  *
      23             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      24             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      25             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      26             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      27             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      28             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      29             :  * DEALINGS IN THE SOFTWARE.
      30             :  ****************************************************************************/
      31             : 
      32             : #include "cpl_string.h"
      33             : #include "cpl_minixml.h"
      34             : #include "cpl_http.h"
      35             : #include "gmlutils.h"
      36             : #include "gdal_frmts.h"
      37             : #include "gdal_pam.h"
      38             : #include "ogr_spatialref.h"
      39             : #include "gmlcoverage.h"
      40             : 
      41             : #include <algorithm>
      42             : 
      43             : #include "wcsdataset.h"
      44             : #include "wcsutils.h"
      45             : 
      46             : using namespace WCSUtils;
      47             : 
      48             : /************************************************************************/
      49             : /*                         GetExtent()                                  */
      50             : /*                                                                      */
      51             : /************************************************************************/
      52             : 
      53          15 : std::vector<double> WCSDataset100::GetExtent(int nXOff, int nYOff, int nXSize,
      54             :                                              int nYSize, CPL_UNUSED int,
      55             :                                              CPL_UNUSED int)
      56             : {
      57          15 :     std::vector<double> extent;
      58             :     // WCS 1.0 extents are the outer edges of outer pixels.
      59          15 :     extent.push_back(adfGeoTransform[0] + (nXOff)*adfGeoTransform[1]);
      60           0 :     extent.push_back(adfGeoTransform[3] +
      61          15 :                      (nYOff + nYSize) * adfGeoTransform[5]);
      62           0 :     extent.push_back(adfGeoTransform[0] +
      63          15 :                      (nXOff + nXSize) * adfGeoTransform[1]);
      64          15 :     extent.push_back(adfGeoTransform[3] + (nYOff)*adfGeoTransform[5]);
      65          15 :     return extent;
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /*                        GetCoverageRequest()                          */
      70             : /*                                                                      */
      71             : /************************************************************************/
      72             : 
      73          15 : std::string WCSDataset100::GetCoverageRequest(bool /* scaled */, int nBufXSize,
      74             :                                               int nBufYSize,
      75             :                                               const std::vector<double> &extent,
      76             :                                               const std::string &osBandList)
      77             : {
      78             : 
      79             :     /* -------------------------------------------------------------------- */
      80             :     /*      URL encode strings that could have questionable characters.     */
      81             :     /* -------------------------------------------------------------------- */
      82          30 :     CPLString osCoverage = CPLGetXMLValue(psService, "CoverageName", "");
      83             : 
      84          15 :     char *pszEncoded = CPLEscapeString(osCoverage, -1, CPLES_URL);
      85          15 :     osCoverage = pszEncoded;
      86          15 :     CPLFree(pszEncoded);
      87             : 
      88          30 :     CPLString osFormat = CPLGetXMLValue(psService, "PreferredFormat", "");
      89             : 
      90          15 :     pszEncoded = CPLEscapeString(osFormat, -1, CPLES_URL);
      91          15 :     osFormat = pszEncoded;
      92          15 :     CPLFree(pszEncoded);
      93             : 
      94             :     /* -------------------------------------------------------------------- */
      95             :     /*      Do we have a time we want to use?                               */
      96             :     /* -------------------------------------------------------------------- */
      97          30 :     CPLString osTime;
      98             : 
      99             :     osTime =
     100          15 :         CSLFetchNameValueDef(papszSDSModifiers, "time", osDefaultTime.c_str());
     101             : 
     102             :     /* -------------------------------------------------------------------- */
     103             :     /*      Construct a "simple" GetCoverage request (WCS 1.0).             */
     104             :     /* -------------------------------------------------------------------- */
     105          15 :     std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
     106          15 :     request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
     107          15 :     request = CPLURLAddKVP(request.c_str(), "REQUEST", "GetCoverage");
     108          30 :     request = CPLURLAddKVP(request.c_str(), "VERSION",
     109          30 :                            CPLGetXMLValue(psService, "Version", "1.0.0"));
     110          15 :     request = CPLURLAddKVP(request.c_str(), "COVERAGE", osCoverage.c_str());
     111          15 :     request = CPLURLAddKVP(request.c_str(), "FORMAT", osFormat.c_str());
     112          30 :     request += CPLString().Printf(
     113          15 :         "&BBOX=%.15g,%.15g,%.15g,%.15g&WIDTH=%d&HEIGHT=%d&CRS=%s", extent[0],
     114          15 :         extent[1], extent[2], extent[3], nBufXSize, nBufYSize, osCRS.c_str());
     115          30 :     CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
     116          15 :     if (extra != "")
     117             :     {
     118          30 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     119          30 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     120             :         {
     121          15 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     122             :             request =
     123          15 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     124             :         }
     125             :     }
     126          15 :     extra = CPLGetXMLValue(psService, "GetCoverageExtra", "");
     127          15 :     if (extra != "")
     128             :     {
     129          30 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     130          30 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     131             :         {
     132          15 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     133             :             request =
     134          15 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     135             :         }
     136             :     }
     137             : 
     138          30 :     CPLString interpolation = CPLGetXMLValue(psService, "Interpolation", "");
     139          15 :     if (interpolation == "")
     140             :     {
     141             :         // old undocumented key for interpolation in service
     142          15 :         interpolation = CPLGetXMLValue(psService, "Resample", "");
     143             :     }
     144          15 :     if (interpolation != "")
     145             :     {
     146           0 :         request += "&INTERPOLATION=" + interpolation;
     147             :     }
     148             : 
     149          15 :     if (osTime != "")
     150             :     {
     151           0 :         request += "&time=";
     152           0 :         request += osTime;
     153             :     }
     154             : 
     155          15 :     if (osBandList != "")
     156             :     {
     157           0 :         request += CPLString().Printf("&%s=%s", osBandIdentifier.c_str(),
     158           0 :                                       osBandList.c_str());
     159             :     }
     160          30 :     return request;
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*                      DescribeCoverageRequest()                       */
     165             : /*                                                                      */
     166             : /************************************************************************/
     167             : 
     168           6 : std::string WCSDataset100::DescribeCoverageRequest()
     169             : {
     170           6 :     std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
     171           6 :     request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
     172           6 :     request = CPLURLAddKVP(request.c_str(), "REQUEST", "DescribeCoverage");
     173          12 :     request = CPLURLAddKVP(request.c_str(), "VERSION",
     174          12 :                            CPLGetXMLValue(psService, "Version", "1.0.0"));
     175          12 :     request = CPLURLAddKVP(request.c_str(), "COVERAGE",
     176          12 :                            CPLGetXMLValue(psService, "CoverageName", ""));
     177          12 :     CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
     178           6 :     if (extra != "")
     179             :     {
     180          10 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     181          10 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     182             :         {
     183           5 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     184             :             request =
     185           5 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     186             :         }
     187             :     }
     188           6 :     extra = CPLGetXMLValue(psService, "DescribeCoverageExtra", "");
     189           6 :     if (extra != "")
     190             :     {
     191           0 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     192           0 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     193             :         {
     194           0 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     195             :             request =
     196           0 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     197             :         }
     198             :     }
     199          12 :     return request;
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*                         CoverageOffering()                           */
     204             : /*                                                                      */
     205             : /************************************************************************/
     206             : 
     207           5 : CPLXMLNode *WCSDataset100::CoverageOffering(CPLXMLNode *psDC)
     208             : {
     209           5 :     return CPLGetXMLNode(psDC, "=CoverageDescription.CoverageOffering");
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /*                         ExtractGridInfo()                            */
     214             : /*                                                                      */
     215             : /*      Collect info about grid from describe coverage for WCS 1.0.0    */
     216             : /*      and above.                                                      */
     217             : /************************************************************************/
     218             : 
     219          15 : bool WCSDataset100::ExtractGridInfo()
     220             : 
     221             : {
     222          15 :     CPLXMLNode *psCO = CPLGetXMLNode(psService, "CoverageOffering");
     223             : 
     224          15 :     if (psCO == nullptr)
     225           0 :         return FALSE;
     226             : 
     227             :     /* -------------------------------------------------------------------- */
     228             :     /*      We need to strip off name spaces so it is easier to             */
     229             :     /*      searchfor plain gml names.                                      */
     230             :     /* -------------------------------------------------------------------- */
     231          15 :     CPLStripXMLNamespace(psCO, nullptr, TRUE);
     232             : 
     233             :     /* -------------------------------------------------------------------- */
     234             :     /*      Verify we have a Rectified Grid.                                */
     235             :     /* -------------------------------------------------------------------- */
     236             :     CPLXMLNode *psRG =
     237          15 :         CPLGetXMLNode(psCO, "domainSet.spatialDomain.RectifiedGrid");
     238             : 
     239          15 :     if (psRG == nullptr)
     240             :     {
     241           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     242             :                  "Unable to find RectifiedGrid in CoverageOffering,\n"
     243             :                  "unable to process WCS Coverage.");
     244           0 :         return FALSE;
     245             :     }
     246             : 
     247             :     /* -------------------------------------------------------------------- */
     248             :     /*      Extract size, geotransform and coordinate system.               */
     249             :     /*      Projection is, if it is, from Point.srsName                     */
     250             :     /* -------------------------------------------------------------------- */
     251          15 :     char *pszProjection = nullptr;
     252          15 :     if (WCSParseGMLCoverage(psRG, &nRasterXSize, &nRasterYSize, adfGeoTransform,
     253          15 :                             &pszProjection) != CE_None)
     254             :     {
     255           0 :         CPLFree(pszProjection);
     256           0 :         return FALSE;
     257             :     }
     258          15 :     if (pszProjection)
     259           0 :         m_oSRS.SetFromUserInput(
     260             :             pszProjection,
     261             :             OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
     262          15 :     CPLFree(pszProjection);
     263             : 
     264             :     // MapServer have origin at pixel boundary
     265          15 :     if (CPLGetXMLBoolean(psService, "OriginAtBoundary"))
     266             :     {
     267           3 :         adfGeoTransform[0] += adfGeoTransform[1] * 0.5;
     268           3 :         adfGeoTransform[0] += adfGeoTransform[2] * 0.5;
     269           3 :         adfGeoTransform[3] += adfGeoTransform[4] * 0.5;
     270           3 :         adfGeoTransform[3] += adfGeoTransform[5] * 0.5;
     271             :     }
     272             : 
     273             :     /* -------------------------------------------------------------------- */
     274             :     /*      Fallback to nativeCRSs declaration.                             */
     275             :     /* -------------------------------------------------------------------- */
     276             :     const char *pszNativeCRSs =
     277          15 :         CPLGetXMLValue(psCO, "supportedCRSs.nativeCRSs", nullptr);
     278             : 
     279          15 :     if (pszNativeCRSs == nullptr)
     280             :         pszNativeCRSs =
     281           9 :             CPLGetXMLValue(psCO, "supportedCRSs.requestResponseCRSs", nullptr);
     282             : 
     283          15 :     if (pszNativeCRSs == nullptr)
     284             :         pszNativeCRSs =
     285           0 :             CPLGetXMLValue(psCO, "supportedCRSs.requestCRSs", nullptr);
     286             : 
     287          15 :     if (pszNativeCRSs == nullptr)
     288             :         pszNativeCRSs =
     289           0 :             CPLGetXMLValue(psCO, "supportedCRSs.responseCRSs", nullptr);
     290             : 
     291          15 :     if (pszNativeCRSs != nullptr && m_oSRS.IsEmpty())
     292             :     {
     293          15 :         if (m_oSRS.SetFromUserInput(
     294             :                 pszNativeCRSs,
     295          15 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
     296             :             OGRERR_NONE)
     297             :         {
     298          15 :             CPLDebug("WCS", "<nativeCRSs> element contents not parsable:\n%s",
     299             :                      pszNativeCRSs);
     300             :         }
     301             :     }
     302             : 
     303             :     // We should try to use the services name for the CRS if possible.
     304          15 :     if (pszNativeCRSs != nullptr &&
     305          15 :         (STARTS_WITH_CI(pszNativeCRSs, "EPSG:") ||
     306           0 :          STARTS_WITH_CI(pszNativeCRSs, "AUTO:") ||
     307           0 :          STARTS_WITH_CI(pszNativeCRSs, "Image ") ||
     308           0 :          STARTS_WITH_CI(pszNativeCRSs, "Engineering ") ||
     309           0 :          STARTS_WITH_CI(pszNativeCRSs, "OGC:")))
     310             :     {
     311          15 :         osCRS = pszNativeCRSs;
     312             : 
     313          15 :         size_t nDivider = osCRS.find(" ");
     314             : 
     315          15 :         if (nDivider != std::string::npos)
     316           0 :             osCRS.resize(nDivider - 1);
     317             :     }
     318             : 
     319             :     /* -------------------------------------------------------------------- */
     320             :     /*      Do we have a coordinate system override?                        */
     321             :     /* -------------------------------------------------------------------- */
     322          15 :     const char *pszProjOverride = CPLGetXMLValue(psService, "SRS", nullptr);
     323             : 
     324          15 :     if (pszProjOverride)
     325             :     {
     326           0 :         if (m_oSRS.SetFromUserInput(
     327             :                 pszProjOverride,
     328           0 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
     329             :             OGRERR_NONE)
     330             :         {
     331           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     332             :                      "<SRS> element contents not parsable:\n%s",
     333             :                      pszProjOverride);
     334           0 :             return FALSE;
     335             :         }
     336             : 
     337           0 :         if (STARTS_WITH_CI(pszProjOverride, "EPSG:") ||
     338           0 :             STARTS_WITH_CI(pszProjOverride, "AUTO:") ||
     339           0 :             STARTS_WITH_CI(pszProjOverride, "OGC:") ||
     340           0 :             STARTS_WITH_CI(pszProjOverride, "Image ") ||
     341           0 :             STARTS_WITH_CI(pszProjOverride, "Engineering "))
     342           0 :             osCRS = pszProjOverride;
     343             :     }
     344             : 
     345             :     /* -------------------------------------------------------------------- */
     346             :     /*      Build CRS name to use.                                          */
     347             :     /* -------------------------------------------------------------------- */
     348          15 :     if (!m_oSRS.IsEmpty() && osCRS == "")
     349             :     {
     350           0 :         const char *pszAuth = m_oSRS.GetAuthorityName(nullptr);
     351           0 :         if (pszAuth != nullptr && EQUAL(pszAuth, "EPSG"))
     352             :         {
     353           0 :             pszAuth = m_oSRS.GetAuthorityCode(nullptr);
     354           0 :             if (pszAuth)
     355             :             {
     356           0 :                 osCRS = "EPSG:";
     357           0 :                 osCRS += pszAuth;
     358             :             }
     359             :             else
     360             :             {
     361           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     362             :                          "Unable to define CRS to use.");
     363           0 :                 return FALSE;
     364             :             }
     365             :         }
     366             :     }
     367             : 
     368             :     /* -------------------------------------------------------------------- */
     369             :     /*      Pick a format type if we don't already have one selected.       */
     370             :     /*                                                                      */
     371             :     /*      We will prefer anything that sounds like TIFF, otherwise        */
     372             :     /*      falling back to the first supported format.  Should we          */
     373             :     /*      consider preferring the nativeFormat if available?              */
     374             :     /* -------------------------------------------------------------------- */
     375          15 :     if (CPLGetXMLValue(psService, "PreferredFormat", nullptr) == nullptr)
     376             :     {
     377           5 :         CPLXMLNode *psSF = CPLGetXMLNode(psCO, "supportedFormats");
     378             :         CPLXMLNode *psNode;
     379           5 :         char **papszFormatList = nullptr;
     380           5 :         CPLString osPreferredFormat;
     381             :         int iFormat;
     382             : 
     383           5 :         if (psSF == nullptr)
     384             :         {
     385           0 :             CPLError(
     386             :                 CE_Failure, CPLE_AppDefined,
     387             :                 "No <PreferredFormat> tag in service definition file, and no\n"
     388             :                 "<supportedFormats> in coverageOffering.");
     389           0 :             return FALSE;
     390             :         }
     391             : 
     392          36 :         for (psNode = psSF->psChild; psNode != nullptr; psNode = psNode->psNext)
     393             :         {
     394          31 :             if (psNode->eType == CXT_Element &&
     395          27 :                 EQUAL(psNode->pszValue, "formats") &&
     396          27 :                 psNode->psChild != nullptr &&
     397          27 :                 psNode->psChild->eType == CXT_Text)
     398             :             {
     399             :                 // This check is looking for deprecated WCS 1.0 capabilities
     400             :                 // with multiple formats space delimited in a single <formats>
     401             :                 // element per GDAL ticket 1748 (done by MapServer 4.10 and
     402             :                 // earlier for instance).
     403          27 :                 if (papszFormatList == nullptr && psNode->psNext == nullptr &&
     404           1 :                     strstr(psNode->psChild->pszValue, " ") != nullptr &&
     405           0 :                     strstr(psNode->psChild->pszValue, ";") == nullptr)
     406             :                 {
     407             :                     char **papszSubList =
     408           0 :                         CSLTokenizeString(psNode->psChild->pszValue);
     409             :                     papszFormatList =
     410           0 :                         CSLInsertStrings(papszFormatList, -1, papszSubList);
     411           0 :                     CSLDestroy(papszSubList);
     412             :                 }
     413             :                 else
     414             :                 {
     415          27 :                     papszFormatList = CSLAddString(papszFormatList,
     416          27 :                                                    psNode->psChild->pszValue);
     417             :                 }
     418             :             }
     419             :         }
     420             : 
     421           7 :         for (iFormat = 0;
     422           7 :              papszFormatList != nullptr && papszFormatList[iFormat] != nullptr;
     423             :              iFormat++)
     424             :         {
     425           7 :             if (osPreferredFormat.empty())
     426           5 :                 osPreferredFormat = papszFormatList[iFormat];
     427             : 
     428           7 :             if (strstr(papszFormatList[iFormat], "tiff") != nullptr ||
     429           7 :                 strstr(papszFormatList[iFormat], "TIFF") != nullptr ||
     430           3 :                 strstr(papszFormatList[iFormat], "Tiff") != nullptr)
     431             :             {
     432           5 :                 osPreferredFormat = papszFormatList[iFormat];
     433           5 :                 break;
     434             :             }
     435             :         }
     436             : 
     437           5 :         CSLDestroy(papszFormatList);
     438             : 
     439           5 :         if (!osPreferredFormat.empty())
     440             :         {
     441           5 :             bServiceDirty = true;
     442           5 :             CPLCreateXMLElementAndValue(psService, "PreferredFormat",
     443             :                                         osPreferredFormat);
     444             :         }
     445             :     }
     446             : 
     447             :     /* -------------------------------------------------------------------- */
     448             :     /*      Try to identify a nodata value.  For now we only support the    */
     449             :     /*      singleValue mechanism.                                          */
     450             :     /* -------------------------------------------------------------------- */
     451          15 :     if (CPLGetXMLValue(psService, "NoDataValue", nullptr) == nullptr)
     452             :     {
     453          15 :         const char *pszSV = CPLGetXMLValue(
     454             :             psCO, "rangeSet.RangeSet.nullValues.singleValue", nullptr);
     455             : 
     456          15 :         if (pszSV != nullptr && (CPLAtof(pszSV) != 0.0 || *pszSV == DIGIT_ZERO))
     457             :         {
     458           0 :             bServiceDirty = true;
     459           0 :             CPLCreateXMLElementAndValue(psService, "NoDataValue", pszSV);
     460             :         }
     461             :     }
     462             : 
     463             :     /* -------------------------------------------------------------------- */
     464             :     /*      Do we have a Band range type.  For now we look for a fairly     */
     465             :     /*      specific configuration.  The rangeset my have one axis named    */
     466             :     /*      "Band", with a set of ascending numerical values.               */
     467             :     /* -------------------------------------------------------------------- */
     468          15 :     osBandIdentifier = CPLGetXMLValue(psService, "BandIdentifier", "");
     469          15 :     CPLXMLNode *psAD = CPLGetXMLNode(
     470             :         psService,
     471             :         "CoverageOffering.rangeSet.RangeSet.axisDescription.AxisDescription");
     472             :     CPLXMLNode *psValues;
     473             : 
     474          22 :     if (osBandIdentifier.empty() && psAD != nullptr &&
     475           7 :         (EQUAL(CPLGetXMLValue(psAD, "name", ""), "Band") ||
     476          23 :          EQUAL(CPLGetXMLValue(psAD, "name", ""), "Bands")) &&
     477           7 :         ((psValues = CPLGetXMLNode(psAD, "values")) != nullptr))
     478             :     {
     479             :         CPLXMLNode *psSV;
     480             :         int iBand;
     481             : 
     482           7 :         osBandIdentifier = CPLGetXMLValue(psAD, "name", "");
     483             : 
     484          13 :         for (psSV = psValues->psChild, iBand = 1; psSV != nullptr;
     485           6 :              psSV = psSV->psNext, iBand++)
     486             :         {
     487           9 :             if (psSV->eType != CXT_Element ||
     488           9 :                 !EQUAL(psSV->pszValue, "singleValue") ||
     489           6 :                 psSV->psChild == nullptr || psSV->psChild->eType != CXT_Text ||
     490           6 :                 atoi(psSV->psChild->pszValue) != iBand)
     491             :             {
     492           3 :                 osBandIdentifier = "";
     493           3 :                 break;
     494             :             }
     495             :         }
     496             : 
     497           7 :         if (!osBandIdentifier.empty())
     498             :         {
     499           4 :             bServiceDirty = true;
     500           4 :             CPLSetXMLValue(psService, "BandIdentifier",
     501             :                            osBandIdentifier.c_str());
     502             :         }
     503             :     }
     504             : 
     505             :     /* -------------------------------------------------------------------- */
     506             :     /*      Do we have a temporal domain?  If so, try to identify a         */
     507             :     /*      default time value.                                             */
     508             :     /* -------------------------------------------------------------------- */
     509          15 :     osDefaultTime = CPLGetXMLValue(psService, "DefaultTime", "");
     510             :     CPLXMLNode *psTD =
     511          15 :         CPLGetXMLNode(psService, "CoverageOffering.domainSet.temporalDomain");
     512          30 :     CPLString osServiceURL = CPLGetXMLValue(psService, "ServiceURL", "");
     513             :     CPLString osCoverageExtra =
     514          15 :         CPLGetXMLValue(psService, "GetCoverageExtra", "");
     515             : 
     516          15 :     if (psTD != nullptr)
     517             :     {
     518             :         CPLXMLNode *psTime;
     519             : 
     520             :         // collect all the allowed time positions.
     521             : 
     522           0 :         for (psTime = psTD->psChild; psTime != nullptr; psTime = psTime->psNext)
     523             :         {
     524           0 :             if (psTime->eType == CXT_Element &&
     525           0 :                 EQUAL(psTime->pszValue, "timePosition") &&
     526           0 :                 psTime->psChild != nullptr &&
     527           0 :                 psTime->psChild->eType == CXT_Text)
     528           0 :                 aosTimePositions.push_back(psTime->psChild->pszValue);
     529             :         }
     530             : 
     531             :         // we will default to the last - likely the most recent - entry.
     532             : 
     533           0 :         if (!aosTimePositions.empty() && osDefaultTime.empty() &&
     534           0 :             osServiceURL.ifind("time=") == std::string::npos &&
     535           0 :             osCoverageExtra.ifind("time=") == std::string::npos)
     536             :         {
     537           0 :             osDefaultTime = aosTimePositions.back();
     538           0 :             bServiceDirty = true;
     539           0 :             CPLCreateXMLElementAndValue(psService, "DefaultTime",
     540             :                                         osDefaultTime.c_str());
     541             :         }
     542             :     }
     543             : 
     544          15 :     return true;
     545             : }
     546             : 
     547             : /************************************************************************/
     548             : /*                      ParseCapabilities()                             */
     549             : /************************************************************************/
     550             : 
     551           5 : CPLErr WCSDataset100::ParseCapabilities(CPLXMLNode *Capabilities,
     552             :                                         const std::string & /* url */)
     553             : {
     554             : 
     555           5 :     CPLStripXMLNamespace(Capabilities, nullptr, TRUE);
     556             : 
     557           5 :     if (strcmp(Capabilities->pszValue, "WCS_Capabilities") != 0)
     558             :     {
     559           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     560             :                  "Error in capabilities document.\n");
     561           0 :         return CE_Failure;
     562             :     }
     563             : 
     564           5 :     char **metadata = nullptr;
     565          10 :     CPLString path = "WCS_GLOBAL#";
     566             : 
     567          10 :     CPLString key = path + "version";
     568           5 :     metadata = CSLSetNameValue(metadata, key, Version());
     569             : 
     570          60 :     for (CPLXMLNode *node = Capabilities->psChild; node != nullptr;
     571          55 :          node = node->psNext)
     572             :     {
     573          55 :         const char *attr = node->pszValue;
     574          55 :         if (node->eType == CXT_Attribute && EQUAL(attr, "updateSequence"))
     575             :         {
     576           4 :             key = path + "updateSequence";
     577           4 :             CPLString value = CPLGetXMLValue(node, nullptr, "");
     578           4 :             metadata = CSLSetNameValue(metadata, key, value);
     579             :         }
     580             :     }
     581             : 
     582             :     // identification metadata
     583          10 :     CPLString path2 = path;
     584          30 :     CPLXMLNode *service = AddSimpleMetaData(
     585             :         &metadata, Capabilities, path2, "Service",
     586          25 :         {"description", "name", "label", "fees", "accessConstraints"});
     587           5 :     if (service)
     588             :     {
     589          10 :         CPLString path3 = std::move(path2);
     590          15 :         CPLString kw = GetKeywords(service, "keywords", "keyword");
     591           5 :         if (kw != "")
     592             :         {
     593           4 :             CPLString name = path + "keywords";
     594           4 :             metadata = CSLSetNameValue(metadata, name, kw);
     595             :         }
     596          20 :         CPLXMLNode *party = AddSimpleMetaData(
     597             :             &metadata, service, path3, "responsibleParty",
     598          15 :             {"individualName", "organisationName", "positionName"});
     599           5 :         CPLXMLNode *info = CPLGetXMLNode(party, "contactInfo");
     600           5 :         if (party && info)
     601             :         {
     602          10 :             CPLString path4 = path3 + "contactInfo.";
     603           5 :             CPLString path5 = path4;
     604          35 :             AddSimpleMetaData(&metadata, info, path4, "address",
     605             :                               {"deliveryPoint", "city", "administrativeArea",
     606             :                                "postalCode", "country",
     607          30 :                                "electronicMailAddress"});
     608          15 :             AddSimpleMetaData(&metadata, info, path5, "phone",
     609          10 :                               {"voice", "facsimile"});
     610             :         }
     611             :     }
     612             : 
     613             :     // provider metadata
     614             :     // operations metadata
     615          10 :     CPLString DescribeCoverageURL;
     616             :     DescribeCoverageURL = CPLGetXMLValue(
     617           5 :         CPLGetXMLNode(
     618             :             CPLGetXMLNode(
     619             :                 CPLSearchXMLNode(
     620             :                     CPLSearchXMLNode(Capabilities, "DescribeCoverage"), "Get"),
     621             :                 "OnlineResource"),
     622             :             "href"),
     623           5 :         nullptr, "");
     624             :     // if DescribeCoverageURL looks wrong (i.e. has localhost) should we change
     625             :     // it?
     626             : 
     627           5 :     this->SetMetadata(metadata, "");
     628           5 :     CSLDestroy(metadata);
     629           5 :     metadata = nullptr;
     630             : 
     631           5 :     if (CPLXMLNode *contents = CPLGetXMLNode(Capabilities, "ContentMetadata"))
     632             :     {
     633           5 :         int index = 1;
     634          23 :         for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
     635          18 :              summary = summary->psNext)
     636             :         {
     637          18 :             if (summary->eType != CXT_Element ||
     638          18 :                 !EQUAL(summary->pszValue, "CoverageOfferingBrief"))
     639             :             {
     640           0 :                 continue;
     641             :             }
     642          18 :             CPLString path3;
     643          18 :             path3.Printf("SUBDATASET_%d_", index);
     644          18 :             index += 1;
     645             : 
     646             :             // the name and description of the subdataset:
     647             :             // GDAL Data Model:
     648             :             // The value of the _NAME is a string that can be passed to
     649             :             // GDALOpen() to access the file.
     650             : 
     651          18 :             CPLXMLNode *node = CPLGetXMLNode(summary, "name");
     652          18 :             if (node)
     653             :             {
     654          36 :                 CPLString key2 = path3 + "NAME";
     655          36 :                 CPLString name = CPLGetXMLValue(node, nullptr, "");
     656          18 :                 CPLString value = DescribeCoverageURL;
     657          18 :                 value = CPLURLAddKVP(value, "VERSION", this->Version());
     658          18 :                 value = CPLURLAddKVP(value, "COVERAGE", name);
     659          18 :                 metadata = CSLSetNameValue(metadata, key2, value);
     660             :             }
     661             :             else
     662             :             {
     663           0 :                 CSLDestroy(metadata);
     664           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     665             :                          "Error in capabilities document.\n");
     666           0 :                 return CE_Failure;
     667             :             }
     668             : 
     669          18 :             node = CPLGetXMLNode(summary, "label");
     670          18 :             if (node)
     671             :             {
     672          18 :                 CPLString key2 = path3 + "DESC";
     673          18 :                 metadata = CSLSetNameValue(metadata, key2,
     674             :                                            CPLGetXMLValue(node, nullptr, ""));
     675             :             }
     676             :             else
     677             :             {
     678           0 :                 CSLDestroy(metadata);
     679           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     680             :                          "Error in capabilities document.\n");
     681           0 :                 return CE_Failure;
     682             :             }
     683             : 
     684             :             // todo: compose global bounding box from lonLatEnvelope
     685             : 
     686             :             // further subdataset (coverage) parameters are parsed in
     687             :             // ParseCoverageCapabilities
     688             :         }
     689             :     }
     690           5 :     this->SetMetadata(metadata, "SUBDATASETS");
     691           5 :     CSLDestroy(metadata);
     692           5 :     return CE_None;
     693             : }
     694             : 
     695           5 : void WCSDataset100::ParseCoverageCapabilities(CPLXMLNode *capabilities,
     696             :                                               const std::string &coverage,
     697             :                                               CPLXMLNode *metadata)
     698             : {
     699           5 :     CPLStripXMLNamespace(capabilities, nullptr, TRUE);
     700           5 :     if (CPLXMLNode *contents = CPLGetXMLNode(capabilities, "ContentMetadata"))
     701             :     {
     702          23 :         for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
     703          18 :              summary = summary->psNext)
     704             :         {
     705          18 :             if (summary->eType != CXT_Element ||
     706          18 :                 !EQUAL(summary->pszValue, "CoverageOfferingBrief"))
     707             :             {
     708          13 :                 continue;
     709             :             }
     710             : 
     711          18 :             CPLXMLNode *node = CPLGetXMLNode(summary, "name");
     712          18 :             if (node)
     713             :             {
     714          18 :                 CPLString name = CPLGetXMLValue(node, nullptr, "");
     715          18 :                 if (name != coverage)
     716             :                 {
     717          13 :                     continue;
     718             :                 }
     719             :             }
     720             : 
     721           5 :             XMLCopyMetadata(summary, metadata, "label");
     722           5 :             XMLCopyMetadata(summary, metadata, "description");
     723             : 
     724          15 :             CPLString kw = GetKeywords(summary, "keywords", "keyword");
     725           5 :             CPLAddXMLAttributeAndValue(
     726             :                 CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
     727             :                 "keywords");
     728             : 
     729             :             // skip metadataLink
     730             :         }
     731             :     }
     732           5 : }

Generated by: LCOV version 1.14