LCOV - code coverage report
Current view: top level - frmts/wcs - wcsdataset110.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 378 427 88.5 %
Date: 2024-11-21 22:18:42 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.14