LCOV - code coverage report
Current view: top level - frmts/wcs - wcsdataset110.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 376 423 88.9 %
Date: 2025-07-05 13:22:42 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.14