LCOV - code coverage report
Current view: top level - frmts/wcs - wcsdataset110.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 374 421 88.8 %
Date: 2025-11-22 03:30:40 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.1.
       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          36 : std::vector<double> WCSDataset110::GetNativeExtent(int nXOff, int nYOff,
      38             :                                                    int nXSize, int nYSize,
      39             :                                                    CPL_UNUSED int nBufXSize,
      40             :                                                    CPL_UNUSED int nBufYSize)
      41             : {
      42          36 :     std::vector<double> extent;
      43             : 
      44             :     // outer edges of outer pixels.
      45          36 :     extent.push_back(m_gt[0] + (nXOff)*m_gt[1]);
      46          36 :     extent.push_back(m_gt[3] + (nYOff + nYSize) * m_gt[5]);
      47          36 :     extent.push_back(m_gt[0] + (nXOff + nXSize) * m_gt[1]);
      48          36 :     extent.push_back(m_gt[3] + (nYOff)*m_gt[5]);
      49             : 
      50          36 :     bool no_shrink = CPLGetXMLBoolean(psService, "OuterExtents");
      51             : 
      52             :     // WCS 1.1 extents are centers of outer pixels.
      53          36 :     if (!no_shrink)
      54             :     {
      55          18 :         extent[2] -= m_gt[1] * 0.5;
      56          18 :         extent[0] += m_gt[1] * 0.5;
      57          18 :         extent[1] -= m_gt[5] * 0.5;
      58          18 :         extent[3] += m_gt[5] * 0.5;
      59             :     }
      60             : 
      61             :     double dfXStep, dfYStep;
      62             : 
      63          36 :     if (!no_shrink)
      64             :     {
      65          18 :         dfXStep = (nXSize / (double)nBufXSize) * m_gt[1];
      66          18 :         dfYStep = (nYSize / (double)nBufYSize) * m_gt[5];
      67             :         // Carefully adjust bounds for pixel centered values at new
      68             :         // sampling density.
      69          18 :         if (nBufXSize != nXSize || nBufYSize != nYSize)
      70             :         {
      71           6 :             extent[0] = nXOff * m_gt[1] + m_gt[0] + dfXStep * 0.5;
      72           6 :             extent[2] = extent[0] + (nBufXSize - 1) * dfXStep;
      73             : 
      74           6 :             extent[3] = nYOff * m_gt[5] + m_gt[3] + dfYStep * 0.5;
      75           6 :             extent[1] = extent[3] + (nBufYSize - 1) * dfYStep;
      76             :         }
      77             :     }
      78             :     else
      79             :     {
      80             :         double adjust =
      81          18 :             CPLAtof(CPLGetXMLValue(psService, "BufSizeAdjust", "0.0"));
      82          18 :         dfXStep = (nXSize / ((double)nBufXSize + adjust)) * m_gt[1];
      83          18 :         dfYStep = (nYSize / ((double)nBufYSize + adjust)) * m_gt[5];
      84             :     }
      85             : 
      86          36 :     extent.push_back(dfXStep);
      87          36 :     extent.push_back(dfYStep);
      88             : 
      89          72 :     return extent;
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                        GetCoverageRequest()                          */
      94             : /*                                                                      */
      95             : /************************************************************************/
      96             : 
      97          36 : std::string WCSDataset110::GetCoverageRequest(bool scaled, int /* nBufXSize */,
      98             :                                               int /* nBufYSize */,
      99             :                                               const std::vector<double> &extent,
     100             :                                               const std::string &osBandList)
     101             : {
     102          72 :     CPLString osRequest;
     103             : 
     104             :     /* -------------------------------------------------------------------- */
     105             :     /*      URL encode strings that could have questionable characters.     */
     106             :     /* -------------------------------------------------------------------- */
     107          72 :     CPLString osCoverage = CPLGetXMLValue(psService, "CoverageName", "");
     108             : 
     109          36 :     char *pszEncoded = CPLEscapeString(osCoverage, -1, CPLES_URL);
     110          36 :     osCoverage = pszEncoded;
     111          36 :     CPLFree(pszEncoded);
     112             : 
     113          72 :     CPLString osFormat = CPLGetXMLValue(psService, "PreferredFormat", "");
     114             : 
     115          36 :     pszEncoded = CPLEscapeString(osFormat, -1, CPLES_URL);
     116          36 :     osFormat = pszEncoded;
     117          36 :     CPLFree(pszEncoded);
     118             : 
     119          72 :     CPLString osRangeSubset = CPLGetXMLValue(psService, "FieldName", "");
     120             : 
     121             :     // todo: MapServer seems to require interpolation
     122             : 
     123          72 :     CPLString interpolation = CPLGetXMLValue(psService, "Interpolation", "");
     124          36 :     if (interpolation == "")
     125             :     {
     126             :         // old undocumented key for interpolation in service
     127          36 :         interpolation = CPLGetXMLValue(psService, "Resample", "");
     128             :     }
     129          36 :     if (interpolation != "")
     130             :     {
     131           0 :         osRangeSubset += ":" + interpolation;
     132             :     }
     133             : 
     134          36 :     if (osBandList != "")
     135             :     {
     136           0 :         if (osBandIdentifier != "")
     137             :         {
     138           0 :             osRangeSubset += CPLString().Printf(
     139           0 :                 "[%s[%s]]", osBandIdentifier.c_str(), osBandList.c_str());
     140             :         }
     141             :     }
     142             : 
     143          36 :     osRangeSubset = "&RangeSubset=" + URLEncode(osRangeSubset);
     144             : 
     145          36 :     double bbox_0 = extent[0],  // min X
     146          36 :         bbox_1 = extent[1],     // min Y
     147          36 :         bbox_2 = extent[2],     // max X
     148          36 :         bbox_3 = extent[3];     // max Y
     149             : 
     150          36 :     if (axis_order_swap)
     151             :     {
     152          21 :         bbox_0 = extent[1];  // min Y
     153          21 :         bbox_1 = extent[0];  // min X
     154          21 :         bbox_2 = extent[3];  // max Y
     155          21 :         bbox_3 = extent[2];  // max X
     156             :     }
     157          36 :     std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
     158          36 :     request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
     159          72 :     request += CPLString().Printf(
     160             :         "&VERSION=%s&REQUEST=GetCoverage&IDENTIFIER=%s"
     161             :         "&FORMAT=%s&BOUNDINGBOX=%.15g,%.15g,%.15g,%.15g,%s%s",
     162          36 :         CPLGetXMLValue(psService, "Version", ""), osCoverage.c_str(),
     163             :         osFormat.c_str(), bbox_0, bbox_1, bbox_2, bbox_3, osCRS.c_str(),
     164          36 :         osRangeSubset.c_str());
     165          36 :     double origin_1 = extent[0],  // min X
     166          36 :         origin_2 = extent[3],     // max Y
     167          36 :         offset_1 = extent[4],     // dX
     168          36 :         offset_2 = extent[5];     // dY
     169             : 
     170          36 :     if (axis_order_swap)
     171             :     {
     172          21 :         origin_1 = extent[3];  // max Y
     173          21 :         origin_2 = extent[0];  // min X
     174          21 :         offset_1 = extent[5];  // dY
     175          21 :         offset_2 = extent[4];  // dX
     176             :     }
     177          72 :     CPLString offsets;
     178          36 :     if (CPLGetXMLBoolean(psService, "OffsetsPositive"))
     179             :     {
     180           9 :         offset_1 = fabs(offset_1);
     181           9 :         offset_2 = fabs(offset_2);
     182             :     }
     183          36 :     if (EQUAL(CPLGetXMLValue(psService, "NrOffsets", "4"), "2"))
     184             :     {
     185          18 :         offsets = CPLString().Printf("%.15g,%.15g", offset_1, offset_2);
     186             :     }
     187             :     else
     188             :     {
     189          18 :         if (axis_order_swap)
     190             :         {
     191             :             // Only tested with GeoServer but this is the correct offset(?)
     192          12 :             offsets = CPLString().Printf("0,%.15g,%.15g,0", offset_2, offset_1);
     193             :         }
     194             :         else
     195             :         {
     196           6 :             offsets = CPLString().Printf("%.15g,0,0,%.15g", offset_1, offset_2);
     197             :         }
     198             :     }
     199             :     bool do_not_include =
     200          36 :         CPLGetXMLBoolean(psService, "GridCRSOptional") && !scaled;
     201          36 :     if (!do_not_include)
     202             :     {
     203          72 :         request += CPLString().Printf(
     204             :             "&GridBaseCRS=%s"
     205             :             "&GridCS=urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS"
     206             :             "&GridType=urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs"
     207             :             "&GridOrigin=%.15g,%.15g"
     208             :             "&GridOffsets=%s",
     209          36 :             osCRS.c_str(), origin_1, origin_2, offsets.c_str());
     210             :     }
     211          72 :     CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
     212          36 :     if (extra != "")
     213             :     {
     214          72 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     215          72 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     216             :         {
     217          36 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     218             :             request =
     219          36 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     220             :         }
     221             :     }
     222          36 :     extra = CPLGetXMLValue(psService, "GetCoverageExtra", "");
     223          36 :     if (extra != "")
     224             :     {
     225          72 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     226          72 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     227             :         {
     228          36 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     229             :             request =
     230          36 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     231             :         }
     232             :     }
     233          36 :     CPLDebug("WCS", "Requesting %s", request.c_str());
     234          72 :     return request;
     235             : }
     236             : 
     237             : /************************************************************************/
     238             : /*                        DescribeCoverageRequest()                     */
     239             : /*                                                                      */
     240             : /************************************************************************/
     241             : 
     242          12 : std::string WCSDataset110::DescribeCoverageRequest()
     243             : {
     244          12 :     std::string request = CPLGetXMLValue(psService, "ServiceURL", "");
     245          12 :     request = CPLURLAddKVP(request.c_str(), "SERVICE", "WCS");
     246          12 :     request = CPLURLAddKVP(request.c_str(), "REQUEST", "DescribeCoverage");
     247          24 :     request = CPLURLAddKVP(request.c_str(), "VERSION",
     248          24 :                            CPLGetXMLValue(psService, "Version", "1.1.0"));
     249          24 :     request = CPLURLAddKVP(request.c_str(), "IDENTIFIERS",
     250          24 :                            CPLGetXMLValue(psService, "CoverageName", ""));
     251          24 :     CPLString extra = CPLGetXMLValue(psService, "Parameters", "");
     252          12 :     if (extra != "")
     253             :     {
     254          24 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     255          24 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     256             :         {
     257          12 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     258             :             request =
     259          12 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     260             :         }
     261             :     }
     262          12 :     extra = CPLGetXMLValue(psService, "DescribeCoverageExtra", "");
     263          12 :     if (extra != "")
     264             :     {
     265           0 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     266           0 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     267             :         {
     268           0 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     269             :             request =
     270           0 :                 CPLURLAddKVP(request.c_str(), pair[0].c_str(), pair[1].c_str());
     271             :         }
     272             :     }
     273          24 :     return request;
     274             : }
     275             : 
     276             : /************************************************************************/
     277             : /*                         CoverageOffering()                           */
     278             : /*                                                                      */
     279             : /************************************************************************/
     280             : 
     281          19 : CPLXMLNode *WCSDataset110::CoverageOffering(CPLXMLNode *psDC)
     282             : {
     283          19 :     return CPLGetXMLNode(psDC, "=CoverageDescriptions.CoverageDescription");
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                          ExtractGridInfo()                           */
     288             : /*                                                                      */
     289             : /*      Collect info about grid from describe coverage for WCS 1.1.     */
     290             : /*                                                                      */
     291             : /************************************************************************/
     292             : 
     293          36 : bool WCSDataset110::ExtractGridInfo()
     294             : 
     295             : {
     296          36 :     CPLXMLNode *psCO = CPLGetXMLNode(psService, "CoverageDescription");
     297             : 
     298          36 :     if (psCO == nullptr)
     299           0 :         return false;
     300             : 
     301             :     /* -------------------------------------------------------------------- */
     302             :     /*      We need to strip off name spaces so it is easier to             */
     303             :     /*      searchfor plain gml names.                                      */
     304             :     /* -------------------------------------------------------------------- */
     305          36 :     CPLStripXMLNamespace(psCO, nullptr, TRUE);
     306             : 
     307             :     /* -------------------------------------------------------------------- */
     308             :     /*      Verify we have a SpatialDomain and GridCRS.                     */
     309             :     /* -------------------------------------------------------------------- */
     310          36 :     CPLXMLNode *psSD = CPLGetXMLNode(psCO, "Domain.SpatialDomain");
     311          36 :     CPLXMLNode *psGCRS = CPLGetXMLNode(psSD, "GridCRS");
     312             : 
     313          36 :     if (psSD == nullptr || psGCRS == nullptr)
     314             :     {
     315           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     316             :                  "Unable to find GridCRS in CoverageDescription,\n"
     317             :                  "unable to process WCS Coverage.");
     318           0 :         return false;
     319             :     }
     320             : 
     321             :     /* -------------------------------------------------------------------- */
     322             :     /*      Establish our coordinate system.                                */
     323             :     /*   This is needed before geometry since we may have axis order swap.  */
     324             :     /* -------------------------------------------------------------------- */
     325          72 :     CPLString crs = ParseCRS(psGCRS);
     326             : 
     327          36 :     if (crs.empty())
     328             :     {
     329           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     330             :                  "Unable to find GridCRS.GridBaseCRS");
     331           0 :         return false;
     332             :     }
     333             : 
     334             :     // SetCRS should fail only if the CRS is really unknown to GDAL
     335          36 :     if (!SetCRS(crs, true))
     336             :     {
     337           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     338             :                  "Unable to interpret GridBaseCRS '%s'.", crs.c_str());
     339           0 :         return false;
     340             :     }
     341             : 
     342             :     /* -------------------------------------------------------------------- */
     343             :     /*      Collect size, origin, and offsets for SetGeometry()             */
     344             :     /*                                                                      */
     345             :     /*      Extract Geotransform from GridCRS.                              */
     346             :     /*                                                                      */
     347             :     /* -------------------------------------------------------------------- */
     348          36 :     const char *pszGridType = CPLGetXMLValue(
     349             :         psGCRS, "GridType", "urn:ogc:def:method:WCS::2dSimpleGrid");
     350             :     bool swap =
     351          36 :         axis_order_swap && !CPLGetXMLBoolean(psService, "NoGridAxisSwap");
     352             :     std::vector<double> origin =
     353          72 :         Flist(Split(CPLGetXMLValue(psGCRS, "GridOrigin", ""), " ", swap));
     354             : 
     355             :     std::vector<std::string> offset_1 =
     356          72 :         Split(CPLGetXMLValue(psGCRS, "GridOffsets", ""), " ");
     357          72 :     std::vector<std::string> offset_2;
     358          36 :     size_t n = offset_1.size();
     359          36 :     if (n % 2 != 0)
     360             :     {
     361           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     362             :                  "GridOffsets has incorrect amount of coefficients.\n"
     363             :                  "Unable to process WCS coverage.");
     364           0 :         return false;
     365             :     }
     366          90 :     for (unsigned int i = 0; i < n / 2; ++i)
     367             :     {
     368          54 :         CPLString s = offset_1.back();
     369          54 :         offset_1.erase(offset_1.end() - 1);
     370             : #if defined(__GNUC__)
     371             : #pragma GCC diagnostic push
     372             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     373             : #endif
     374          54 :         offset_2.insert(offset_2.begin(), s);
     375             : #if defined(__GNUC__)
     376             : #pragma GCC diagnostic pop
     377             : #endif
     378             :     }
     379          72 :     std::vector<std::vector<double>> offsets;
     380          36 :     if (swap)
     381             :     {
     382           0 :         offsets.push_back(Flist(offset_2));
     383           0 :         offsets.push_back(Flist(offset_1));
     384             :     }
     385             :     else
     386             :     {
     387          36 :         offsets.push_back(Flist(offset_1));
     388          36 :         offsets.push_back(Flist(offset_2));
     389             :     }
     390             : 
     391          36 :     if (strstr(pszGridType, ":2dGridIn2dCrs") ||
     392          18 :         strstr(pszGridType, ":2dGridin2dCrs"))
     393             :     {
     394          18 :         if (!(offset_1.size() == 2 && origin.size() == 2))
     395             :         {
     396           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     397             :                      "2dGridIn2dCrs does not have expected GridOrigin or\n"
     398             :                      "GridOffsets values - unable to process WCS coverage.");
     399           0 :             return false;
     400             :         }
     401             :     }
     402             : 
     403          18 :     else if (strstr(pszGridType, ":2dGridIn3dCrs"))
     404             :     {
     405           0 :         if (!(offset_1.size() == 3 && origin.size() == 3))
     406             :         {
     407           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     408             :                      "2dGridIn3dCrs does not have expected GridOrigin or\n"
     409             :                      "GridOffsets values - unable to process WCS coverage.");
     410           0 :             return false;
     411             :         }
     412             :     }
     413             : 
     414          18 :     else if (strstr(pszGridType, ":2dSimpleGrid"))
     415             :     {
     416          18 :         if (!(offset_1.size() == 1 && origin.size() == 2))
     417             :         {
     418           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     419             :                      "2dSimpleGrid does not have expected GridOrigin or\n"
     420             :                      "GridOffsets values - unable to process WCS coverage.");
     421           0 :             return false;
     422             :         }
     423             :     }
     424             : 
     425             :     else
     426             :     {
     427           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     428             :                  "Unrecognized GridCRS.GridType value '%s',\n"
     429             :                  "unable to process WCS coverage.",
     430             :                  pszGridType);
     431           0 :         return false;
     432             :     }
     433             : 
     434             :     /* -------------------------------------------------------------------- */
     435             :     /*      Search for an ImageCRS for raster size.                         */
     436             :     /* -------------------------------------------------------------------- */
     437          72 :     std::vector<int> size;
     438             :     CPLXMLNode *psNode;
     439             : 
     440         108 :     for (psNode = psSD->psChild; psNode != nullptr && size.size() == 0;
     441          72 :          psNode = psNode->psNext)
     442             :     {
     443          72 :         if (psNode->eType != CXT_Element ||
     444          72 :             !EQUAL(psNode->pszValue, "BoundingBox"))
     445          18 :             continue;
     446             : 
     447         108 :         CPLString osBBCRS = ParseCRS(psNode);
     448          54 :         if (strstr(osBBCRS, ":imageCRS"))
     449             :         {
     450          36 :             std::vector<std::string> bbox = ParseBoundingBox(psNode);
     451          18 :             if (bbox.size() >= 2)
     452             :             {
     453          36 :                 std::vector<int> low = Ilist(Split(bbox[0].c_str(), " "), 0, 2);
     454             :                 std::vector<int> high =
     455          36 :                     Ilist(Split(bbox[1].c_str(), " "), 0, 2);
     456          18 :                 if (low[0] == 0 && low[1] == 0)
     457             :                 {
     458          18 :                     size.push_back(high[0]);
     459          18 :                     size.push_back(high[1]);
     460             :                 }
     461             :             }
     462             :         }
     463             :     }
     464             : 
     465             :     /* -------------------------------------------------------------------- */
     466             :     /*      Otherwise we search for a bounding box in our coordinate        */
     467             :     /*      system and derive the size from that.                           */
     468             :     /* -------------------------------------------------------------------- */
     469          72 :     for (psNode = psSD->psChild; psNode != nullptr && size.size() == 0;
     470          36 :          psNode = psNode->psNext)
     471             :     {
     472          36 :         if (psNode->eType != CXT_Element ||
     473          36 :             !EQUAL(psNode->pszValue, "BoundingBox"))
     474           0 :             continue;
     475             : 
     476          72 :         CPLString osBBCRS = ParseCRS(psNode);
     477          36 :         if (osBBCRS == osCRS)
     478             :         {
     479          36 :             std::vector<std::string> bbox = ParseBoundingBox(psNode);
     480             :             bool not_rot =
     481          36 :                 (offsets[0].size() == 1 && offsets[1].size() == 1) ||
     482           0 :                 ((swap && offsets[0][0] == 0.0 && offsets[1][1] == 0.0) ||
     483          18 :                  (!swap && offsets[0][1] == 0.0 && offsets[1][0] == 0.0));
     484          18 :             if (bbox.size() >= 2 && not_rot)
     485             :             {
     486             :                 std::vector<double> low =
     487          36 :                     Flist(Split(bbox[0].c_str(), " ", axis_order_swap), 0, 2);
     488             :                 std::vector<double> high =
     489          18 :                     Flist(Split(bbox[1].c_str(), " ", axis_order_swap), 0, 2);
     490          18 :                 double c1 = offsets[0][0];
     491             :                 double c2 =
     492          18 :                     offsets[1].size() == 1 ? offsets[1][0] : offsets[1][1];
     493          18 :                 size.push_back((int)((high[0] - low[0]) / c1 + 1.01));
     494          18 :                 size.push_back((int)((high[1] - low[1]) / fabs(c2) + 1.01));
     495             :             }
     496             :         }
     497             :     }
     498             : 
     499          36 :     if (size.size() < 2)
     500             :     {
     501           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     502             :                  "Could not determine the size of the grid.");
     503           0 :         return false;
     504             :     }
     505             : 
     506          36 :     SetGeometry(size, origin, offsets);
     507             : 
     508             :     /* -------------------------------------------------------------------- */
     509             :     /*      Do we have a coordinate system override?                        */
     510             :     /* -------------------------------------------------------------------- */
     511          36 :     const char *pszProjOverride = CPLGetXMLValue(psService, "SRS", nullptr);
     512             : 
     513          36 :     if (pszProjOverride)
     514             :     {
     515           0 :         if (m_oSRS.SetFromUserInput(
     516             :                 pszProjOverride,
     517           0 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
     518             :             OGRERR_NONE)
     519             :         {
     520           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     521             :                      "<SRS> element contents not parsable:\n%s",
     522             :                      pszProjOverride);
     523           0 :             return false;
     524             :         }
     525             :     }
     526             : 
     527             :     /* -------------------------------------------------------------------- */
     528             :     /*      Pick a format type if we don't already have one selected.       */
     529             :     /*                                                                      */
     530             :     /*      We will prefer anything that sounds like TIFF, otherwise        */
     531             :     /*      falling back to the first supported format.  Should we          */
     532             :     /*      consider preferring the nativeFormat if available?              */
     533             :     /* -------------------------------------------------------------------- */
     534          36 :     if (CPLGetXMLValue(psService, "PreferredFormat", nullptr) == nullptr)
     535             :     {
     536          24 :         CPLString osPreferredFormat;
     537             : 
     538         125 :         for (psNode = psCO->psChild; psNode != nullptr; psNode = psNode->psNext)
     539             :         {
     540         125 :             if (psNode->eType == CXT_Element &&
     541         125 :                 EQUAL(psNode->pszValue, "SupportedFormat") && psNode->psChild &&
     542          20 :                 psNode->psChild->eType == CXT_Text)
     543             :             {
     544          20 :                 if (osPreferredFormat.empty())
     545          12 :                     osPreferredFormat = psNode->psChild->pszValue;
     546             : 
     547          20 :                 if (strstr(psNode->psChild->pszValue, "tiff") != nullptr ||
     548          11 :                     strstr(psNode->psChild->pszValue, "TIFF") != nullptr ||
     549           8 :                     strstr(psNode->psChild->pszValue, "Tiff") != nullptr)
     550             :                 {
     551          12 :                     osPreferredFormat = psNode->psChild->pszValue;
     552          12 :                     break;
     553             :                 }
     554             :             }
     555             :         }
     556             : 
     557          12 :         if (!osPreferredFormat.empty())
     558             :         {
     559          12 :             bServiceDirty = true;
     560          12 :             CPLCreateXMLElementAndValue(psService, "PreferredFormat",
     561             :                                         osPreferredFormat);
     562             :         }
     563             :     }
     564             : 
     565             :     /* -------------------------------------------------------------------- */
     566             :     /*      Try to identify a nodata value.  For now we only support the    */
     567             :     /*      singleValue mechanism.                                          */
     568             :     /* -------------------------------------------------------------------- */
     569          36 :     if (CPLGetXMLValue(psService, "NoDataValue", nullptr) == nullptr)
     570             :     {
     571             :         const char *pszSV =
     572          24 :             CPLGetXMLValue(psCO, "Range.Field.NullValue", nullptr);
     573             : 
     574          24 :         if (pszSV != nullptr && (CPLAtof(pszSV) != 0.0 || *pszSV == DIGIT_ZERO))
     575             :         {
     576           6 :             bServiceDirty = true;
     577           6 :             CPLCreateXMLElementAndValue(psService, "NoDataValue", pszSV);
     578             :         }
     579             :     }
     580             : 
     581             :     /* -------------------------------------------------------------------- */
     582             :     /*      Grab the field name, if possible.                               */
     583             :     /* -------------------------------------------------------------------- */
     584          36 :     if (CPLGetXMLValue(psService, "FieldName", nullptr) == nullptr)
     585             :     {
     586             :         CPLString osFieldName =
     587          12 :             CPLGetXMLValue(psCO, "Range.Field.Identifier", "");
     588             : 
     589          12 :         if (!osFieldName.empty())
     590             :         {
     591          12 :             bServiceDirty = true;
     592          12 :             CPLCreateXMLElementAndValue(psService, "FieldName", osFieldName);
     593             :         }
     594             :         else
     595             :         {
     596           0 :             CPLError(
     597             :                 CE_Failure, CPLE_AppDefined,
     598             :                 "Unable to find required Identifier name %s for Range Field.",
     599             :                 osCRS.c_str());
     600           0 :             return false;
     601             :         }
     602             :     }
     603             : 
     604             :     /* -------------------------------------------------------------------- */
     605             :     /*      Do we have a "Band" axis?  If so try to grab the bandcount      */
     606             :     /*      and data type from it.                                          */
     607             :     /* -------------------------------------------------------------------- */
     608          36 :     osBandIdentifier = CPLGetXMLValue(psService, "BandIdentifier", "");
     609             :     CPLXMLNode *psAxis =
     610          36 :         CPLGetXMLNode(psService, "CoverageDescription.Range.Field.Axis");
     611             : 
     612          36 :     if (osBandIdentifier.empty() &&
     613          24 :         (EQUAL(CPLGetXMLValue(psAxis, "Identifier", ""), "Band") ||
     614          81 :          EQUAL(CPLGetXMLValue(psAxis, "Identifier", ""), "Bands")) &&
     615          24 :         CPLGetXMLNode(psAxis, "AvailableKeys") != nullptr)
     616             :     {
     617          24 :         osBandIdentifier = CPLGetXMLValue(psAxis, "Identifier", "");
     618             : 
     619             :         // verify keys are ascending starting at 1
     620          24 :         CPLXMLNode *psValues = CPLGetXMLNode(psAxis, "AvailableKeys");
     621             :         CPLXMLNode *psSV;
     622             :         int iBand;
     623             : 
     624          36 :         for (psSV = psValues->psChild, iBand = 1; psSV != nullptr;
     625          12 :              psSV = psSV->psNext, iBand++)
     626             :         {
     627          30 :             if (psSV->eType != CXT_Element || !EQUAL(psSV->pszValue, "Key") ||
     628          30 :                 psSV->psChild == nullptr || psSV->psChild->eType != CXT_Text ||
     629          30 :                 atoi(psSV->psChild->pszValue) != iBand)
     630             :             {
     631          18 :                 osBandIdentifier = "";
     632          18 :                 break;
     633             :             }
     634             :         }
     635             : 
     636          24 :         if (!osBandIdentifier.empty())
     637             :         {
     638           6 :             if (CPLGetXMLValue(psService, "BandIdentifier", nullptr) == nullptr)
     639             :             {
     640           6 :                 bServiceDirty = true;
     641           6 :                 CPLSetXMLValue(psService, "BandIdentifier",
     642             :                                osBandIdentifier.c_str());
     643             :             }
     644             : 
     645           6 :             if (CPLGetXMLValue(psService, "BandCount", nullptr) == nullptr)
     646             :             {
     647           6 :                 bServiceDirty = true;
     648           6 :                 CPLSetXMLValue(psService, "BandCount",
     649          12 :                                CPLString().Printf("%d", iBand - 1));
     650             :             }
     651             :         }
     652             : 
     653             :         // Is this an ESRI server returning a GDAL recognised data type?
     654          48 :         CPLString osDataType = CPLGetXMLValue(psAxis, "DataType", "");
     655          24 :         if (GDALGetDataTypeByName(osDataType) != GDT_Unknown &&
     656           0 :             CPLGetXMLValue(psService, "BandType", nullptr) == nullptr)
     657             :         {
     658           0 :             bServiceDirty = true;
     659           0 :             CPLCreateXMLElementAndValue(psService, "BandType", osDataType);
     660             :         }
     661             :     }
     662             : 
     663          36 :     return true;
     664             : }
     665             : 
     666             : /************************************************************************/
     667             : /*                      ParseCapabilities()                             */
     668             : /************************************************************************/
     669             : 
     670          19 : CPLErr WCSDataset110::ParseCapabilities(CPLXMLNode *Capabilities,
     671             :                                         const std::string &url)
     672             : {
     673          19 :     CPLStripXMLNamespace(Capabilities, nullptr, TRUE);
     674             : 
     675             :     // make sure this is a capabilities document
     676          19 :     if (strcmp(Capabilities->pszValue, "Capabilities") != 0)
     677             :     {
     678           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     679             :                  "Error in capabilities document.\n");
     680           0 :         return CE_Failure;
     681             :     }
     682             : 
     683          19 :     char **metadata = nullptr;
     684          38 :     std::string path = "WCS_GLOBAL#";
     685             : 
     686          38 :     CPLString key = path + "version";
     687          19 :     metadata = CSLSetNameValue(metadata, key, Version());
     688             : 
     689         271 :     for (CPLXMLNode *node = Capabilities->psChild; node != nullptr;
     690         252 :          node = node->psNext)
     691             :     {
     692         252 :         const char *attr = node->pszValue;
     693         252 :         if (node->eType == CXT_Attribute && EQUAL(attr, "updateSequence"))
     694             :         {
     695          13 :             key = path + "updateSequence";
     696          13 :             CPLString value = CPLGetXMLValue(node, nullptr, "");
     697          13 :             metadata = CSLSetNameValue(metadata, key, value);
     698             :         }
     699             :     }
     700             : 
     701             :     // identification metadata
     702          38 :     std::string path2 = path;
     703          95 :     CPLXMLNode *service = AddSimpleMetaData(
     704             :         &metadata, Capabilities, path2, "ServiceIdentification",
     705          76 :         {"Title", "Abstract", "Fees", "AccessConstraints"});
     706          57 :     CPLString kw = GetKeywords(service, "Keywords", "Keyword");
     707          19 :     if (kw != "")
     708             :     {
     709          13 :         CPLString name = path + "Keywords";
     710          13 :         metadata = CSLSetNameValue(metadata, name, kw);
     711             :     }
     712          57 :     CPLString profiles = GetKeywords(service, "", "Profile");
     713          19 :     if (profiles != "")
     714             :     {
     715           7 :         CPLString name = path + "Profiles";
     716           7 :         metadata = CSLSetNameValue(metadata, name, profiles);
     717             :     }
     718             : 
     719             :     // provider metadata
     720          19 :     path2 = path;
     721          38 :     CPLXMLNode *provider = AddSimpleMetaData(
     722          19 :         &metadata, Capabilities, path2, "ServiceProvider", {"ProviderName"});
     723          19 :     if (provider)
     724             :     {
     725          19 :         CPLXMLNode *site = CPLGetXMLNode(provider, "ProviderSite");
     726          19 :         if (site)
     727             :         {
     728          30 :             std::string path3 = path2 + "ProviderSite";
     729             :             CPLString value =
     730          15 :                 CPLGetXMLValue(CPLGetXMLNode(site, "href"), nullptr, "");
     731          15 :             metadata = CSLSetNameValue(metadata, path3.c_str(), value);
     732             :         }
     733          38 :         std::string path3 = std::move(path2);
     734             :         CPLXMLNode *contact =
     735          76 :             AddSimpleMetaData(&metadata, provider, path3, "ServiceContact",
     736          57 :                               {"IndividualName", "PositionName", "Role"});
     737          19 :         if (contact)
     738             :         {
     739          38 :             std::string path4 = std::move(path3);
     740             :             CPLXMLNode *info =
     741          57 :                 AddSimpleMetaData(&metadata, contact, path4, "ContactInfo",
     742          38 :                                   {"HoursOfService", "ContactInstructions"});
     743          19 :             if (info)
     744             :             {
     745          38 :                 std::string path5 = path4;
     746          38 :                 std::string path6 = path4;
     747         133 :                 AddSimpleMetaData(&metadata, info, path5, "Address",
     748             :                                   {"DeliveryPoint", "City",
     749             :                                    "AdministrativeArea", "PostalCode",
     750         114 :                                    "Country", "ElectronicMailAddress"});
     751          57 :                 AddSimpleMetaData(&metadata, info, path6, "Phone",
     752          38 :                                   {"Voice", "Facsimile"});
     753          19 :                 CPL_IGNORE_RET_VAL(path4);
     754             :             }
     755             :         }
     756             :     }
     757             : 
     758             :     // operations metadata
     759          38 :     CPLString DescribeCoverageURL = "";
     760          19 :     CPLXMLNode *service2 = CPLGetXMLNode(Capabilities, "OperationsMetadata");
     761          19 :     if (service2)
     762             :     {
     763          91 :         for (CPLXMLNode *operation = service2->psChild; operation != nullptr;
     764          72 :              operation = operation->psNext)
     765             :         {
     766          72 :             if (operation->eType != CXT_Element ||
     767          68 :                 !EQUAL(operation->pszValue, "Operation"))
     768             :             {
     769          15 :                 continue;
     770             :             }
     771          57 :             if (EQUAL(CPLGetXMLValue(CPLGetXMLNode(operation, "name"), nullptr,
     772             :                                      ""),
     773             :                       "DescribeCoverage"))
     774             :             {
     775             :                 DescribeCoverageURL = CPLGetXMLValue(
     776          19 :                     CPLGetXMLNode(CPLSearchXMLNode(operation, "Get"), "href"),
     777          19 :                     nullptr, "");
     778             :             }
     779             :         }
     780             :     }
     781             :     // if DescribeCoverageURL looks wrong, we change it
     782          19 :     if (DescribeCoverageURL.find("localhost") != std::string::npos)
     783             :     {
     784           2 :         DescribeCoverageURL = URLRemoveKey(url.c_str(), "request");
     785             :     }
     786             : 
     787             :     // service metadata (in 2.0)
     788          38 :     CPLString ext = "ServiceMetadata";
     789          57 :     CPLString formats = GetKeywords(Capabilities, ext, "formatSupported");
     790          19 :     if (formats != "")
     791             :     {
     792           7 :         CPLString name = path + "formatSupported";
     793           7 :         metadata = CSLSetNameValue(metadata, name, formats);
     794             :     }
     795             :     // wcs:Extensions: interpolation, CRS, others?
     796          19 :     ext += ".Extension";
     797             :     CPLString interpolation =
     798          57 :         GetKeywords(Capabilities, ext, "interpolationSupported");
     799          19 :     if (interpolation == "")
     800             :     {
     801             :         interpolation =
     802          30 :             GetKeywords(Capabilities, ext + ".InterpolationMetadata",
     803          15 :                         "InterpolationSupported");
     804             :     }
     805          19 :     if (interpolation != "")
     806             :     {
     807           7 :         CPLString name = path + "InterpolationSupported";
     808           7 :         metadata = CSLSetNameValue(metadata, name, interpolation);
     809             :     }
     810          57 :     CPLString crs = GetKeywords(Capabilities, ext, "crsSupported");
     811          19 :     if (crs == "")
     812             :     {
     813          15 :         crs = GetKeywords(Capabilities, ext + ".CrsMetadata", "crsSupported");
     814             :     }
     815          19 :     if (crs != "")
     816             :     {
     817           5 :         CPLString name = path + "crsSupported";
     818           5 :         metadata = CSLSetNameValue(metadata, name, crs);
     819             :     }
     820             : 
     821          19 :     this->SetMetadata(metadata, "");
     822          19 :     CSLDestroy(metadata);
     823          19 :     metadata = nullptr;
     824             : 
     825             :     // contents metadata
     826          19 :     CPLXMLNode *contents = CPLGetXMLNode(Capabilities, "Contents");
     827          19 :     if (contents)
     828             :     {
     829          19 :         int index = 1;
     830         150 :         for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
     831         131 :              summary = summary->psNext)
     832             :         {
     833         131 :             if (summary->eType != CXT_Element ||
     834         129 :                 !EQUAL(summary->pszValue, "CoverageSummary"))
     835             :             {
     836          26 :                 continue;
     837             :             }
     838         105 :             CPLString path3;
     839         105 :             path3.Printf("SUBDATASET_%d_", index);
     840         105 :             index += 1;
     841             : 
     842             :             // the name and description of the subdataset:
     843             :             // GDAL Data Model:
     844             :             // The value of the _NAME is a string that can be passed to
     845             :             // GDALOpen() to access the file.
     846             : 
     847         105 :             CPLString key2 = path3 + "NAME";
     848             : 
     849         105 :             CPLString name = DescribeCoverageURL;
     850         105 :             name = CPLURLAddKVP(name, "version", this->Version());
     851             : 
     852         105 :             CPLXMLNode *node = CPLGetXMLNode(summary, "CoverageId");
     853         105 :             std::string id;
     854         105 :             if (node)
     855             :             {
     856          60 :                 id = CPLGetXMLValue(node, nullptr, "");
     857             :             }
     858             :             else
     859             :             {
     860          45 :                 node = CPLGetXMLNode(summary, "Identifier");
     861          45 :                 if (node)
     862             :                 {
     863          45 :                     id = CPLGetXMLValue(node, nullptr, "");
     864             :                 }
     865             :                 else
     866             :                 {
     867             :                     // todo: maybe not an error since CoverageSummary may be
     868             :                     // within CoverageSummary (07-067r5 Fig4)
     869           0 :                     CSLDestroy(metadata);
     870           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     871             :                              "Error in capabilities document.\n");
     872           0 :                     return CE_Failure;
     873             :                 }
     874             :             }
     875         105 :             name = CPLURLAddKVP(name, "coverage", id.c_str());
     876         105 :             name = "WCS:" + name;
     877         105 :             metadata = CSLSetNameValue(metadata, key2, name);
     878             : 
     879         105 :             key2 = path3 + "DESC";
     880             : 
     881         105 :             node = CPLGetXMLNode(summary, "Title");
     882         105 :             if (node)
     883             :             {
     884          45 :                 metadata = CSLSetNameValue(metadata, key2,
     885             :                                            CPLGetXMLValue(node, nullptr, ""));
     886             :             }
     887             :             else
     888             :             {
     889          60 :                 metadata = CSLSetNameValue(metadata, key2, id.c_str());
     890             :             }
     891             : 
     892             :             // todo: compose global bounding box from WGS84BoundingBox and
     893             :             // BoundingBox
     894             : 
     895             :             // further subdataset (coverage) parameters are parsed in
     896             :             // ParseCoverageCapabilities
     897             :         }
     898             :     }
     899          19 :     this->SetMetadata(metadata, "SUBDATASETS");
     900          19 :     CSLDestroy(metadata);
     901          19 :     return CE_None;
     902             : }
     903             : 
     904          19 : void WCSDataset110::ParseCoverageCapabilities(CPLXMLNode *capabilities,
     905             :                                               const std::string &coverage,
     906             :                                               CPLXMLNode *metadata)
     907             : {
     908          19 :     CPLStripXMLNamespace(capabilities, nullptr, TRUE);
     909          19 :     CPLXMLNode *contents = CPLGetXMLNode(capabilities, "Contents");
     910          19 :     if (contents)
     911             :     {
     912         150 :         for (CPLXMLNode *summary = contents->psChild; summary != nullptr;
     913         131 :              summary = summary->psNext)
     914             :         {
     915         131 :             if (summary->eType != CXT_Element ||
     916         129 :                 !EQUAL(summary->pszValue, "CoverageSummary"))
     917             :             {
     918         112 :                 continue;
     919             :             }
     920         105 :             CPLXMLNode *node = CPLGetXMLNode(summary, "CoverageId");
     921         105 :             CPLString id;
     922         105 :             if (node)
     923             :             {
     924          60 :                 id = CPLGetXMLValue(node, nullptr, "");
     925             :             }
     926             :             else
     927             :             {
     928          45 :                 node = CPLGetXMLNode(summary, "Identifier");
     929          45 :                 if (node)
     930             :                 {
     931          45 :                     id = CPLGetXMLValue(node, nullptr, "");
     932             :                 }
     933             :                 else
     934             :                 {
     935           0 :                     id = "";
     936             :                 }
     937             :             }
     938         105 :             if (id != coverage)
     939             :             {
     940          86 :                 continue;
     941             :             }
     942             : 
     943             :             // Description
     944             :             // todo: there could be Title and Abstract for each supported
     945             :             // language
     946          19 :             XMLCopyMetadata(summary, metadata, "Title");
     947          19 :             XMLCopyMetadata(summary, metadata, "Abstract");
     948             : 
     949             :             // 2.0.1 stuff
     950          19 :             XMLCopyMetadata(summary, metadata, "CoverageSubtype");
     951             : 
     952             :             // Keywords
     953          57 :             CPLString kw = GetKeywords(summary, "Keywords", "Keyword");
     954          19 :             CPLAddXMLAttributeAndValue(
     955             :                 CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
     956             :                 "Keywords");
     957             : 
     958             :             // WCSContents
     959          19 :             const char *tags[] = {"SupportedCRS", "SupportedFormat",
     960             :                                   "OtherSource"};
     961          76 :             for (unsigned int i = 0; i < CPL_ARRAYSIZE(tags); i++)
     962             :             {
     963          57 :                 kw = GetKeywords(summary, "", tags[i]);
     964          57 :                 CPLAddXMLAttributeAndValue(
     965             :                     CPLCreateXMLElementAndValue(metadata, "MDI", kw), "key",
     966             :                     tags[i]);
     967             :             }
     968             : 
     969             :             // skipping WGS84BoundingBox, BoundingBox, Metadata, Extension
     970             :             // since those we'll get from coverage description
     971             :         }
     972             :     }
     973          19 : }

Generated by: LCOV version 1.14