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

Generated by: LCOV version 1.14