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

Generated by: LCOV version 1.14