LCOV - code coverage report
Current view: top level - frmts/wcs - wcsdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 447 664 67.3 %
Date: 2025-05-31 00:00:17 Functions: 27 31 87.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WCS Client Driver
       4             :  * Purpose:  Implementation of Dataset and RasterBand classes for WCS.
       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             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_string.h"
      15             : #include "cpl_minixml.h"
      16             : #include "cpl_http.h"
      17             : #include "gmlutils.h"
      18             : #include "gdal_frmts.h"
      19             : #include "gdal_pam.h"
      20             : #include "ogr_spatialref.h"
      21             : #include "gmlcoverage.h"
      22             : 
      23             : #include <algorithm>
      24             : 
      25             : #include "wcsdataset.h"
      26             : #include "wcsrasterband.h"
      27             : #include "wcsutils.h"
      28             : #include "wcsdrivercore.h"
      29             : 
      30             : using namespace WCSUtils;
      31             : 
      32             : /************************************************************************/
      33             : /*                             WCSDataset()                             */
      34             : /************************************************************************/
      35             : 
      36         121 : WCSDataset::WCSDataset(int version, const char *cache_dir)
      37             :     : m_cache_dir(cache_dir), bServiceDirty(false), psService(nullptr),
      38             :       papszSDSModifiers(nullptr), m_Version(version), native_crs(true),
      39             :       axis_order_swap(false), pabySavedDataBuffer(nullptr),
      40         121 :       papszHttpOptions(nullptr), nMaxCols(-1), nMaxRows(-1)
      41             : {
      42         121 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      43         121 :     adfGeoTransform[0] = 0.0;
      44         121 :     adfGeoTransform[1] = 1.0;
      45         121 :     adfGeoTransform[2] = 0.0;
      46         121 :     adfGeoTransform[3] = 0.0;
      47         121 :     adfGeoTransform[4] = 0.0;
      48         121 :     adfGeoTransform[5] = 1.0;
      49             : 
      50         121 :     apszCoverageOfferingMD[0] = nullptr;
      51         121 :     apszCoverageOfferingMD[1] = nullptr;
      52         121 : }
      53             : 
      54             : /************************************************************************/
      55             : /*                            ~WCSDataset()                             */
      56             : /************************************************************************/
      57             : 
      58         121 : WCSDataset::~WCSDataset()
      59             : 
      60             : {
      61             :     // perhaps this should be moved into a FlushCache(bool bAtClosing) method.
      62         121 :     if (bServiceDirty && !STARTS_WITH_CI(GetDescription(), "<WCS_GDAL>"))
      63             :     {
      64          24 :         CPLSerializeXMLTreeToFile(psService, GetDescription());
      65          24 :         bServiceDirty = false;
      66             :     }
      67             : 
      68         121 :     CPLDestroyXMLNode(psService);
      69             : 
      70         121 :     CSLDestroy(papszHttpOptions);
      71         121 :     CSLDestroy(papszSDSModifiers);
      72             : 
      73         121 :     CPLFree(apszCoverageOfferingMD[0]);
      74             : 
      75         121 :     FlushMemoryResult();
      76         121 : }
      77             : 
      78             : /************************************************************************/
      79             : /*                           SetCRS()                                   */
      80             : /*                                                                      */
      81             : /*      Set the name and the WKT of the projection of this dataset.     */
      82             : /*      Based on the projection, sets the axis order flag.              */
      83             : /*      Also set the native flag.                                       */
      84             : /************************************************************************/
      85             : 
      86          57 : bool WCSDataset::SetCRS(const std::string &crs, bool native)
      87             : {
      88          57 :     osCRS = crs;
      89          57 :     char *pszProjection = nullptr;
      90          57 :     if (!CRSImpliesAxisOrderSwap(osCRS, axis_order_swap, &pszProjection))
      91             :     {
      92           0 :         return false;
      93             :     }
      94          57 :     m_oSRS.importFromWkt(pszProjection);
      95          57 :     CPLFree(pszProjection);
      96          57 :     native_crs = native;
      97          57 :     return true;
      98             : }
      99             : 
     100             : /************************************************************************/
     101             : /*                           SetGeometry()                              */
     102             : /*                                                                      */
     103             : /*      Set GeoTransform and RasterSize from the coverage envelope,     */
     104             : /*      axis_order, grid size, and grid offsets.                        */
     105             : /************************************************************************/
     106             : 
     107          57 : void WCSDataset::SetGeometry(const std::vector<int> &size,
     108             :                              const std::vector<double> &origin,
     109             :                              const std::vector<std::vector<double>> &offsets)
     110             : {
     111             :     // note that this method is not used by wcsdataset100.cpp
     112          57 :     nRasterXSize = size[0];
     113          57 :     nRasterYSize = size[1];
     114             : 
     115          57 :     adfGeoTransform[0] = origin[0];
     116          57 :     adfGeoTransform[1] = offsets[0][0];
     117          57 :     adfGeoTransform[2] = offsets[0].size() == 1 ? 0.0 : offsets[0][1];
     118          57 :     adfGeoTransform[3] = origin[1];
     119          57 :     adfGeoTransform[4] = offsets[1].size() == 1 ? 0.0 : offsets[1][0];
     120          57 :     adfGeoTransform[5] = offsets[1].size() == 1 ? offsets[1][0] : offsets[1][1];
     121             : 
     122          57 :     if (!CPLGetXMLBoolean(psService, "OriginAtBoundary"))
     123             :     {
     124          54 :         adfGeoTransform[0] -= adfGeoTransform[1] * 0.5;
     125          54 :         adfGeoTransform[0] -= adfGeoTransform[2] * 0.5;
     126          54 :         adfGeoTransform[3] -= adfGeoTransform[4] * 0.5;
     127          54 :         adfGeoTransform[3] -= adfGeoTransform[5] * 0.5;
     128             :     }
     129          57 : }
     130             : 
     131             : /************************************************************************/
     132             : /*                           TestUseBlockIO()                           */
     133             : /*                                                                      */
     134             : /*      Check whether we should use blocked IO (true) or direct io      */
     135             : /*      (FALSE) for a given request configuration and environment.      */
     136             : /************************************************************************/
     137             : 
     138          80 : int WCSDataset::TestUseBlockIO(CPL_UNUSED int nXOff, CPL_UNUSED int nYOff,
     139             :                                int nXSize, int nYSize, int nBufXSize,
     140             :                                int nBufYSize) const
     141             : {
     142          80 :     int bUseBlockedIO = bForceCachedIO;
     143             : 
     144          80 :     if (nYSize == 1 || nXSize * ((double)nYSize) < 100.0)
     145          56 :         bUseBlockedIO = TRUE;
     146             : 
     147          80 :     if (nBufYSize == 1 || nBufXSize * ((double)nBufYSize) < 100.0)
     148          56 :         bUseBlockedIO = TRUE;
     149             : 
     150         136 :     if (bUseBlockedIO &&
     151          56 :         CPLTestBool(CPLGetConfigOption("GDAL_ONE_BIG_READ", "NO")))
     152           0 :         bUseBlockedIO = FALSE;
     153             : 
     154          80 :     return bUseBlockedIO;
     155             : }
     156             : 
     157             : /************************************************************************/
     158             : /*                             IRasterIO()                              */
     159             : /************************************************************************/
     160             : 
     161          22 : CPLErr WCSDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     162             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
     163             :                              int nBufYSize, GDALDataType eBufType,
     164             :                              int nBandCount, BANDMAP_TYPE panBandMap,
     165             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
     166             :                              GSpacing nBandSpace,
     167             :                              GDALRasterIOExtraArg *psExtraArg)
     168             : 
     169             : {
     170          22 :     if ((nMaxCols > 0 && nMaxCols < nBufXSize) ||
     171          22 :         (nMaxRows > 0 && nMaxRows < nBufYSize))
     172           0 :         return CE_Failure;
     173             : 
     174             :     /* -------------------------------------------------------------------- */
     175             :     /*      We need various criteria to skip out to block based methods.    */
     176             :     /* -------------------------------------------------------------------- */
     177          22 :     if (TestUseBlockIO(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize))
     178          11 :         return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     179             :                                          pData, nBufXSize, nBufYSize, eBufType,
     180             :                                          nBandCount, panBandMap, nPixelSpace,
     181          11 :                                          nLineSpace, nBandSpace, psExtraArg);
     182             :     else
     183          11 :         return DirectRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     184             :                               nBufXSize, nBufYSize, eBufType, nBandCount,
     185             :                               panBandMap, nPixelSpace, nLineSpace, nBandSpace,
     186          11 :                               psExtraArg);
     187             : }
     188             : 
     189             : /************************************************************************/
     190             : /*                           DirectRasterIO()                           */
     191             : /*                                                                      */
     192             : /*      Make exactly one request to the server for this data.           */
     193             : /************************************************************************/
     194             : 
     195          24 : CPLErr WCSDataset::DirectRasterIO(CPL_UNUSED GDALRWFlag eRWFlag, int nXOff,
     196             :                                   int nYOff, int nXSize, int nYSize,
     197             :                                   void *pData, int nBufXSize, int nBufYSize,
     198             :                                   GDALDataType eBufType, int nBandCount,
     199             :                                   const int *panBandMap, GSpacing nPixelSpace,
     200             :                                   GSpacing nLineSpace, GSpacing nBandSpace,
     201             :                                   GDALRasterIOExtraArg *psExtraArg)
     202             : {
     203          24 :     CPLDebug("WCS", "DirectRasterIO(%d,%d,%d,%d) -> (%d,%d) (%d bands)\n",
     204             :              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, nBandCount);
     205             : 
     206             :     /* -------------------------------------------------------------------- */
     207             :     /*      Get the coverage.                                               */
     208             :     /* -------------------------------------------------------------------- */
     209             : 
     210             :     // if INTERLEAVE is set to PIXEL, then we'll request all bands.
     211             :     // That is necessary at least with MapServer, which seems to often
     212             :     // return all bands instead of requested.
     213             :     // todo: in 2.0.1 the band list in this dataset may be user-defined
     214             : 
     215          24 :     int band_count = nBandCount;
     216          24 :     if (EQUAL(CPLGetXMLValue(psService, "INTERLEAVE", ""), "PIXEL"))
     217             :     {
     218          24 :         band_count = 0;
     219             :     }
     220             : 
     221          24 :     CPLHTTPResult *psResult = nullptr;
     222             :     CPLErr eErr =
     223          24 :         GetCoverage(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
     224             :                     band_count, panBandMap, psExtraArg, &psResult);
     225             : 
     226          24 :     if (eErr != CE_None)
     227           0 :         return eErr;
     228             : 
     229             :     /* -------------------------------------------------------------------- */
     230             :     /*      Try and open result as a dataset.                               */
     231             :     /* -------------------------------------------------------------------- */
     232          24 :     GDALDataset *poTileDS = GDALOpenResult(psResult);
     233             : 
     234          24 :     if (poTileDS == nullptr)
     235           0 :         return CE_Failure;
     236             : 
     237             :     /* -------------------------------------------------------------------- */
     238             :     /*      Verify configuration.                                           */
     239             :     /* -------------------------------------------------------------------- */
     240          48 :     if (poTileDS->GetRasterXSize() != nBufXSize ||
     241          24 :         poTileDS->GetRasterYSize() != nBufYSize)
     242             :     {
     243           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     244             :                  "Returned tile does not match expected configuration.\n"
     245             :                  "Got %dx%d instead of %dx%d.",
     246             :                  poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
     247             :                  nBufXSize, nBufYSize);
     248           0 :         delete poTileDS;
     249           0 :         return CE_Failure;
     250             :     }
     251             : 
     252          24 :     if (band_count != 0 && ((!osBandIdentifier.empty() &&
     253           0 :                              poTileDS->GetRasterCount() != nBandCount) ||
     254           0 :                             (osBandIdentifier.empty() &&
     255           0 :                              poTileDS->GetRasterCount() != GetRasterCount())))
     256             :     {
     257           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     258             :                  "Returned tile does not match expected band count.");
     259           0 :         delete poTileDS;
     260           0 :         return CE_Failure;
     261             :     }
     262             : 
     263             :     /* -------------------------------------------------------------------- */
     264             :     /*      Pull requested bands from the downloaded dataset.               */
     265             :     /* -------------------------------------------------------------------- */
     266          24 :     eErr = CE_None;
     267             : 
     268          69 :     for (int iBand = 0; iBand < nBandCount && eErr == CE_None; iBand++)
     269             :     {
     270          45 :         GDALRasterBand *poTileBand = nullptr;
     271             : 
     272          45 :         if (!osBandIdentifier.empty())
     273          18 :             poTileBand = poTileDS->GetRasterBand(iBand + 1);
     274             :         else
     275          27 :             poTileBand = poTileDS->GetRasterBand(panBandMap[iBand]);
     276             : 
     277          45 :         eErr = poTileBand->RasterIO(GF_Read, 0, 0, nBufXSize, nBufYSize,
     278          45 :                                     ((GByte *)pData) + iBand * nBandSpace,
     279             :                                     nBufXSize, nBufYSize, eBufType, nPixelSpace,
     280             :                                     nLineSpace, nullptr);
     281             :     }
     282             : 
     283             :     /* -------------------------------------------------------------------- */
     284             :     /*      Cleanup                                                         */
     285             :     /* -------------------------------------------------------------------- */
     286          24 :     delete poTileDS;
     287             : 
     288          24 :     FlushMemoryResult();
     289             : 
     290          24 :     return eErr;
     291             : }
     292             : 
     293             : static bool ProcessError(CPLHTTPResult *psResult);
     294             : 
     295             : /************************************************************************/
     296             : /*                            GetCoverage()                             */
     297             : /*                                                                      */
     298             : /*      Issue the appropriate version of request for a given window,    */
     299             : /*      buffer size and band list.                                      */
     300             : /************************************************************************/
     301             : 
     302          72 : CPLErr WCSDataset::GetCoverage(int nXOff, int nYOff, int nXSize, int nYSize,
     303             :                                int nBufXSize, int nBufYSize, int nBandCount,
     304             :                                const int *panBandList,
     305             :                                GDALRasterIOExtraArg *psExtraArg,
     306             :                                CPLHTTPResult **ppsResult)
     307             : 
     308             : {
     309             :     /* -------------------------------------------------------------------- */
     310             :     /*      Figure out the georeferenced extents.                           */
     311             :     /* -------------------------------------------------------------------- */
     312             :     std::vector<double> extent =
     313         144 :         GetExtent(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
     314             : 
     315             :     /* -------------------------------------------------------------------- */
     316             :     /*      Build band list if we have the band identifier.                 */
     317             :     /* -------------------------------------------------------------------- */
     318         144 :     std::string osBandList;
     319             : 
     320          72 :     if (!osBandIdentifier.empty() && nBandCount > 0 && panBandList != nullptr)
     321             :     {
     322             :         int iBand;
     323             : 
     324           0 :         for (iBand = 0; iBand < nBandCount; iBand++)
     325             :         {
     326           0 :             if (iBand > 0)
     327           0 :                 osBandList += ",";
     328           0 :             osBandList += CPLString().Printf("%d", panBandList[iBand]);
     329             :         }
     330             :     }
     331             : 
     332             :     /* -------------------------------------------------------------------- */
     333             :     /*      Construct a KVP GetCoverage request.                            */
     334             :     /* -------------------------------------------------------------------- */
     335          72 :     bool scaled = nBufXSize != nXSize || nBufYSize != nYSize;
     336             :     std::string osRequest =
     337         144 :         GetCoverageRequest(scaled, nBufXSize, nBufYSize, extent, osBandList);
     338             :     // for the test setup we need the actual URLs this driver generates
     339             :     // fprintf(stdout, "URL=%s\n", osRequest.c_str());
     340             : 
     341             :     /* -------------------------------------------------------------------- */
     342             :     /*      Fetch the result.                                               */
     343             :     /* -------------------------------------------------------------------- */
     344          72 :     CPLErrorReset();
     345          72 :     if (psExtraArg && psExtraArg->pfnProgress != nullptr)
     346             :     {
     347          24 :         *ppsResult = CPLHTTPFetchEx(
     348          24 :             osRequest.c_str(), papszHttpOptions, psExtraArg->pfnProgress,
     349             :             psExtraArg->pProgressData, nullptr, nullptr);
     350             :     }
     351             :     else
     352             :     {
     353          48 :         *ppsResult = CPLHTTPFetch(osRequest.c_str(), papszHttpOptions);
     354             :     }
     355             : 
     356          72 :     if (ProcessError(*ppsResult))
     357           0 :         return CE_Failure;
     358             :     else
     359          72 :         return CE_None;
     360             : }
     361             : 
     362             : /************************************************************************/
     363             : /*                          DescribeCoverage()                          */
     364             : /*                                                                      */
     365             : /*      Fetch the DescribeCoverage result and attach it to the          */
     366             : /*      service description.                                            */
     367             : /************************************************************************/
     368             : 
     369          25 : int WCSDataset::DescribeCoverage()
     370             : 
     371             : {
     372          50 :     std::string osRequest;
     373             : 
     374             :     /* -------------------------------------------------------------------- */
     375             :     /*      Fetch coverage description for this coverage.                   */
     376             :     /* -------------------------------------------------------------------- */
     377             : 
     378          25 :     CPLXMLNode *psDC = nullptr;
     379             : 
     380             :     // if it is in cache, get it from there
     381             :     std::string dc_filename =
     382          50 :         this->GetDescription();  // the WCS_GDAL file (<basename>.xml)
     383          25 :     dc_filename.erase(dc_filename.length() - 4, 4);
     384          25 :     dc_filename += ".DC.xml";
     385          25 :     if (FileIsReadable(dc_filename))
     386             :     {
     387           0 :         psDC = CPLParseXMLFile(dc_filename.c_str());
     388             :     }
     389             : 
     390          25 :     if (!psDC)
     391             :     {
     392          25 :         osRequest = DescribeCoverageRequest();
     393          25 :         CPLErrorReset();
     394             :         CPLHTTPResult *psResult =
     395          25 :             CPLHTTPFetch(osRequest.c_str(), papszHttpOptions);
     396          25 :         if (ProcessError(psResult))
     397             :         {
     398           1 :             return FALSE;
     399             :         }
     400             : 
     401             :         /* --------------------------------------------------------------------
     402             :          */
     403             :         /*      Parse result. */
     404             :         /* --------------------------------------------------------------------
     405             :          */
     406          24 :         psDC = CPLParseXMLString((const char *)psResult->pabyData);
     407          24 :         CPLHTTPDestroyResult(psResult);
     408             : 
     409          24 :         if (psDC == nullptr)
     410             :         {
     411           0 :             return FALSE;
     412             :         }
     413             : 
     414             :         // if we have cache, put it there
     415          24 :         if (dc_filename != "")
     416             :         {
     417          24 :             CPLSerializeXMLTreeToFile(psDC, dc_filename.c_str());
     418             :         }
     419             :     }
     420             : 
     421          24 :     CPLStripXMLNamespace(psDC, nullptr, TRUE);
     422             : 
     423             :     /* -------------------------------------------------------------------- */
     424             :     /*      Did we get a CoverageOffering?                                  */
     425             :     /* -------------------------------------------------------------------- */
     426          24 :     CPLXMLNode *psCO = CoverageOffering(psDC);
     427             : 
     428          24 :     if (!psCO)
     429             :     {
     430           0 :         CPLDestroyXMLNode(psDC);
     431             : 
     432           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     433             :                  "Failed to fetch a <CoverageOffering> back %s.",
     434             :                  osRequest.c_str());
     435           0 :         return FALSE;
     436             :     }
     437             : 
     438             :     /* -------------------------------------------------------------------- */
     439             :     /*      Duplicate the coverage offering, and insert into                */
     440             :     /* -------------------------------------------------------------------- */
     441          24 :     CPLXMLNode *psNext = psCO->psNext;
     442          24 :     psCO->psNext = nullptr;
     443             : 
     444          24 :     CPLAddXMLChild(psService, CPLCloneXMLTree(psCO));
     445          24 :     bServiceDirty = true;
     446             : 
     447          24 :     psCO->psNext = psNext;
     448             : 
     449          24 :     CPLDestroyXMLNode(psDC);
     450          24 :     return TRUE;
     451             : }
     452             : 
     453             : /************************************************************************/
     454             : /*                            ProcessError()                            */
     455             : /*                                                                      */
     456             : /*      Process an HTTP error, reporting it via CPL, and destroying     */
     457             : /*      the HTTP result object.  Returns TRUE if there was an error,    */
     458             : /*      or FALSE if the result seems ok.                                */
     459             : /************************************************************************/
     460             : 
     461         121 : static bool ProcessError(CPLHTTPResult *psResult)
     462             : 
     463             : {
     464             :     /* -------------------------------------------------------------------- */
     465             :     /*      There isn't much we can do in this case.  Hopefully an error    */
     466             :     /*      was already issued by CPLHTTPFetch()                            */
     467             :     /* -------------------------------------------------------------------- */
     468         121 :     if (psResult == nullptr || psResult->nDataLen == 0)
     469             :     {
     470           1 :         CPLHTTPDestroyResult(psResult);
     471           1 :         return TRUE;
     472             :     }
     473             : 
     474             :     /* -------------------------------------------------------------------- */
     475             :     /*      If we got an html document, we presume it is an error           */
     476             :     /*      message and report it verbatim up to a certain size limit.      */
     477             :     /* -------------------------------------------------------------------- */
     478             : 
     479         120 :     if (psResult->pszContentType != nullptr &&
     480         120 :         strstr(psResult->pszContentType, "html") != nullptr)
     481             :     {
     482           0 :         std::string osErrorMsg = (char *)psResult->pabyData;
     483             : 
     484           0 :         if (osErrorMsg.size() > 2048)
     485           0 :             osErrorMsg.resize(2048);
     486             : 
     487           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Malformed Result:\n%s",
     488             :                  osErrorMsg.c_str());
     489           0 :         CPLHTTPDestroyResult(psResult);
     490           0 :         return TRUE;
     491             :     }
     492             : 
     493             :     /* -------------------------------------------------------------------- */
     494             :     /*      Does this look like a service exception?  We would like to      */
     495             :     /*      check based on the Content-type, but this seems quite           */
     496             :     /*      undependable, even from MapServer!                              */
     497             :     /* -------------------------------------------------------------------- */
     498         120 :     if (strstr((const char *)psResult->pabyData, "ExceptionReport"))
     499             :     {
     500             :         CPLXMLNode *psTree =
     501           0 :             CPLParseXMLString((const char *)psResult->pabyData);
     502           0 :         CPLStripXMLNamespace(psTree, nullptr, TRUE);
     503             :         std::string msg = CPLGetXMLValue(
     504           0 :             psTree, "=ServiceExceptionReport.ServiceException", "");
     505           0 :         if (msg == "")
     506             :         {
     507             :             msg = CPLGetXMLValue(
     508           0 :                 psTree, "=ExceptionReport.Exception.exceptionCode", "");
     509           0 :             if (msg != "")
     510             :             {
     511           0 :                 msg += ": ";
     512             :             }
     513             :             msg += CPLGetXMLValue(
     514           0 :                 psTree, "=ExceptionReport.Exception.ExceptionText", "");
     515             :         }
     516           0 :         if (msg != "")
     517           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", msg.c_str());
     518             :         else
     519           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     520             :                      "Corrupt Service Exception:\n%s",
     521           0 :                      (const char *)psResult->pabyData);
     522           0 :         CPLDestroyXMLNode(psTree);
     523           0 :         CPLHTTPDestroyResult(psResult);
     524           0 :         return TRUE;
     525             :     }
     526             : 
     527             :     /* -------------------------------------------------------------------- */
     528             :     /*      Hopefully the error already issued by CPLHTTPFetch() is         */
     529             :     /*      sufficient.                                                     */
     530             :     /* -------------------------------------------------------------------- */
     531         120 :     if (CPLGetLastErrorNo() != 0)
     532             :     {
     533           0 :         CPLHTTPDestroyResult(psResult);
     534           0 :         return TRUE;
     535             :     }
     536             : 
     537         120 :     return false;
     538             : }
     539             : 
     540             : /************************************************************************/
     541             : /*                       EstablishRasterDetails()                       */
     542             : /*                                                                      */
     543             : /*      Do a "test" coverage query to work out the number of bands,     */
     544             : /*      and pixel data type of the remote coverage.                     */
     545             : /************************************************************************/
     546             : 
     547          72 : int WCSDataset::EstablishRasterDetails()
     548             : 
     549             : {
     550          72 :     CPLXMLNode *psCO = CPLGetXMLNode(psService, "CoverageOffering");
     551             : 
     552             :     const char *pszCols =
     553          72 :         CPLGetXMLValue(psCO, "dimensionLimit.columns", nullptr);
     554          72 :     const char *pszRows = CPLGetXMLValue(psCO, "dimensionLimit.rows", nullptr);
     555          72 :     if (pszCols && pszRows)
     556             :     {
     557           0 :         nMaxCols = atoi(pszCols);
     558           0 :         nMaxRows = atoi(pszRows);
     559           0 :         SetMetadataItem("MAXNCOLS", pszCols, "IMAGE_STRUCTURE");
     560           0 :         SetMetadataItem("MAXNROWS", pszRows, "IMAGE_STRUCTURE");
     561             :     }
     562             : 
     563             :     /* -------------------------------------------------------------------- */
     564             :     /*      Do we already have bandcount and pixel type settings?           */
     565             :     /* -------------------------------------------------------------------- */
     566         133 :     if (CPLGetXMLValue(psService, "BandCount", nullptr) != nullptr &&
     567          61 :         CPLGetXMLValue(psService, "BandType", nullptr) != nullptr)
     568          48 :         return TRUE;
     569             : 
     570             :     /* -------------------------------------------------------------------- */
     571             :     /*      Fetch a small block of raster data.                             */
     572             :     /* -------------------------------------------------------------------- */
     573          24 :     CPLHTTPResult *psResult = nullptr;
     574             :     CPLErr eErr;
     575             : 
     576          24 :     eErr = GetCoverage(0, 0, 2, 2, 2, 2, 0, nullptr, nullptr, &psResult);
     577          24 :     if (eErr != CE_None)
     578           0 :         return false;
     579             : 
     580             :     /* -------------------------------------------------------------------- */
     581             :     /*      Try and open result as a dataset.                               */
     582             :     /* -------------------------------------------------------------------- */
     583          24 :     GDALDataset *poDS = GDALOpenResult(psResult);
     584             : 
     585          24 :     if (poDS == nullptr)
     586           0 :         return false;
     587             : 
     588          24 :     const auto poSRS = poDS->GetSpatialRef();
     589          24 :     m_oSRS.Clear();
     590          24 :     if (poSRS)
     591          24 :         m_oSRS = *poSRS;
     592             : 
     593             :     /* -------------------------------------------------------------------- */
     594             :     /*      Record details.                                                 */
     595             :     /* -------------------------------------------------------------------- */
     596          24 :     if (poDS->GetRasterCount() < 1)
     597             :     {
     598           0 :         delete poDS;
     599           0 :         return false;
     600             :     }
     601             : 
     602          24 :     if (CPLGetXMLValue(psService, "BandCount", nullptr) == nullptr)
     603          11 :         CPLCreateXMLElementAndValue(
     604             :             psService, "BandCount",
     605          22 :             CPLString().Printf("%d", poDS->GetRasterCount()));
     606             : 
     607          24 :     CPLCreateXMLElementAndValue(
     608             :         psService, "BandType",
     609             :         GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()));
     610             : 
     611          24 :     bServiceDirty = true;
     612             : 
     613             :     /* -------------------------------------------------------------------- */
     614             :     /*      Cleanup                                                         */
     615             :     /* -------------------------------------------------------------------- */
     616          24 :     delete poDS;
     617             : 
     618          24 :     FlushMemoryResult();
     619             : 
     620          24 :     return TRUE;
     621             : }
     622             : 
     623             : /************************************************************************/
     624             : /*                         FlushMemoryResult()                          */
     625             : /*                                                                      */
     626             : /*      This actually either cleans up the in memory /vsimem/           */
     627             : /*      temporary file, or the on disk temporary file.                  */
     628             : /************************************************************************/
     629         265 : void WCSDataset::FlushMemoryResult()
     630             : 
     631             : {
     632         265 :     if (!osResultFilename.empty())
     633             :     {
     634          72 :         VSIUnlink(osResultFilename.c_str());
     635          72 :         osResultFilename = "";
     636             :     }
     637             : 
     638         265 :     if (pabySavedDataBuffer)
     639             :     {
     640          72 :         CPLFree(pabySavedDataBuffer);
     641          72 :         pabySavedDataBuffer = nullptr;
     642             :     }
     643         265 : }
     644             : 
     645             : /************************************************************************/
     646             : /*                           GDALOpenResult()                           */
     647             : /*                                                                      */
     648             : /*      Open a CPLHTTPResult as a GDALDataset (if possible).  First     */
     649             : /*      attempt is to open handle it "in memory".  Eventually we        */
     650             : /*      will add support for handling it on file if necessary.          */
     651             : /*                                                                      */
     652             : /*      This method will free CPLHTTPResult, the caller should not      */
     653             : /*      access it after the call.                                       */
     654             : /************************************************************************/
     655             : 
     656          72 : GDALDataset *WCSDataset::GDALOpenResult(CPLHTTPResult *psResult)
     657             : 
     658             : {
     659          72 :     FlushMemoryResult();
     660             : 
     661          72 :     CPLDebug("WCS", "GDALOpenResult() on content-type: %s",
     662             :              psResult->pszContentType);
     663             : 
     664             :     /* -------------------------------------------------------------------- */
     665             :     /*      If this is multipart/related content type, we should search     */
     666             :     /*      for the second part.                                            */
     667             :     /* -------------------------------------------------------------------- */
     668          72 :     GByte *pabyData = psResult->pabyData;
     669          72 :     int nDataLen = psResult->nDataLen;
     670             : 
     671         216 :     if (psResult->pszContentType &&
     672          72 :         strstr(psResult->pszContentType, "multipart") &&
     673           0 :         CPLHTTPParseMultipartMime(psResult))
     674             :     {
     675           0 :         if (psResult->nMimePartCount > 1)
     676             :         {
     677           0 :             pabyData = psResult->pasMimePart[1].pabyData;
     678           0 :             nDataLen = psResult->pasMimePart[1].nDataLen;
     679             : 
     680             :             const char *pszContentTransferEncoding =
     681           0 :                 CSLFetchNameValue(psResult->pasMimePart[1].papszHeaders,
     682             :                                   "Content-Transfer-Encoding");
     683           0 :             if (pszContentTransferEncoding &&
     684           0 :                 EQUAL(pszContentTransferEncoding, "base64"))
     685             :             {
     686           0 :                 nDataLen = CPLBase64DecodeInPlace(pabyData);
     687             :             }
     688             :         }
     689             :     }
     690             : 
     691             : /* -------------------------------------------------------------------- */
     692             : /*      Create a memory file from the result.                           */
     693             : /* -------------------------------------------------------------------- */
     694             : #ifdef DEBUG_WCS
     695             :     // this facility is used by requests.pl to generate files for the test
     696             :     // server
     697             :     std::string xfn = CPLGetXMLValue(psService, "filename", "");
     698             :     if (xfn != "")
     699             :     {
     700             :         VSILFILE *fpTemp = VSIFOpenL(xfn, "wb");
     701             :         VSIFWriteL(pabyData, nDataLen, 1, fpTemp);
     702             :         VSIFCloseL(fpTemp);
     703             :     }
     704             : #endif
     705             :     // Eventually we should be looking at mime info and stuff to figure
     706             :     // out an optimal filename, but for now we just use a fixed one.
     707          72 :     osResultFilename = VSIMemGenerateHiddenFilename("wcsresult.dat");
     708             : 
     709          72 :     VSILFILE *fp = VSIFileFromMemBuffer(osResultFilename.c_str(), pabyData,
     710             :                                         nDataLen, FALSE);
     711             : 
     712          72 :     if (fp == nullptr)
     713             :     {
     714           0 :         CPLHTTPDestroyResult(psResult);
     715           0 :         return nullptr;
     716             :     }
     717             : 
     718          72 :     VSIFCloseL(fp);
     719             : 
     720             :     /* -------------------------------------------------------------------- */
     721             :     /*      Try opening this result as a gdaldataset.                       */
     722             :     /* -------------------------------------------------------------------- */
     723             :     GDALDataset *poDS =
     724          72 :         (GDALDataset *)GDALOpen(osResultFilename.c_str(), GA_ReadOnly);
     725             : 
     726             :     /* -------------------------------------------------------------------- */
     727             :     /*      If opening it in memory didn't work, perhaps we need to         */
     728             :     /*      write to a temp file on disk?                                   */
     729             :     /* -------------------------------------------------------------------- */
     730          72 :     if (poDS == nullptr)
     731             :     {
     732             :         std::string osTempFilename =
     733           0 :             CPLString().Printf("/tmp/%p_wcs.dat", this);
     734           0 :         VSILFILE *fpTemp = VSIFOpenL(osTempFilename.c_str(), "wb");
     735           0 :         if (fpTemp == nullptr)
     736             :         {
     737           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     738             :                      "Failed to create temporary file:%s",
     739             :                      osTempFilename.c_str());
     740             :         }
     741             :         else
     742             :         {
     743           0 :             if (VSIFWriteL(pabyData, nDataLen, 1, fpTemp) != 1)
     744             :             {
     745           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     746             :                          "Failed to write temporary file:%s",
     747             :                          osTempFilename.c_str());
     748           0 :                 VSIFCloseL(fpTemp);
     749           0 :                 VSIUnlink(osTempFilename.c_str());
     750             :             }
     751             :             else
     752             :             {
     753           0 :                 VSIFCloseL(fpTemp);
     754           0 :                 VSIUnlink(osResultFilename.c_str());
     755           0 :                 osResultFilename = std::move(osTempFilename);
     756             : 
     757             :                 poDS =
     758           0 :                     GDALDataset::Open(osResultFilename.c_str(),
     759             :                                       GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR);
     760             :             }
     761             :         }
     762             :     }
     763             : 
     764             :     /* -------------------------------------------------------------------- */
     765             :     /*      Steal the memory buffer from HTTP result.                       */
     766             :     /* -------------------------------------------------------------------- */
     767          72 :     pabySavedDataBuffer = psResult->pabyData;
     768             : 
     769          72 :     psResult->pabyData = nullptr;
     770             : 
     771          72 :     if (poDS == nullptr)
     772           0 :         FlushMemoryResult();
     773             : 
     774          72 :     CPLHTTPDestroyResult(psResult);
     775             : 
     776          72 :     return poDS;
     777             : }
     778             : 
     779             : /************************************************************************/
     780             : /*                            WCSParseVersion()                         */
     781             : /************************************************************************/
     782             : 
     783         217 : static int WCSParseVersion(const char *version)
     784             : {
     785         217 :     if (EQUAL(version, "2.0.1"))
     786          63 :         return 201;
     787         154 :     if (EQUAL(version, "1.1.2"))
     788          18 :         return 112;
     789         136 :     if (EQUAL(version, "1.1.1"))
     790          51 :         return 111;
     791          85 :     if (EQUAL(version, "1.1.0"))
     792          39 :         return 110;
     793          46 :     if (EQUAL(version, "1.0.0"))
     794          46 :         return 100;
     795           0 :     return 0;
     796             : }
     797             : 
     798             : /************************************************************************/
     799             : /*                             Version()                                */
     800             : /************************************************************************/
     801             : 
     802         147 : const char *WCSDataset::Version() const
     803             : {
     804         147 :     if (this->m_Version == 201)
     805          67 :         return "2.0.1";
     806          80 :     if (this->m_Version == 112)
     807          11 :         return "1.1.2";
     808          69 :     if (this->m_Version == 111)
     809          35 :         return "1.1.1";
     810          34 :     if (this->m_Version == 110)
     811          11 :         return "1.1.0";
     812          23 :     if (this->m_Version == 100)
     813          23 :         return "1.0.0";
     814           0 :     return "";
     815             : }
     816             : 
     817             : /************************************************************************/
     818             : /*                      FetchCapabilities()                             */
     819             : /************************************************************************/
     820             : 
     821             : #define WCS_HTTP_OPTIONS "TIMEOUT", "USERPWD", "HTTPAUTH"
     822             : 
     823          24 : static bool FetchCapabilities(GDALOpenInfo *poOpenInfo,
     824             :                               const std::string &urlIn, const std::string &path)
     825             : {
     826          48 :     std::string url = CPLURLAddKVP(urlIn.c_str(), "SERVICE", "WCS");
     827          24 :     url = CPLURLAddKVP(url.c_str(), "REQUEST", "GetCapabilities");
     828          24 :     std::string extra = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     829          48 :                                              "GetCapabilitiesExtra", "");
     830          24 :     if (extra != "")
     831             :     {
     832           0 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     833           0 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     834             :         {
     835           0 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     836           0 :             url = CPLURLAddKVP(url.c_str(), pair[0].c_str(), pair[1].c_str());
     837             :         }
     838             :     }
     839          24 :     char **options = nullptr;
     840          24 :     const char *keys[] = {WCS_HTTP_OPTIONS};
     841          96 :     for (unsigned int i = 0; i < CPL_ARRAYSIZE(keys); i++)
     842             :     {
     843             :         std::string value =
     844         144 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, keys[i], "");
     845          72 :         if (value != "")
     846             :         {
     847           0 :             options = CSLSetNameValue(options, keys[i], value.c_str());
     848             :         }
     849             :     }
     850          24 :     CPLHTTPResult *psResult = CPLHTTPFetch(url.c_str(), options);
     851          24 :     CSLDestroy(options);
     852          24 :     if (ProcessError(psResult))
     853             :     {
     854           0 :         return false;
     855             :     }
     856          48 :     CPLXMLTreeCloser doc(CPLParseXMLString((const char *)psResult->pabyData));
     857          24 :     CPLHTTPDestroyResult(psResult);
     858          24 :     if (doc.get() == nullptr)
     859             :     {
     860           0 :         return false;
     861             :     }
     862          24 :     CPLXMLNode *capabilities = doc.get();
     863          24 :     CPLSerializeXMLTreeToFile(capabilities, path.c_str());
     864          24 :     return true;
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                      CreateFromCapabilities()                        */
     869             : /************************************************************************/
     870             : 
     871          24 : WCSDataset *WCSDataset::CreateFromCapabilities(const std::string &cache,
     872             :                                                const std::string &path,
     873             :                                                const std::string &url)
     874             : {
     875          48 :     CPLXMLTreeCloser doc(CPLParseXMLFile(path.c_str()));
     876          24 :     if (doc.get() == nullptr)
     877             :     {
     878           0 :         return nullptr;
     879             :     }
     880          24 :     CPLXMLNode *capabilities = doc.getDocumentElement();
     881          24 :     if (capabilities == nullptr)
     882             :     {
     883           0 :         return nullptr;
     884             :     }
     885             :     // get version, this version will overwrite the user's request
     886             :     int version_from_server =
     887          24 :         WCSParseVersion(CPLGetXMLValue(capabilities, "version", ""));
     888          24 :     if (version_from_server == 0)
     889             :     {
     890             :         // broken server, assume 1.0.0
     891           0 :         version_from_server = 100;
     892             :     }
     893             :     WCSDataset *poDS;
     894          24 :     if (version_from_server == 201)
     895             :     {
     896           7 :         poDS = new WCSDataset201(cache.c_str());
     897             :     }
     898          17 :     else if (version_from_server / 10 == 11)
     899             :     {
     900          12 :         poDS = new WCSDataset110(version_from_server, cache.c_str());
     901             :     }
     902             :     else
     903             :     {
     904           5 :         poDS = new WCSDataset100(cache.c_str());
     905             :     }
     906          24 :     if (poDS->ParseCapabilities(capabilities, url) != CE_None)
     907             :     {
     908           0 :         delete poDS;
     909           0 :         return nullptr;
     910             :     }
     911          24 :     poDS->SetDescription(RemoveExt(path).c_str());
     912          24 :     poDS->TrySaveXML();
     913          24 :     return poDS;
     914             : }
     915             : 
     916             : /************************************************************************/
     917             : /*                        CreateFromMetadata()                          */
     918             : /************************************************************************/
     919             : 
     920          24 : WCSDataset *WCSDataset::CreateFromMetadata(const std::string &cache,
     921             :                                            const std::string &path)
     922             : {
     923             :     WCSDataset *poDS;
     924          24 :     if (FileIsReadable(path))
     925             :     {
     926          24 :         CPLXMLTreeCloser doc(CPLParseXMLFile(path.c_str()));
     927          24 :         CPLXMLNode *metadata = doc.get();
     928          24 :         if (metadata == nullptr)
     929             :         {
     930           0 :             return nullptr;
     931             :         }
     932          24 :         int version_from_metadata = WCSParseVersion(CPLGetXMLValue(
     933          24 :             SearchChildWithValue(SearchChildWithValue(metadata, "domain", ""),
     934             :                                  "key", "WCS_GLOBAL#version"),
     935             :             nullptr, ""));
     936          24 :         if (version_from_metadata == 201)
     937             :         {
     938           7 :             poDS = new WCSDataset201(cache.c_str());
     939             :         }
     940          17 :         else if (version_from_metadata / 10 == 11)
     941             :         {
     942          12 :             poDS = new WCSDataset110(version_from_metadata, cache.c_str());
     943             :         }
     944           5 :         else if (version_from_metadata / 10 == 10)
     945             :         {
     946           5 :             poDS = new WCSDataset100(cache.c_str());
     947             :         }
     948             :         else
     949             :         {
     950           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     951             :                      "The metadata does not contain version. RECREATE_META?");
     952           0 :             return nullptr;
     953             :         }
     954          48 :         std::string modifiedPath = RemoveExt(RemoveExt(path));
     955          24 :         poDS->SetDescription(modifiedPath.c_str());
     956          24 :         poDS->TryLoadXML();  // todo: avoid reload
     957             :     }
     958             :     else
     959             :     {
     960             :         // obviously there was an error
     961             :         // processing the Capabilities file
     962             :         // so we show it to the user
     963           0 :         GByte *pabyOut = nullptr;
     964           0 :         std::string modifiedPath = RemoveExt(RemoveExt(path)) + ".xml";
     965           0 :         if (!VSIIngestFile(nullptr, modifiedPath.c_str(), &pabyOut, nullptr,
     966             :                            -1))
     967           0 :             return nullptr;
     968           0 :         std::string error = reinterpret_cast<char *>(pabyOut);
     969           0 :         if (error.size() > 2048)
     970             :         {
     971           0 :             error.resize(2048);
     972             :         }
     973           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error:\n%s", error.c_str());
     974           0 :         CPLFree(pabyOut);
     975           0 :         return nullptr;
     976             :     }
     977          24 :     return poDS;
     978             : }
     979             : 
     980             : /************************************************************************/
     981             : /*                        BootstrapGlobal()                             */
     982             : /************************************************************************/
     983             : 
     984          48 : static WCSDataset *BootstrapGlobal(GDALOpenInfo *poOpenInfo,
     985             :                                    const std::string &cache,
     986             :                                    const std::string &url)
     987             : {
     988             :     // do we have the capabilities file
     989          96 :     std::string filename;
     990             :     bool cached;
     991          48 :     if (SearchCache(cache, url, filename, ".xml", cached) != CE_None)
     992             :     {
     993           0 :         return nullptr;  // error in cache
     994             :     }
     995          48 :     if (!cached)
     996             :     {
     997          24 :         filename = "XXXXX";
     998          24 :         if (AddEntryToCache(cache, url, filename, ".xml") != CE_None)
     999             :         {
    1000           0 :             return nullptr;  // error in cache
    1001             :         }
    1002          24 :         if (!FetchCapabilities(poOpenInfo, url, filename))
    1003             :         {
    1004           0 :             DeleteEntryFromCache(cache, "", url);
    1005           0 :             return nullptr;
    1006             :         }
    1007          24 :         return WCSDataset::CreateFromCapabilities(cache, filename, url);
    1008             :     }
    1009          48 :     std::string metadata = RemoveExt(filename) + ".aux.xml";
    1010             :     bool recreate_meta =
    1011          24 :         CPLFetchBool(poOpenInfo->papszOpenOptions, "RECREATE_META", false);
    1012          24 :     if (FileIsReadable(metadata) && !recreate_meta)
    1013             :     {
    1014          24 :         return WCSDataset::CreateFromMetadata(cache, metadata);
    1015             :     }
    1016             :     // we have capabilities but not meta
    1017           0 :     return WCSDataset::CreateFromCapabilities(cache, filename, url);
    1018             : }
    1019             : 
    1020             : /************************************************************************/
    1021             : /*                          CreateService()                             */
    1022             : /************************************************************************/
    1023             : 
    1024          24 : static CPLXMLNode *CreateService(const std::string &base_url,
    1025             :                                  const std::string &version,
    1026             :                                  const std::string &coverage,
    1027             :                                  const std::string &parameters)
    1028             : {
    1029             :     // construct WCS_GDAL XML into psService
    1030          24 :     std::string xml = "<WCS_GDAL>";
    1031          24 :     xml += "<ServiceURL>" + base_url + "</ServiceURL>";
    1032          24 :     xml += "<Version>" + version + "</Version>";
    1033          24 :     xml += "<CoverageName>" + coverage + "</CoverageName>";
    1034          24 :     xml += "<Parameters>" + parameters + "</Parameters>";
    1035          24 :     xml += "</WCS_GDAL>";
    1036          24 :     CPLXMLNode *psService = CPLParseXMLString(xml.c_str());
    1037          48 :     return psService;
    1038             : }
    1039             : 
    1040             : /************************************************************************/
    1041             : /*                          UpdateService()                             */
    1042             : /************************************************************************/
    1043             : 
    1044             : #define WCS_SERVICE_OPTIONS                                                    \
    1045             :     "PreferredFormat", "NoDataValue", "BlockXSize", "BlockYSize",              \
    1046             :         "OverviewCount", "GetCoverageExtra", "DescribeCoverageExtra",          \
    1047             :         "Domain", "BandCount", "BandType", "DefaultTime", "CRS"
    1048             : 
    1049             : #define WCS_TWEAK_OPTIONS                                                      \
    1050             :     "OriginAtBoundary", "OuterExtents", "BufSizeAdjust", "OffsetsPositive",    \
    1051             :         "NrOffsets", "GridCRSOptional", "NoGridAxisSwap", "GridAxisLabelSwap", \
    1052             :         "SubsetAxisSwap", "UseScaleFactor", "INTERLEAVE"
    1053             : 
    1054          72 : static bool UpdateService(CPLXMLNode *service, GDALOpenInfo *poOpenInfo)
    1055             : {
    1056          72 :     bool updated = false;
    1057             :     // descriptions in frmt_wcs.html
    1058          72 :     const char *keys[] = {"Subset",
    1059             :                           "RangeSubsetting",
    1060             :                           WCS_URL_PARAMETERS,
    1061             :                           WCS_SERVICE_OPTIONS,
    1062             :                           WCS_TWEAK_OPTIONS,
    1063             :                           WCS_HTTP_OPTIONS
    1064             : #ifdef DEBUG_WCS
    1065             :                           ,
    1066             :                           "filename"
    1067             : #endif
    1068             :     };
    1069        2808 :     for (unsigned int i = 0; i < CPL_ARRAYSIZE(keys); i++)
    1070             :     {
    1071             :         const char *value;
    1072        2736 :         if (CSLFindString(poOpenInfo->papszOpenOptions, keys[i]) != -1)
    1073             :         {
    1074          23 :             value = "TRUE";
    1075             :         }
    1076             :         else
    1077             :         {
    1078        2713 :             value = CSLFetchNameValue(poOpenInfo->papszOpenOptions, keys[i]);
    1079        2713 :             if (value == nullptr)
    1080             :             {
    1081        2580 :                 continue;
    1082             :             }
    1083             :         }
    1084         156 :         updated = CPLUpdateXML(service, keys[i], value) || updated;
    1085             :     }
    1086          72 :     return updated;
    1087             : }
    1088             : 
    1089             : /************************************************************************/
    1090             : /*                          CreateFromCache()                           */
    1091             : /************************************************************************/
    1092             : 
    1093           0 : static WCSDataset *CreateFromCache(const std::string &cache)
    1094             : {
    1095           0 :     WCSDataset *ds = new WCSDataset201(cache.c_str());
    1096           0 :     if (!ds)
    1097             :     {
    1098           0 :         return nullptr;
    1099             :     }
    1100           0 :     char **metadata = nullptr;
    1101           0 :     std::vector<std::string> contents = ReadCache(cache);
    1102           0 :     std::string path = "SUBDATASET_";
    1103           0 :     unsigned int index = 1;
    1104           0 :     for (unsigned int i = 0; i < contents.size(); ++i)
    1105             :     {
    1106           0 :         std::string name = path + CPLString().Printf("%d_", index) + "NAME";
    1107           0 :         std::string value = "WCS:" + contents[i];
    1108           0 :         metadata = CSLSetNameValue(metadata, name.c_str(), value.c_str());
    1109           0 :         index += 1;
    1110             :     }
    1111           0 :     ds->SetMetadata(metadata, "SUBDATASETS");
    1112           0 :     CSLDestroy(metadata);
    1113           0 :     return ds;
    1114             : }
    1115             : 
    1116             : /************************************************************************/
    1117             : /*                              ParseURL()                              */
    1118             : /************************************************************************/
    1119             : 
    1120          96 : static void ParseURL(std::string &url, std::string &version,
    1121             :                      std::string &coverage, std::string &parameters)
    1122             : {
    1123          96 :     version = CPLURLGetValue(url.c_str(), "version");
    1124          96 :     url = URLRemoveKey(url.c_str(), "version");
    1125             :     // the default version, the aim is to have version explicitly in cache keys
    1126          96 :     if (WCSParseVersion(version.c_str()) == 0)
    1127             :     {
    1128           0 :         version = "2.0.1";
    1129             :     }
    1130          96 :     coverage = CPLURLGetValue(url.c_str(), "coverageid");  // 2.0
    1131          96 :     if (coverage == "")
    1132             :     {
    1133          96 :         coverage = CPLURLGetValue(url.c_str(), "identifiers");  // 1.1
    1134          96 :         if (coverage == "")
    1135             :         {
    1136          96 :             coverage = CPLURLGetValue(url.c_str(), "coverage");  // 1.0
    1137          96 :             url = URLRemoveKey(url.c_str(), "coverage");
    1138             :         }
    1139             :         else
    1140             :         {
    1141           0 :             url = URLRemoveKey(url.c_str(), "identifiers");
    1142             :         }
    1143             :     }
    1144             :     else
    1145             :     {
    1146           0 :         url = URLRemoveKey(url.c_str(), "coverageid");
    1147             :     }
    1148          96 :     size_t pos = url.find("?");
    1149          96 :     if (pos == std::string::npos)
    1150             :     {
    1151           0 :         url += "?";
    1152           0 :         return;
    1153             :     }
    1154          96 :     parameters = url.substr(pos + 1, std::string::npos);
    1155          96 :     url.erase(pos + 1, std::string::npos);
    1156             : }
    1157             : 
    1158             : /************************************************************************/
    1159             : /*                                Open()                                */
    1160             : /************************************************************************/
    1161             : 
    1162          97 : GDALDataset *WCSDataset::Open(GDALOpenInfo *poOpenInfo)
    1163             : 
    1164             : {
    1165          97 :     if (!WCSDriverIdentify(poOpenInfo))
    1166             :     {
    1167           0 :         return nullptr;
    1168             :     }
    1169             : 
    1170             :     std::string cache =
    1171         194 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "CACHE", "");
    1172          97 :     if (!SetupCache(cache, CPLFetchBool(poOpenInfo->papszOpenOptions,
    1173             :                                         "CLEAR_CACHE", false)))
    1174             :     {
    1175           0 :         return nullptr;
    1176             :     }
    1177          97 :     CPLXMLNode *service = nullptr;
    1178          97 :     char **papszModifiers = nullptr;
    1179             : 
    1180          97 :     if (poOpenInfo->nHeaderBytes == 0 &&
    1181          96 :         STARTS_WITH_CI((const char *)poOpenInfo->pszFilename, "WCS:"))
    1182             :     {
    1183             :         /* --------------------------------------------------------------------
    1184             :          */
    1185             :         /*      Filename is WCS:URL */
    1186             :         /* --------------------------------------------------------------------
    1187             :          */
    1188          96 :         std::string url = (const char *)(poOpenInfo->pszFilename + 4);
    1189             : 
    1190          96 :         const char *del = CSLFetchNameValue(poOpenInfo->papszOpenOptions,
    1191             :                                             "DELETE_FROM_CACHE");
    1192          96 :         if (del != nullptr)
    1193             :         {
    1194           0 :             int k = atoi(del);
    1195           0 :             std::vector<std::string> contents = ReadCache(cache);
    1196           0 :             if (k > 0 && k <= (int)contents.size())
    1197             :             {
    1198           0 :                 DeleteEntryFromCache(cache, "", contents[k - 1]);
    1199             :             }
    1200             :         }
    1201             : 
    1202          96 :         if (url == "")
    1203             :         {
    1204           0 :             return CreateFromCache(cache);
    1205             :         }
    1206             : 
    1207          96 :         if (CPLFetchBool(poOpenInfo->papszOpenOptions, "REFRESH_CACHE", false))
    1208             :         {
    1209           0 :             DeleteEntryFromCache(cache, "", url);
    1210             :         }
    1211             : 
    1212             :         // the cache:
    1213             :         // db = key=URL database
    1214             :         // key.xml = service file
    1215             :         // key.xml.aux.xml = metadata file
    1216             :         // key.xml = Capabilities response
    1217             :         // key.aux.xml = Global metadata
    1218             :         // key.DC.xml = DescribeCoverage response
    1219             : 
    1220          96 :         std::string filename;
    1221             :         bool cached;
    1222          96 :         if (SearchCache(cache, url, filename, ".xml", cached) != CE_None)
    1223             :         {
    1224           0 :             return nullptr;  // error in cache
    1225             :         }
    1226             : 
    1227          96 :         std::string full_url = url, version, coverage, parameters;
    1228          96 :         ParseURL(url, version, coverage, parameters);
    1229             : 
    1230             :         // The goal is to get the service XML and a filename for it
    1231             : 
    1232          96 :         bool updated = false;
    1233          96 :         if (cached)
    1234             :         {
    1235             :             /* --------------------------------------------------------------------
    1236             :              */
    1237             :             /*          The fast route, service file is in cache. */
    1238             :             /* --------------------------------------------------------------------
    1239             :              */
    1240          48 :             if (coverage == "")
    1241             :             {
    1242             :                 std::string url2 =
    1243           0 :                     CPLURLAddKVP(url.c_str(), "version", version.c_str());
    1244           0 :                 WCSDataset *global = BootstrapGlobal(poOpenInfo, cache, url2);
    1245           0 :                 return global;
    1246             :             }
    1247          48 :             service = CPLParseXMLFile(filename.c_str());
    1248             :         }
    1249             :         else
    1250             :         {
    1251             :             /* --------------------------------------------------------------------
    1252             :              */
    1253             :             /*          Get capabilities. */
    1254             :             /* --------------------------------------------------------------------
    1255             :              */
    1256             :             std::string url2 =
    1257          48 :                 CPLURLAddKVP(url.c_str(), "version", version.c_str());
    1258          48 :             if (parameters != "")
    1259             :             {
    1260          48 :                 url2 += "&" + parameters;
    1261             :             }
    1262          48 :             WCSDataset *global = BootstrapGlobal(poOpenInfo, cache, url2);
    1263          48 :             if (!global)
    1264             :             {
    1265           0 :                 return nullptr;
    1266             :             }
    1267          48 :             if (coverage == "")
    1268             :             {
    1269          24 :                 return global;
    1270             :             }
    1271          24 :             if (version == "")
    1272             :             {
    1273           0 :                 version = global->Version();
    1274             :             }
    1275          24 :             service = CreateService(url, version, coverage, parameters);
    1276             :             /* --------------------------------------------------------------------
    1277             :              */
    1278             :             /*          The filename for the new service file. */
    1279             :             /* --------------------------------------------------------------------
    1280             :              */
    1281          24 :             filename = "XXXXX";
    1282          24 :             if (AddEntryToCache(cache, full_url, filename, ".xml") != CE_None)
    1283             :             {
    1284           0 :                 return nullptr;  // error in cache
    1285             :             }
    1286             :             // Create basic service metadata
    1287             :             // copy global metadata (not SUBDATASETS metadata)
    1288          48 :             std::string global_base = std::string(global->GetDescription());
    1289          48 :             std::string global_meta = global_base + ".aux.xml";
    1290          48 :             std::string capabilities = global_base + ".xml";
    1291          48 :             CPLXMLTreeCloser doc(CPLParseXMLFile(global_meta.c_str()));
    1292          24 :             CPLXMLNode *metadata = doc.getDocumentElement();
    1293             :             CPLXMLNode *domain =
    1294          24 :                 SearchChildWithValue(metadata, "domain", "SUBDATASETS");
    1295          24 :             if (domain != nullptr)
    1296             :             {
    1297          24 :                 CPLRemoveXMLChild(metadata, domain);
    1298          24 :                 CPLDestroyXMLNode(domain);
    1299             :             }
    1300             :             // get metadata for this coverage from the capabilities XML
    1301          48 :             CPLXMLTreeCloser doc2(CPLParseXMLFile(capabilities.c_str()));
    1302          24 :             global->ParseCoverageCapabilities(doc2.getDocumentElement(),
    1303          24 :                                               coverage, metadata->psChild);
    1304          24 :             delete global;
    1305          24 :             std::string metadata_filename = filename + ".aux.xml";
    1306          24 :             CPLSerializeXMLTreeToFile(metadata, metadata_filename.c_str());
    1307          24 :             updated = true;
    1308             :         }
    1309          72 :         CPLFree(poOpenInfo->pszFilename);
    1310          72 :         poOpenInfo->pszFilename = CPLStrdup(filename.c_str());
    1311          72 :         updated = UpdateService(service, poOpenInfo) || updated;
    1312          72 :         if (updated || !cached)
    1313             :         {
    1314          72 :             CPLSerializeXMLTreeToFile(service, filename.c_str());
    1315          72 :         }
    1316             :     }
    1317             :     /* -------------------------------------------------------------------- */
    1318             :     /*      Is this a WCS_GDAL service description file or "in url"         */
    1319             :     /*      equivalent?                                                     */
    1320             :     /* -------------------------------------------------------------------- */
    1321           1 :     else if (poOpenInfo->nHeaderBytes == 0 &&
    1322           0 :              STARTS_WITH_CI((const char *)poOpenInfo->pszFilename,
    1323             :                             "<WCS_GDAL>"))
    1324             :     {
    1325           0 :         service = CPLParseXMLString(poOpenInfo->pszFilename);
    1326             :     }
    1327           1 :     else if (poOpenInfo->nHeaderBytes >= 10 &&
    1328           1 :              STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "<WCS_GDAL>"))
    1329             :     {
    1330           1 :         service = CPLParseXMLFile(poOpenInfo->pszFilename);
    1331             :     }
    1332             :     /* -------------------------------------------------------------------- */
    1333             :     /*      Is this apparently a subdataset?                                */
    1334             :     /* -------------------------------------------------------------------- */
    1335           0 :     else if (STARTS_WITH_CI((const char *)poOpenInfo->pszFilename,
    1336           0 :                             "WCS_SDS:") &&
    1337           0 :              poOpenInfo->nHeaderBytes == 0)
    1338             :     {
    1339             :         int iLast;
    1340             : 
    1341           0 :         papszModifiers = CSLTokenizeString2(poOpenInfo->pszFilename + 8, ",",
    1342             :                                             CSLT_HONOURSTRINGS);
    1343             : 
    1344           0 :         iLast = CSLCount(papszModifiers) - 1;
    1345           0 :         if (iLast >= 0)
    1346             :         {
    1347           0 :             service = CPLParseXMLFile(papszModifiers[iLast]);
    1348           0 :             CPLFree(papszModifiers[iLast]);
    1349           0 :             papszModifiers[iLast] = nullptr;
    1350             :         }
    1351             :     }
    1352             : 
    1353             :     /* -------------------------------------------------------------------- */
    1354             :     /*      Success so far?                                                 */
    1355             :     /* -------------------------------------------------------------------- */
    1356          73 :     if (service == nullptr)
    1357             :     {
    1358           0 :         CSLDestroy(papszModifiers);
    1359           0 :         return nullptr;
    1360             :     }
    1361             : 
    1362             :     /* -------------------------------------------------------------------- */
    1363             :     /*      Confirm the requested access is supported.                      */
    1364             :     /* -------------------------------------------------------------------- */
    1365          73 :     if (poOpenInfo->eAccess == GA_Update)
    1366             :     {
    1367           0 :         CSLDestroy(papszModifiers);
    1368           0 :         CPLDestroyXMLNode(service);
    1369           0 :         ReportUpdateNotSupportedByDriver("WCS");
    1370           0 :         return nullptr;
    1371             :     }
    1372             : 
    1373             :     /* -------------------------------------------------------------------- */
    1374             :     /*      Check for required minimum fields.                              */
    1375             :     /* -------------------------------------------------------------------- */
    1376         146 :     if (!CPLGetXMLValue(service, "ServiceURL", nullptr) ||
    1377          73 :         !CPLGetXMLValue(service, "CoverageName", nullptr))
    1378             :     {
    1379           0 :         CSLDestroy(papszModifiers);
    1380           0 :         CPLError(
    1381             :             CE_Failure, CPLE_OpenFailed,
    1382             :             "Missing one or both of ServiceURL and CoverageName elements.\n"
    1383             :             "See WCS driver documentation for details on service description "
    1384             :             "file format.");
    1385             : 
    1386           0 :         CPLDestroyXMLNode(service);
    1387           0 :         return nullptr;
    1388             :     }
    1389             : 
    1390             :     /* -------------------------------------------------------------------- */
    1391             :     /*      What version are we working with?                               */
    1392             :     /* -------------------------------------------------------------------- */
    1393          73 :     const char *pszVersion = CPLGetXMLValue(service, "Version", "1.0.0");
    1394             : 
    1395          73 :     int nVersion = WCSParseVersion(pszVersion);
    1396             : 
    1397          73 :     if (nVersion == 0)
    1398             :     {
    1399           0 :         CSLDestroy(papszModifiers);
    1400           0 :         CPLDestroyXMLNode(service);
    1401           0 :         return nullptr;
    1402             :     }
    1403             : 
    1404             :     /* -------------------------------------------------------------------- */
    1405             :     /*      Create a corresponding GDALDataset.                             */
    1406             :     /* -------------------------------------------------------------------- */
    1407             :     WCSDataset *poDS;
    1408          73 :     if (nVersion == 201)
    1409             :     {
    1410          21 :         poDS = new WCSDataset201(cache.c_str());
    1411             :     }
    1412          52 :     else if (nVersion / 10 == 11)
    1413             :     {
    1414          36 :         poDS = new WCSDataset110(nVersion, cache.c_str());
    1415             :     }
    1416             :     else
    1417             :     {
    1418          16 :         poDS = new WCSDataset100(cache.c_str());
    1419             :     }
    1420             : 
    1421          73 :     poDS->psService = service;
    1422          73 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1423          73 :     poDS->papszSDSModifiers = papszModifiers;
    1424             :     // WCS:URL => basic metadata was already made
    1425             :     // Metadata is needed in ExtractGridInfo
    1426          73 :     poDS->TryLoadXML();
    1427             : 
    1428             :     /* -------------------------------------------------------------------- */
    1429             :     /*      Capture HTTP parameters.                                        */
    1430             :     /* -------------------------------------------------------------------- */
    1431             :     const char *pszParam;
    1432             : 
    1433          73 :     poDS->papszHttpOptions =
    1434          73 :         CSLSetNameValue(poDS->papszHttpOptions, "TIMEOUT",
    1435             :                         CPLGetXMLValue(service, "Timeout", "30"));
    1436             : 
    1437          73 :     pszParam = CPLGetXMLValue(service, "HTTPAUTH", nullptr);
    1438          73 :     if (pszParam)
    1439           0 :         poDS->papszHttpOptions =
    1440           0 :             CSLSetNameValue(poDS->papszHttpOptions, "HTTPAUTH", pszParam);
    1441             : 
    1442          73 :     pszParam = CPLGetXMLValue(service, "USERPWD", nullptr);
    1443          73 :     if (pszParam)
    1444           0 :         poDS->papszHttpOptions =
    1445           0 :             CSLSetNameValue(poDS->papszHttpOptions, "USERPWD", pszParam);
    1446             : 
    1447             :     /* -------------------------------------------------------------------- */
    1448             :     /*      If we don't have the DescribeCoverage result for this           */
    1449             :     /*      coverage, fetch it now.                                         */
    1450             :     /* -------------------------------------------------------------------- */
    1451         136 :     if (CPLGetXMLNode(service, "CoverageOffering") == nullptr &&
    1452          63 :         CPLGetXMLNode(service, "CoverageDescription") == nullptr)
    1453             :     {
    1454          25 :         if (!poDS->DescribeCoverage())
    1455             :         {
    1456           1 :             delete poDS;
    1457           1 :             return nullptr;
    1458             :         }
    1459             :     }
    1460             : 
    1461             :     /* -------------------------------------------------------------------- */
    1462             :     /*      Extract coordinate system, grid size, and geotransform from     */
    1463             :     /*      the coverage description and/or service description             */
    1464             :     /*      information.                                                    */
    1465             :     /* -------------------------------------------------------------------- */
    1466          72 :     if (!poDS->ExtractGridInfo())
    1467             :     {
    1468           0 :         delete poDS;
    1469           0 :         return nullptr;
    1470             :     }
    1471             : 
    1472             :     /* -------------------------------------------------------------------- */
    1473             :     /*      Leave now or there may be a GetCoverage call.                   */
    1474             :     /*                                                                      */
    1475             :     /* -------------------------------------------------------------------- */
    1476          72 :     int nBandCount = -1;
    1477         144 :     std::string sBandCount = CPLGetXMLValue(service, "BandCount", "");
    1478          72 :     if (sBandCount != "")
    1479             :     {
    1480          61 :         nBandCount = atoi(sBandCount.c_str());
    1481             :     }
    1482          72 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions, "SKIP_GETCOVERAGE", false) ||
    1483             :         nBandCount == 0)
    1484             :     {
    1485           0 :         return poDS;
    1486             :     }
    1487             : 
    1488             :     /* -------------------------------------------------------------------- */
    1489             :     /*      Extract band count and type from a sample.                      */
    1490             :     /* -------------------------------------------------------------------- */
    1491          72 :     if (!poDS->EstablishRasterDetails())  // todo: do this only if missing info
    1492             :     {
    1493           0 :         delete poDS;
    1494           0 :         return nullptr;
    1495             :     }
    1496             : 
    1497             :     /* -------------------------------------------------------------------- */
    1498             :     /*      It is ok to not have bands. The user just needs to supply       */
    1499             :     /*      more information.                                               */
    1500             :     /* -------------------------------------------------------------------- */
    1501          72 :     nBandCount = atoi(CPLGetXMLValue(service, "BandCount", "0"));
    1502          72 :     if (nBandCount == 0)
    1503             :     {
    1504           0 :         return poDS;
    1505             :     }
    1506             : 
    1507             :     /* -------------------------------------------------------------------- */
    1508             :     /*      Create band information objects.                                */
    1509             :     /* -------------------------------------------------------------------- */
    1510             :     int iBand;
    1511             : 
    1512          72 :     if (!GDALCheckBandCount(nBandCount, FALSE))
    1513             :     {
    1514           0 :         delete poDS;
    1515           0 :         return nullptr;
    1516             :     }
    1517             : 
    1518         207 :     for (iBand = 0; iBand < nBandCount; iBand++)
    1519             :     {
    1520         135 :         WCSRasterBand *band = new WCSRasterBand(poDS, iBand + 1, -1);
    1521             :         // copy band specific metadata to the band
    1522         135 :         char **md_from = poDS->GetMetadata("");
    1523         135 :         char **md_to = nullptr;
    1524         135 :         if (md_from)
    1525             :         {
    1526         270 :             std::string our_key = CPLString().Printf("FIELD_%d_", iBand + 1);
    1527        3192 :             for (char **from = md_from; *from != nullptr; ++from)
    1528             :             {
    1529        6114 :                 std::vector<std::string> kv = Split(*from, "=");
    1530        6114 :                 if (kv.size() > 1 &&
    1531        3057 :                     STARTS_WITH(kv[0].c_str(), our_key.c_str()))
    1532             :                 {
    1533         174 :                     std::string key = kv[0];
    1534          87 :                     std::string value = kv[1];
    1535          87 :                     key.erase(0, our_key.length());
    1536          87 :                     md_to = CSLSetNameValue(md_to, key.c_str(), value.c_str());
    1537             :                 }
    1538             :             }
    1539             :         }
    1540         135 :         band->SetMetadata(md_to, "");
    1541         135 :         CSLDestroy(md_to);
    1542         135 :         poDS->SetBand(iBand + 1, band);
    1543             :     }
    1544             : 
    1545             :     /* -------------------------------------------------------------------- */
    1546             :     /*      Set time metadata on the dataset if we are selecting a          */
    1547             :     /*      temporal slice.                                                 */
    1548             :     /* -------------------------------------------------------------------- */
    1549          72 :     std::string osTime = CSLFetchNameValueDef(poDS->papszSDSModifiers, "time",
    1550          72 :                                               poDS->osDefaultTime.c_str());
    1551             : 
    1552          72 :     if (osTime != "")
    1553           0 :         poDS->GDALMajorObject::SetMetadataItem("TIME_POSITION", osTime.c_str());
    1554             : 
    1555             :     /* -------------------------------------------------------------------- */
    1556             :     /*      Do we have a band identifier to select only a subset of bands?  */
    1557             :     /* -------------------------------------------------------------------- */
    1558          72 :     poDS->osBandIdentifier = CPLGetXMLValue(service, "BandIdentifier", "");
    1559             : 
    1560             :     /* -------------------------------------------------------------------- */
    1561             :     /*      Do we have time based subdatasets?  If so, record them in       */
    1562             :     /*      metadata.  Note we don't do subdatasets if this is a            */
    1563             :     /*      subdataset or if this is an all-in-memory service.              */
    1564             :     /* -------------------------------------------------------------------- */
    1565         216 :     if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "WCS_SDS:") &&
    1566         144 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "<WCS_GDAL>") &&
    1567          72 :         !poDS->aosTimePositions.empty())
    1568             :     {
    1569           0 :         char **papszSubdatasets = nullptr;
    1570             :         int iTime;
    1571             : 
    1572           0 :         for (iTime = 0; iTime < (int)poDS->aosTimePositions.size(); iTime++)
    1573             :         {
    1574           0 :             std::string osName;
    1575           0 :             std::string osValue;
    1576             : 
    1577           0 :             osName = CPLString().Printf("SUBDATASET_%d_NAME", iTime + 1);
    1578           0 :             osValue = CPLString().Printf("WCS_SDS:time=\"%s\",%s",
    1579           0 :                                          poDS->aosTimePositions[iTime].c_str(),
    1580           0 :                                          poOpenInfo->pszFilename);
    1581           0 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets, osName.c_str(),
    1582             :                                                osValue.c_str());
    1583             : 
    1584             :             std::string osCoverage =
    1585           0 :                 CPLGetXMLValue(poDS->psService, "CoverageName", "");
    1586             : 
    1587           0 :             osName = CPLString().Printf("SUBDATASET_%d_DESC", iTime + 1);
    1588             :             osValue =
    1589           0 :                 CPLString().Printf("Coverage %s at time %s", osCoverage.c_str(),
    1590           0 :                                    poDS->aosTimePositions[iTime].c_str());
    1591           0 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets, osName.c_str(),
    1592             :                                                osValue.c_str());
    1593             :         }
    1594             : 
    1595           0 :         poDS->GDALMajorObject::SetMetadata(papszSubdatasets, "SUBDATASETS");
    1596             : 
    1597           0 :         CSLDestroy(papszSubdatasets);
    1598             :     }
    1599             : 
    1600             :     /* -------------------------------------------------------------------- */
    1601             :     /*      Initialize any PAM information.                                 */
    1602             :     /* -------------------------------------------------------------------- */
    1603          72 :     poDS->TryLoadXML();
    1604          72 :     return poDS;
    1605             : }
    1606             : 
    1607             : /************************************************************************/
    1608             : /*                          GetGeoTransform()                           */
    1609             : /************************************************************************/
    1610             : 
    1611          72 : CPLErr WCSDataset::GetGeoTransform(double *padfTransform)
    1612             : 
    1613             : {
    1614          72 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
    1615          72 :     return CE_None;
    1616             : }
    1617             : 
    1618             : /************************************************************************/
    1619             : /*                          GetSpatialRef()                             */
    1620             : /************************************************************************/
    1621             : 
    1622          48 : const OGRSpatialReference *WCSDataset::GetSpatialRef() const
    1623             : 
    1624             : {
    1625          48 :     const auto poSRS = GDALPamDataset::GetSpatialRef();
    1626          48 :     if (poSRS)
    1627           0 :         return poSRS;
    1628             : 
    1629          48 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
    1630             : }
    1631             : 
    1632             : /************************************************************************/
    1633             : /*                            GetFileList()                             */
    1634             : /************************************************************************/
    1635             : 
    1636           0 : char **WCSDataset::GetFileList()
    1637             : 
    1638             : {
    1639           0 :     char **papszFileList = GDALPamDataset::GetFileList();
    1640             : 
    1641             : /* -------------------------------------------------------------------- */
    1642             : /*      ESRI also wishes to include service urls in the file list       */
    1643             : /*      though this is not currently part of the general definition     */
    1644             : /*      of GetFileList() for GDAL.                                      */
    1645             : /* -------------------------------------------------------------------- */
    1646             : #ifdef ESRI_BUILD
    1647             :     std::string file;
    1648             :     file.Printf("%s%s", CPLGetXMLValue(psService, "ServiceURL", ""),
    1649             :                 CPLGetXMLValue(psService, "CoverageName", ""));
    1650             :     papszFileList = CSLAddString(papszFileList, file.c_str());
    1651             : #endif /* def ESRI_BUILD */
    1652             : 
    1653           0 :     return papszFileList;
    1654             : }
    1655             : 
    1656             : /************************************************************************/
    1657             : /*                      GetMetadataDomainList()                         */
    1658             : /************************************************************************/
    1659             : 
    1660           0 : char **WCSDataset::GetMetadataDomainList()
    1661             : {
    1662           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
    1663           0 :                                    TRUE, "xml:CoverageOffering", nullptr);
    1664             : }
    1665             : 
    1666             : /************************************************************************/
    1667             : /*                            GetMetadata()                             */
    1668             : /************************************************************************/
    1669             : 
    1670         348 : char **WCSDataset::GetMetadata(const char *pszDomain)
    1671             : 
    1672             : {
    1673         348 :     if (pszDomain == nullptr || !EQUAL(pszDomain, "xml:CoverageOffering"))
    1674         348 :         return GDALPamDataset::GetMetadata(pszDomain);
    1675             : 
    1676           0 :     CPLXMLNode *psNode = CPLGetXMLNode(psService, "CoverageOffering");
    1677             : 
    1678           0 :     if (psNode == nullptr)
    1679           0 :         psNode = CPLGetXMLNode(psService, "CoverageDescription");
    1680             : 
    1681           0 :     if (psNode == nullptr)
    1682           0 :         return nullptr;
    1683             : 
    1684           0 :     if (apszCoverageOfferingMD[0] == nullptr)
    1685             :     {
    1686           0 :         CPLXMLNode *psNext = psNode->psNext;
    1687           0 :         psNode->psNext = nullptr;
    1688             : 
    1689           0 :         apszCoverageOfferingMD[0] = CPLSerializeXMLTree(psNode);
    1690             : 
    1691           0 :         psNode->psNext = psNext;
    1692             :     }
    1693             : 
    1694           0 :     return apszCoverageOfferingMD;
    1695             : }
    1696             : 
    1697             : /************************************************************************/
    1698             : /*                          GDALRegister_WCS()                          */
    1699             : /************************************************************************/
    1700             : 
    1701        1889 : void GDALRegister_WCS()
    1702             : 
    1703             : {
    1704        1889 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    1705         282 :         return;
    1706             : 
    1707        1607 :     GDALDriver *poDriver = new GDALDriver();
    1708        1607 :     WCSDriverSetCommonMetadata(poDriver);
    1709             : 
    1710        1607 :     poDriver->pfnOpen = WCSDataset::Open;
    1711             : 
    1712        1607 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1713             : }

Generated by: LCOV version 1.14