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

Generated by: LCOV version 1.14