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: 2024-11-21 22:18:42 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             :         const 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 = osTempFilename;
     756             : 
     757           0 :                 poDS = (GDALDataset *)GDALOpen(osResultFilename.c_str(),
     758             :                                                GA_ReadOnly);
     759             :             }
     760             :         }
     761             :     }
     762             : 
     763             :     /* -------------------------------------------------------------------- */
     764             :     /*      Steal the memory buffer from HTTP result.                       */
     765             :     /* -------------------------------------------------------------------- */
     766          72 :     pabySavedDataBuffer = psResult->pabyData;
     767             : 
     768          72 :     psResult->pabyData = nullptr;
     769             : 
     770          72 :     if (poDS == nullptr)
     771           0 :         FlushMemoryResult();
     772             : 
     773          72 :     CPLHTTPDestroyResult(psResult);
     774             : 
     775          72 :     return poDS;
     776             : }
     777             : 
     778             : /************************************************************************/
     779             : /*                            WCSParseVersion()                         */
     780             : /************************************************************************/
     781             : 
     782         217 : static int WCSParseVersion(const char *version)
     783             : {
     784         217 :     if (EQUAL(version, "2.0.1"))
     785          63 :         return 201;
     786         154 :     if (EQUAL(version, "1.1.2"))
     787          18 :         return 112;
     788         136 :     if (EQUAL(version, "1.1.1"))
     789          51 :         return 111;
     790          85 :     if (EQUAL(version, "1.1.0"))
     791          39 :         return 110;
     792          46 :     if (EQUAL(version, "1.0.0"))
     793          46 :         return 100;
     794           0 :     return 0;
     795             : }
     796             : 
     797             : /************************************************************************/
     798             : /*                             Version()                                */
     799             : /************************************************************************/
     800             : 
     801         147 : const char *WCSDataset::Version() const
     802             : {
     803         147 :     if (this->m_Version == 201)
     804          67 :         return "2.0.1";
     805          80 :     if (this->m_Version == 112)
     806          11 :         return "1.1.2";
     807          69 :     if (this->m_Version == 111)
     808          35 :         return "1.1.1";
     809          34 :     if (this->m_Version == 110)
     810          11 :         return "1.1.0";
     811          23 :     if (this->m_Version == 100)
     812          23 :         return "1.0.0";
     813           0 :     return "";
     814             : }
     815             : 
     816             : /************************************************************************/
     817             : /*                      FetchCapabilities()                             */
     818             : /************************************************************************/
     819             : 
     820             : #define WCS_HTTP_OPTIONS "TIMEOUT", "USERPWD", "HTTPAUTH"
     821             : 
     822          24 : static bool FetchCapabilities(GDALOpenInfo *poOpenInfo,
     823             :                               const std::string &urlIn, const std::string &path)
     824             : {
     825          48 :     std::string url = CPLURLAddKVP(urlIn.c_str(), "SERVICE", "WCS");
     826          24 :     url = CPLURLAddKVP(url.c_str(), "REQUEST", "GetCapabilities");
     827          24 :     std::string extra = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     828          48 :                                              "GetCapabilitiesExtra", "");
     829          24 :     if (extra != "")
     830             :     {
     831           0 :         std::vector<std::string> pairs = Split(extra.c_str(), "&");
     832           0 :         for (unsigned int i = 0; i < pairs.size(); ++i)
     833             :         {
     834           0 :             std::vector<std::string> pair = Split(pairs[i].c_str(), "=");
     835           0 :             url = CPLURLAddKVP(url.c_str(), pair[0].c_str(), pair[1].c_str());
     836             :         }
     837             :     }
     838          24 :     char **options = nullptr;
     839          24 :     const char *keys[] = {WCS_HTTP_OPTIONS};
     840          96 :     for (unsigned int i = 0; i < CPL_ARRAYSIZE(keys); i++)
     841             :     {
     842             :         std::string value =
     843         144 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, keys[i], "");
     844          72 :         if (value != "")
     845             :         {
     846           0 :             options = CSLSetNameValue(options, keys[i], value.c_str());
     847             :         }
     848             :     }
     849          24 :     CPLHTTPResult *psResult = CPLHTTPFetch(url.c_str(), options);
     850          24 :     CSLDestroy(options);
     851          24 :     if (ProcessError(psResult))
     852             :     {
     853           0 :         return false;
     854             :     }
     855          48 :     CPLXMLTreeCloser doc(CPLParseXMLString((const char *)psResult->pabyData));
     856          24 :     CPLHTTPDestroyResult(psResult);
     857          24 :     if (doc.get() == nullptr)
     858             :     {
     859           0 :         return false;
     860             :     }
     861          24 :     CPLXMLNode *capabilities = doc.get();
     862          24 :     CPLSerializeXMLTreeToFile(capabilities, path.c_str());
     863          24 :     return true;
     864             : }
     865             : 
     866             : /************************************************************************/
     867             : /*                      CreateFromCapabilities()                        */
     868             : /************************************************************************/
     869             : 
     870          24 : WCSDataset *WCSDataset::CreateFromCapabilities(const std::string &cache,
     871             :                                                const std::string &path,
     872             :                                                const std::string &url)
     873             : {
     874          48 :     CPLXMLTreeCloser doc(CPLParseXMLFile(path.c_str()));
     875          24 :     if (doc.get() == nullptr)
     876             :     {
     877           0 :         return nullptr;
     878             :     }
     879          24 :     CPLXMLNode *capabilities = doc.getDocumentElement();
     880          24 :     if (capabilities == nullptr)
     881             :     {
     882           0 :         return nullptr;
     883             :     }
     884             :     // get version, this version will overwrite the user's request
     885             :     int version_from_server =
     886          24 :         WCSParseVersion(CPLGetXMLValue(capabilities, "version", ""));
     887          24 :     if (version_from_server == 0)
     888             :     {
     889             :         // broken server, assume 1.0.0
     890           0 :         version_from_server = 100;
     891             :     }
     892             :     WCSDataset *poDS;
     893          24 :     if (version_from_server == 201)
     894             :     {
     895           7 :         poDS = new WCSDataset201(cache.c_str());
     896             :     }
     897          17 :     else if (version_from_server / 10 == 11)
     898             :     {
     899          12 :         poDS = new WCSDataset110(version_from_server, cache.c_str());
     900             :     }
     901             :     else
     902             :     {
     903           5 :         poDS = new WCSDataset100(cache.c_str());
     904             :     }
     905          24 :     if (poDS->ParseCapabilities(capabilities, url) != CE_None)
     906             :     {
     907           0 :         delete poDS;
     908           0 :         return nullptr;
     909             :     }
     910          24 :     poDS->SetDescription(RemoveExt(path).c_str());
     911          24 :     poDS->TrySaveXML();
     912          24 :     return poDS;
     913             : }
     914             : 
     915             : /************************************************************************/
     916             : /*                        CreateFromMetadata()                          */
     917             : /************************************************************************/
     918             : 
     919          24 : WCSDataset *WCSDataset::CreateFromMetadata(const std::string &cache,
     920             :                                            const std::string &path)
     921             : {
     922             :     WCSDataset *poDS;
     923          24 :     if (FileIsReadable(path))
     924             :     {
     925          24 :         CPLXMLTreeCloser doc(CPLParseXMLFile(path.c_str()));
     926          24 :         CPLXMLNode *metadata = doc.get();
     927          24 :         if (metadata == nullptr)
     928             :         {
     929           0 :             return nullptr;
     930             :         }
     931          24 :         int version_from_metadata = WCSParseVersion(CPLGetXMLValue(
     932          24 :             SearchChildWithValue(SearchChildWithValue(metadata, "domain", ""),
     933             :                                  "key", "WCS_GLOBAL#version"),
     934             :             nullptr, ""));
     935          24 :         if (version_from_metadata == 201)
     936             :         {
     937           7 :             poDS = new WCSDataset201(cache.c_str());
     938             :         }
     939          17 :         else if (version_from_metadata / 10 == 11)
     940             :         {
     941          12 :             poDS = new WCSDataset110(version_from_metadata, cache.c_str());
     942             :         }
     943           5 :         else if (version_from_metadata / 10 == 10)
     944             :         {
     945           5 :             poDS = new WCSDataset100(cache.c_str());
     946             :         }
     947             :         else
     948             :         {
     949           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     950             :                      "The metadata does not contain version. RECREATE_META?");
     951           0 :             return nullptr;
     952             :         }
     953          48 :         std::string modifiedPath = RemoveExt(RemoveExt(path));
     954          24 :         poDS->SetDescription(modifiedPath.c_str());
     955          24 :         poDS->TryLoadXML();  // todo: avoid reload
     956             :     }
     957             :     else
     958             :     {
     959             :         // obviously there was an error
     960             :         // processing the Capabilities file
     961             :         // so we show it to the user
     962           0 :         GByte *pabyOut = nullptr;
     963           0 :         std::string modifiedPath = RemoveExt(RemoveExt(path)) + ".xml";
     964           0 :         if (!VSIIngestFile(nullptr, modifiedPath.c_str(), &pabyOut, nullptr,
     965             :                            -1))
     966           0 :             return nullptr;
     967           0 :         std::string error = reinterpret_cast<char *>(pabyOut);
     968           0 :         if (error.size() > 2048)
     969             :         {
     970           0 :             error.resize(2048);
     971             :         }
     972           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error:\n%s", error.c_str());
     973           0 :         CPLFree(pabyOut);
     974           0 :         return nullptr;
     975             :     }
     976          24 :     return poDS;
     977             : }
     978             : 
     979             : /************************************************************************/
     980             : /*                        BootstrapGlobal()                             */
     981             : /************************************************************************/
     982             : 
     983          48 : static WCSDataset *BootstrapGlobal(GDALOpenInfo *poOpenInfo,
     984             :                                    const std::string &cache,
     985             :                                    const std::string &url)
     986             : {
     987             :     // do we have the capabilities file
     988          96 :     std::string filename;
     989             :     bool cached;
     990          48 :     if (SearchCache(cache, url, filename, ".xml", cached) != CE_None)
     991             :     {
     992           0 :         return nullptr;  // error in cache
     993             :     }
     994          48 :     if (!cached)
     995             :     {
     996          24 :         filename = "XXXXX";
     997          24 :         if (AddEntryToCache(cache, url, filename, ".xml") != CE_None)
     998             :         {
     999           0 :             return nullptr;  // error in cache
    1000             :         }
    1001          24 :         if (!FetchCapabilities(poOpenInfo, url, filename))
    1002             :         {
    1003           0 :             DeleteEntryFromCache(cache, "", url);
    1004           0 :             return nullptr;
    1005             :         }
    1006          24 :         return WCSDataset::CreateFromCapabilities(cache, filename, url);
    1007             :     }
    1008          48 :     std::string metadata = RemoveExt(filename) + ".aux.xml";
    1009             :     bool recreate_meta =
    1010          24 :         CPLFetchBool(poOpenInfo->papszOpenOptions, "RECREATE_META", false);
    1011          24 :     if (FileIsReadable(metadata) && !recreate_meta)
    1012             :     {
    1013          24 :         return WCSDataset::CreateFromMetadata(cache, metadata);
    1014             :     }
    1015             :     // we have capabilities but not meta
    1016           0 :     return WCSDataset::CreateFromCapabilities(cache, filename, url);
    1017             : }
    1018             : 
    1019             : /************************************************************************/
    1020             : /*                          CreateService()                             */
    1021             : /************************************************************************/
    1022             : 
    1023          24 : static CPLXMLNode *CreateService(const std::string &base_url,
    1024             :                                  const std::string &version,
    1025             :                                  const std::string &coverage,
    1026             :                                  const std::string &parameters)
    1027             : {
    1028             :     // construct WCS_GDAL XML into psService
    1029          24 :     std::string xml = "<WCS_GDAL>";
    1030          24 :     xml += "<ServiceURL>" + base_url + "</ServiceURL>";
    1031          24 :     xml += "<Version>" + version + "</Version>";
    1032          24 :     xml += "<CoverageName>" + coverage + "</CoverageName>";
    1033          24 :     xml += "<Parameters>" + parameters + "</Parameters>";
    1034          24 :     xml += "</WCS_GDAL>";
    1035          24 :     CPLXMLNode *psService = CPLParseXMLString(xml.c_str());
    1036          48 :     return psService;
    1037             : }
    1038             : 
    1039             : /************************************************************************/
    1040             : /*                          UpdateService()                             */
    1041             : /************************************************************************/
    1042             : 
    1043             : #define WCS_SERVICE_OPTIONS                                                    \
    1044             :     "PreferredFormat", "NoDataValue", "BlockXSize", "BlockYSize",              \
    1045             :         "OverviewCount", "GetCoverageExtra", "DescribeCoverageExtra",          \
    1046             :         "Domain", "BandCount", "BandType", "DefaultTime", "CRS"
    1047             : 
    1048             : #define WCS_TWEAK_OPTIONS                                                      \
    1049             :     "OriginAtBoundary", "OuterExtents", "BufSizeAdjust", "OffsetsPositive",    \
    1050             :         "NrOffsets", "GridCRSOptional", "NoGridAxisSwap", "GridAxisLabelSwap", \
    1051             :         "SubsetAxisSwap", "UseScaleFactor", "INTERLEAVE"
    1052             : 
    1053          72 : static bool UpdateService(CPLXMLNode *service, GDALOpenInfo *poOpenInfo)
    1054             : {
    1055          72 :     bool updated = false;
    1056             :     // descriptions in frmt_wcs.html
    1057          72 :     const char *keys[] = {"Subset",
    1058             :                           "RangeSubsetting",
    1059             :                           WCS_URL_PARAMETERS,
    1060             :                           WCS_SERVICE_OPTIONS,
    1061             :                           WCS_TWEAK_OPTIONS,
    1062             :                           WCS_HTTP_OPTIONS
    1063             : #ifdef DEBUG_WCS
    1064             :                           ,
    1065             :                           "filename"
    1066             : #endif
    1067             :     };
    1068        2808 :     for (unsigned int i = 0; i < CPL_ARRAYSIZE(keys); i++)
    1069             :     {
    1070             :         const char *value;
    1071        2736 :         if (CSLFindString(poOpenInfo->papszOpenOptions, keys[i]) != -1)
    1072             :         {
    1073          23 :             value = "TRUE";
    1074             :         }
    1075             :         else
    1076             :         {
    1077        2713 :             value = CSLFetchNameValue(poOpenInfo->papszOpenOptions, keys[i]);
    1078        2713 :             if (value == nullptr)
    1079             :             {
    1080        2580 :                 continue;
    1081             :             }
    1082             :         }
    1083         156 :         updated = CPLUpdateXML(service, keys[i], value) || updated;
    1084             :     }
    1085          72 :     return updated;
    1086             : }
    1087             : 
    1088             : /************************************************************************/
    1089             : /*                          CreateFromCache()                           */
    1090             : /************************************************************************/
    1091             : 
    1092           0 : static WCSDataset *CreateFromCache(const std::string &cache)
    1093             : {
    1094           0 :     WCSDataset *ds = new WCSDataset201(cache.c_str());
    1095           0 :     if (!ds)
    1096             :     {
    1097           0 :         return nullptr;
    1098             :     }
    1099           0 :     char **metadata = nullptr;
    1100           0 :     std::vector<std::string> contents = ReadCache(cache);
    1101           0 :     std::string path = "SUBDATASET_";
    1102           0 :     unsigned int index = 1;
    1103           0 :     for (unsigned int i = 0; i < contents.size(); ++i)
    1104             :     {
    1105           0 :         std::string name = path + CPLString().Printf("%d_", index) + "NAME";
    1106           0 :         std::string value = "WCS:" + contents[i];
    1107           0 :         metadata = CSLSetNameValue(metadata, name.c_str(), value.c_str());
    1108           0 :         index += 1;
    1109             :     }
    1110           0 :     ds->SetMetadata(metadata, "SUBDATASETS");
    1111           0 :     CSLDestroy(metadata);
    1112           0 :     return ds;
    1113             : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                              ParseURL()                              */
    1117             : /************************************************************************/
    1118             : 
    1119          96 : static void ParseURL(std::string &url, std::string &version,
    1120             :                      std::string &coverage, std::string &parameters)
    1121             : {
    1122          96 :     version = CPLURLGetValue(url.c_str(), "version");
    1123          96 :     url = URLRemoveKey(url.c_str(), "version");
    1124             :     // the default version, the aim is to have version explicitly in cache keys
    1125          96 :     if (WCSParseVersion(version.c_str()) == 0)
    1126             :     {
    1127           0 :         version = "2.0.1";
    1128             :     }
    1129          96 :     coverage = CPLURLGetValue(url.c_str(), "coverageid");  // 2.0
    1130          96 :     if (coverage == "")
    1131             :     {
    1132          96 :         coverage = CPLURLGetValue(url.c_str(), "identifiers");  // 1.1
    1133          96 :         if (coverage == "")
    1134             :         {
    1135          96 :             coverage = CPLURLGetValue(url.c_str(), "coverage");  // 1.0
    1136          96 :             url = URLRemoveKey(url.c_str(), "coverage");
    1137             :         }
    1138             :         else
    1139             :         {
    1140           0 :             url = URLRemoveKey(url.c_str(), "identifiers");
    1141             :         }
    1142             :     }
    1143             :     else
    1144             :     {
    1145           0 :         url = URLRemoveKey(url.c_str(), "coverageid");
    1146             :     }
    1147          96 :     size_t pos = url.find("?");
    1148          96 :     if (pos == std::string::npos)
    1149             :     {
    1150           0 :         url += "?";
    1151           0 :         return;
    1152             :     }
    1153          96 :     parameters = url.substr(pos + 1, std::string::npos);
    1154          96 :     url.erase(pos + 1, std::string::npos);
    1155             : }
    1156             : 
    1157             : /************************************************************************/
    1158             : /*                                Open()                                */
    1159             : /************************************************************************/
    1160             : 
    1161          97 : GDALDataset *WCSDataset::Open(GDALOpenInfo *poOpenInfo)
    1162             : 
    1163             : {
    1164          97 :     if (!WCSDriverIdentify(poOpenInfo))
    1165             :     {
    1166           0 :         return nullptr;
    1167             :     }
    1168             : 
    1169             :     std::string cache =
    1170         194 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "CACHE", "");
    1171          97 :     if (!SetupCache(cache, CPLFetchBool(poOpenInfo->papszOpenOptions,
    1172             :                                         "CLEAR_CACHE", false)))
    1173             :     {
    1174           0 :         return nullptr;
    1175             :     }
    1176          97 :     CPLXMLNode *service = nullptr;
    1177          97 :     char **papszModifiers = nullptr;
    1178             : 
    1179          97 :     if (poOpenInfo->nHeaderBytes == 0 &&
    1180          96 :         STARTS_WITH_CI((const char *)poOpenInfo->pszFilename, "WCS:"))
    1181             :     {
    1182             :         /* --------------------------------------------------------------------
    1183             :          */
    1184             :         /*      Filename is WCS:URL */
    1185             :         /* --------------------------------------------------------------------
    1186             :          */
    1187          96 :         std::string url = (const char *)(poOpenInfo->pszFilename + 4);
    1188             : 
    1189          96 :         const char *del = CSLFetchNameValue(poOpenInfo->papszOpenOptions,
    1190             :                                             "DELETE_FROM_CACHE");
    1191          96 :         if (del != nullptr)
    1192             :         {
    1193           0 :             int k = atoi(del);
    1194           0 :             std::vector<std::string> contents = ReadCache(cache);
    1195           0 :             if (k > 0 && k <= (int)contents.size())
    1196             :             {
    1197           0 :                 DeleteEntryFromCache(cache, "", contents[k - 1]);
    1198             :             }
    1199             :         }
    1200             : 
    1201          96 :         if (url == "")
    1202             :         {
    1203           0 :             return CreateFromCache(cache);
    1204             :         }
    1205             : 
    1206          96 :         if (CPLFetchBool(poOpenInfo->papszOpenOptions, "REFRESH_CACHE", false))
    1207             :         {
    1208           0 :             DeleteEntryFromCache(cache, "", url);
    1209             :         }
    1210             : 
    1211             :         // the cache:
    1212             :         // db = key=URL database
    1213             :         // key.xml = service file
    1214             :         // key.xml.aux.xml = metadata file
    1215             :         // key.xml = Capabilities response
    1216             :         // key.aux.xml = Global metadata
    1217             :         // key.DC.xml = DescribeCoverage response
    1218             : 
    1219          96 :         std::string filename;
    1220             :         bool cached;
    1221          96 :         if (SearchCache(cache, url, filename, ".xml", cached) != CE_None)
    1222             :         {
    1223           0 :             return nullptr;  // error in cache
    1224             :         }
    1225             : 
    1226          96 :         std::string full_url = url, version, coverage, parameters;
    1227          96 :         ParseURL(url, version, coverage, parameters);
    1228             : 
    1229             :         // The goal is to get the service XML and a filename for it
    1230             : 
    1231          96 :         bool updated = false;
    1232          96 :         if (cached)
    1233             :         {
    1234             :             /* --------------------------------------------------------------------
    1235             :              */
    1236             :             /*          The fast route, service file is in cache. */
    1237             :             /* --------------------------------------------------------------------
    1238             :              */
    1239          48 :             if (coverage == "")
    1240             :             {
    1241             :                 std::string url2 =
    1242           0 :                     CPLURLAddKVP(url.c_str(), "version", version.c_str());
    1243           0 :                 WCSDataset *global = BootstrapGlobal(poOpenInfo, cache, url2);
    1244           0 :                 return global;
    1245             :             }
    1246          48 :             service = CPLParseXMLFile(filename.c_str());
    1247             :         }
    1248             :         else
    1249             :         {
    1250             :             /* --------------------------------------------------------------------
    1251             :              */
    1252             :             /*          Get capabilities. */
    1253             :             /* --------------------------------------------------------------------
    1254             :              */
    1255             :             std::string url2 =
    1256          48 :                 CPLURLAddKVP(url.c_str(), "version", version.c_str());
    1257          48 :             if (parameters != "")
    1258             :             {
    1259          48 :                 url2 += "&" + parameters;
    1260             :             }
    1261          48 :             WCSDataset *global = BootstrapGlobal(poOpenInfo, cache, url2);
    1262          48 :             if (!global)
    1263             :             {
    1264           0 :                 return nullptr;
    1265             :             }
    1266          48 :             if (coverage == "")
    1267             :             {
    1268          24 :                 return global;
    1269             :             }
    1270          24 :             if (version == "")
    1271             :             {
    1272           0 :                 version = global->Version();
    1273             :             }
    1274          24 :             service = CreateService(url, version, coverage, parameters);
    1275             :             /* --------------------------------------------------------------------
    1276             :              */
    1277             :             /*          The filename for the new service file. */
    1278             :             /* --------------------------------------------------------------------
    1279             :              */
    1280          24 :             filename = "XXXXX";
    1281          24 :             if (AddEntryToCache(cache, full_url, filename, ".xml") != CE_None)
    1282             :             {
    1283           0 :                 return nullptr;  // error in cache
    1284             :             }
    1285             :             // Create basic service metadata
    1286             :             // copy global metadata (not SUBDATASETS metadata)
    1287          48 :             std::string global_base = std::string(global->GetDescription());
    1288          48 :             std::string global_meta = global_base + ".aux.xml";
    1289          48 :             std::string capabilities = global_base + ".xml";
    1290          48 :             CPLXMLTreeCloser doc(CPLParseXMLFile(global_meta.c_str()));
    1291          24 :             CPLXMLNode *metadata = doc.getDocumentElement();
    1292             :             CPLXMLNode *domain =
    1293          24 :                 SearchChildWithValue(metadata, "domain", "SUBDATASETS");
    1294          24 :             if (domain != nullptr)
    1295             :             {
    1296          24 :                 CPLRemoveXMLChild(metadata, domain);
    1297          24 :                 CPLDestroyXMLNode(domain);
    1298             :             }
    1299             :             // get metadata for this coverage from the capabilities XML
    1300          48 :             CPLXMLTreeCloser doc2(CPLParseXMLFile(capabilities.c_str()));
    1301          24 :             global->ParseCoverageCapabilities(doc2.getDocumentElement(),
    1302          24 :                                               coverage, metadata->psChild);
    1303          24 :             delete global;
    1304          24 :             std::string metadata_filename = filename + ".aux.xml";
    1305          24 :             CPLSerializeXMLTreeToFile(metadata, metadata_filename.c_str());
    1306          24 :             updated = true;
    1307             :         }
    1308          72 :         CPLFree(poOpenInfo->pszFilename);
    1309          72 :         poOpenInfo->pszFilename = CPLStrdup(filename.c_str());
    1310          72 :         updated = UpdateService(service, poOpenInfo) || updated;
    1311          72 :         if (updated || !cached)
    1312             :         {
    1313          72 :             CPLSerializeXMLTreeToFile(service, filename.c_str());
    1314          72 :         }
    1315             :     }
    1316             :     /* -------------------------------------------------------------------- */
    1317             :     /*      Is this a WCS_GDAL service description file or "in url"         */
    1318             :     /*      equivalent?                                                     */
    1319             :     /* -------------------------------------------------------------------- */
    1320           1 :     else if (poOpenInfo->nHeaderBytes == 0 &&
    1321           0 :              STARTS_WITH_CI((const char *)poOpenInfo->pszFilename,
    1322             :                             "<WCS_GDAL>"))
    1323             :     {
    1324           0 :         service = CPLParseXMLString(poOpenInfo->pszFilename);
    1325             :     }
    1326           1 :     else if (poOpenInfo->nHeaderBytes >= 10 &&
    1327           1 :              STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "<WCS_GDAL>"))
    1328             :     {
    1329           1 :         service = CPLParseXMLFile(poOpenInfo->pszFilename);
    1330             :     }
    1331             :     /* -------------------------------------------------------------------- */
    1332             :     /*      Is this apparently a subdataset?                                */
    1333             :     /* -------------------------------------------------------------------- */
    1334           0 :     else if (STARTS_WITH_CI((const char *)poOpenInfo->pszFilename,
    1335           0 :                             "WCS_SDS:") &&
    1336           0 :              poOpenInfo->nHeaderBytes == 0)
    1337             :     {
    1338             :         int iLast;
    1339             : 
    1340           0 :         papszModifiers = CSLTokenizeString2(poOpenInfo->pszFilename + 8, ",",
    1341             :                                             CSLT_HONOURSTRINGS);
    1342             : 
    1343           0 :         iLast = CSLCount(papszModifiers) - 1;
    1344           0 :         if (iLast >= 0)
    1345             :         {
    1346           0 :             service = CPLParseXMLFile(papszModifiers[iLast]);
    1347           0 :             CPLFree(papszModifiers[iLast]);
    1348           0 :             papszModifiers[iLast] = nullptr;
    1349             :         }
    1350             :     }
    1351             : 
    1352             :     /* -------------------------------------------------------------------- */
    1353             :     /*      Success so far?                                                 */
    1354             :     /* -------------------------------------------------------------------- */
    1355          73 :     if (service == nullptr)
    1356             :     {
    1357           0 :         CSLDestroy(papszModifiers);
    1358           0 :         return nullptr;
    1359             :     }
    1360             : 
    1361             :     /* -------------------------------------------------------------------- */
    1362             :     /*      Confirm the requested access is supported.                      */
    1363             :     /* -------------------------------------------------------------------- */
    1364          73 :     if (poOpenInfo->eAccess == GA_Update)
    1365             :     {
    1366           0 :         CSLDestroy(papszModifiers);
    1367           0 :         CPLDestroyXMLNode(service);
    1368           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1369             :                  "The WCS driver does not support update access to existing"
    1370             :                  " datasets.\n");
    1371           0 :         return nullptr;
    1372             :     }
    1373             : 
    1374             :     /* -------------------------------------------------------------------- */
    1375             :     /*      Check for required minimum fields.                              */
    1376             :     /* -------------------------------------------------------------------- */
    1377         146 :     if (!CPLGetXMLValue(service, "ServiceURL", nullptr) ||
    1378          73 :         !CPLGetXMLValue(service, "CoverageName", nullptr))
    1379             :     {
    1380           0 :         CSLDestroy(papszModifiers);
    1381           0 :         CPLError(
    1382             :             CE_Failure, CPLE_OpenFailed,
    1383             :             "Missing one or both of ServiceURL and CoverageName elements.\n"
    1384             :             "See WCS driver documentation for details on service description "
    1385             :             "file format.");
    1386             : 
    1387           0 :         CPLDestroyXMLNode(service);
    1388           0 :         return nullptr;
    1389             :     }
    1390             : 
    1391             :     /* -------------------------------------------------------------------- */
    1392             :     /*      What version are we working with?                               */
    1393             :     /* -------------------------------------------------------------------- */
    1394          73 :     const char *pszVersion = CPLGetXMLValue(service, "Version", "1.0.0");
    1395             : 
    1396          73 :     int nVersion = WCSParseVersion(pszVersion);
    1397             : 
    1398          73 :     if (nVersion == 0)
    1399             :     {
    1400           0 :         CSLDestroy(papszModifiers);
    1401           0 :         CPLDestroyXMLNode(service);
    1402           0 :         return nullptr;
    1403             :     }
    1404             : 
    1405             :     /* -------------------------------------------------------------------- */
    1406             :     /*      Create a corresponding GDALDataset.                             */
    1407             :     /* -------------------------------------------------------------------- */
    1408             :     WCSDataset *poDS;
    1409          73 :     if (nVersion == 201)
    1410             :     {
    1411          21 :         poDS = new WCSDataset201(cache.c_str());
    1412             :     }
    1413          52 :     else if (nVersion / 10 == 11)
    1414             :     {
    1415          36 :         poDS = new WCSDataset110(nVersion, cache.c_str());
    1416             :     }
    1417             :     else
    1418             :     {
    1419          16 :         poDS = new WCSDataset100(cache.c_str());
    1420             :     }
    1421             : 
    1422          73 :     poDS->psService = service;
    1423          73 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1424          73 :     poDS->papszSDSModifiers = papszModifiers;
    1425             :     // WCS:URL => basic metadata was already made
    1426             :     // Metadata is needed in ExtractGridInfo
    1427          73 :     poDS->TryLoadXML();
    1428             : 
    1429             :     /* -------------------------------------------------------------------- */
    1430             :     /*      Capture HTTP parameters.                                        */
    1431             :     /* -------------------------------------------------------------------- */
    1432             :     const char *pszParam;
    1433             : 
    1434          73 :     poDS->papszHttpOptions =
    1435          73 :         CSLSetNameValue(poDS->papszHttpOptions, "TIMEOUT",
    1436             :                         CPLGetXMLValue(service, "Timeout", "30"));
    1437             : 
    1438          73 :     pszParam = CPLGetXMLValue(service, "HTTPAUTH", nullptr);
    1439          73 :     if (pszParam)
    1440           0 :         poDS->papszHttpOptions =
    1441           0 :             CSLSetNameValue(poDS->papszHttpOptions, "HTTPAUTH", pszParam);
    1442             : 
    1443          73 :     pszParam = CPLGetXMLValue(service, "USERPWD", nullptr);
    1444          73 :     if (pszParam)
    1445           0 :         poDS->papszHttpOptions =
    1446           0 :             CSLSetNameValue(poDS->papszHttpOptions, "USERPWD", pszParam);
    1447             : 
    1448             :     /* -------------------------------------------------------------------- */
    1449             :     /*      If we don't have the DescribeCoverage result for this           */
    1450             :     /*      coverage, fetch it now.                                         */
    1451             :     /* -------------------------------------------------------------------- */
    1452         136 :     if (CPLGetXMLNode(service, "CoverageOffering") == nullptr &&
    1453          63 :         CPLGetXMLNode(service, "CoverageDescription") == nullptr)
    1454             :     {
    1455          25 :         if (!poDS->DescribeCoverage())
    1456             :         {
    1457           1 :             delete poDS;
    1458           1 :             return nullptr;
    1459             :         }
    1460             :     }
    1461             : 
    1462             :     /* -------------------------------------------------------------------- */
    1463             :     /*      Extract coordinate system, grid size, and geotransform from     */
    1464             :     /*      the coverage description and/or service description             */
    1465             :     /*      information.                                                    */
    1466             :     /* -------------------------------------------------------------------- */
    1467          72 :     if (!poDS->ExtractGridInfo())
    1468             :     {
    1469           0 :         delete poDS;
    1470           0 :         return nullptr;
    1471             :     }
    1472             : 
    1473             :     /* -------------------------------------------------------------------- */
    1474             :     /*      Leave now or there may be a GetCoverage call.                   */
    1475             :     /*                                                                      */
    1476             :     /* -------------------------------------------------------------------- */
    1477          72 :     int nBandCount = -1;
    1478         144 :     std::string sBandCount = CPLGetXMLValue(service, "BandCount", "");
    1479          72 :     if (sBandCount != "")
    1480             :     {
    1481          61 :         nBandCount = atoi(sBandCount.c_str());
    1482             :     }
    1483          72 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions, "SKIP_GETCOVERAGE", false) ||
    1484             :         nBandCount == 0)
    1485             :     {
    1486           0 :         return poDS;
    1487             :     }
    1488             : 
    1489             :     /* -------------------------------------------------------------------- */
    1490             :     /*      Extract band count and type from a sample.                      */
    1491             :     /* -------------------------------------------------------------------- */
    1492          72 :     if (!poDS->EstablishRasterDetails())  // todo: do this only if missing info
    1493             :     {
    1494           0 :         delete poDS;
    1495           0 :         return nullptr;
    1496             :     }
    1497             : 
    1498             :     /* -------------------------------------------------------------------- */
    1499             :     /*      It is ok to not have bands. The user just needs to supply       */
    1500             :     /*      more information.                                               */
    1501             :     /* -------------------------------------------------------------------- */
    1502          72 :     nBandCount = atoi(CPLGetXMLValue(service, "BandCount", "0"));
    1503          72 :     if (nBandCount == 0)
    1504             :     {
    1505           0 :         return poDS;
    1506             :     }
    1507             : 
    1508             :     /* -------------------------------------------------------------------- */
    1509             :     /*      Create band information objects.                                */
    1510             :     /* -------------------------------------------------------------------- */
    1511             :     int iBand;
    1512             : 
    1513          72 :     if (!GDALCheckBandCount(nBandCount, FALSE))
    1514             :     {
    1515           0 :         delete poDS;
    1516           0 :         return nullptr;
    1517             :     }
    1518             : 
    1519         207 :     for (iBand = 0; iBand < nBandCount; iBand++)
    1520             :     {
    1521         135 :         WCSRasterBand *band = new WCSRasterBand(poDS, iBand + 1, -1);
    1522             :         // copy band specific metadata to the band
    1523         135 :         char **md_from = poDS->GetMetadata("");
    1524         135 :         char **md_to = nullptr;
    1525         135 :         if (md_from)
    1526             :         {
    1527         270 :             std::string our_key = CPLString().Printf("FIELD_%d_", iBand + 1);
    1528        3192 :             for (char **from = md_from; *from != nullptr; ++from)
    1529             :             {
    1530        6114 :                 std::vector<std::string> kv = Split(*from, "=");
    1531        6114 :                 if (kv.size() > 1 &&
    1532        3057 :                     STARTS_WITH(kv[0].c_str(), our_key.c_str()))
    1533             :                 {
    1534         174 :                     std::string key = kv[0];
    1535          87 :                     std::string value = kv[1];
    1536          87 :                     key.erase(0, our_key.length());
    1537          87 :                     md_to = CSLSetNameValue(md_to, key.c_str(), value.c_str());
    1538             :                 }
    1539             :             }
    1540             :         }
    1541         135 :         band->SetMetadata(md_to, "");
    1542         135 :         CSLDestroy(md_to);
    1543         135 :         poDS->SetBand(iBand + 1, band);
    1544             :     }
    1545             : 
    1546             :     /* -------------------------------------------------------------------- */
    1547             :     /*      Set time metadata on the dataset if we are selecting a          */
    1548             :     /*      temporal slice.                                                 */
    1549             :     /* -------------------------------------------------------------------- */
    1550          72 :     std::string osTime = CSLFetchNameValueDef(poDS->papszSDSModifiers, "time",
    1551          72 :                                               poDS->osDefaultTime.c_str());
    1552             : 
    1553          72 :     if (osTime != "")
    1554           0 :         poDS->GDALMajorObject::SetMetadataItem("TIME_POSITION", osTime.c_str());
    1555             : 
    1556             :     /* -------------------------------------------------------------------- */
    1557             :     /*      Do we have a band identifier to select only a subset of bands?  */
    1558             :     /* -------------------------------------------------------------------- */
    1559          72 :     poDS->osBandIdentifier = CPLGetXMLValue(service, "BandIdentifier", "");
    1560             : 
    1561             :     /* -------------------------------------------------------------------- */
    1562             :     /*      Do we have time based subdatasets?  If so, record them in       */
    1563             :     /*      metadata.  Note we don't do subdatasets if this is a            */
    1564             :     /*      subdataset or if this is an all-in-memory service.              */
    1565             :     /* -------------------------------------------------------------------- */
    1566         216 :     if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "WCS_SDS:") &&
    1567         144 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "<WCS_GDAL>") &&
    1568          72 :         !poDS->aosTimePositions.empty())
    1569             :     {
    1570           0 :         char **papszSubdatasets = nullptr;
    1571             :         int iTime;
    1572             : 
    1573           0 :         for (iTime = 0; iTime < (int)poDS->aosTimePositions.size(); iTime++)
    1574             :         {
    1575           0 :             std::string osName;
    1576           0 :             std::string osValue;
    1577             : 
    1578           0 :             osName = CPLString().Printf("SUBDATASET_%d_NAME", iTime + 1);
    1579           0 :             osValue = CPLString().Printf("WCS_SDS:time=\"%s\",%s",
    1580           0 :                                          poDS->aosTimePositions[iTime].c_str(),
    1581           0 :                                          poOpenInfo->pszFilename);
    1582           0 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets, osName.c_str(),
    1583             :                                                osValue.c_str());
    1584             : 
    1585             :             std::string osCoverage =
    1586           0 :                 CPLGetXMLValue(poDS->psService, "CoverageName", "");
    1587             : 
    1588           0 :             osName = CPLString().Printf("SUBDATASET_%d_DESC", iTime + 1);
    1589             :             osValue =
    1590           0 :                 CPLString().Printf("Coverage %s at time %s", osCoverage.c_str(),
    1591           0 :                                    poDS->aosTimePositions[iTime].c_str());
    1592           0 :             papszSubdatasets = CSLSetNameValue(papszSubdatasets, osName.c_str(),
    1593             :                                                osValue.c_str());
    1594             :         }
    1595             : 
    1596           0 :         poDS->GDALMajorObject::SetMetadata(papszSubdatasets, "SUBDATASETS");
    1597             : 
    1598           0 :         CSLDestroy(papszSubdatasets);
    1599             :     }
    1600             : 
    1601             :     /* -------------------------------------------------------------------- */
    1602             :     /*      Initialize any PAM information.                                 */
    1603             :     /* -------------------------------------------------------------------- */
    1604          72 :     poDS->TryLoadXML();
    1605          72 :     return poDS;
    1606             : }
    1607             : 
    1608             : /************************************************************************/
    1609             : /*                          GetGeoTransform()                           */
    1610             : /************************************************************************/
    1611             : 
    1612          72 : CPLErr WCSDataset::GetGeoTransform(double *padfTransform)
    1613             : 
    1614             : {
    1615          72 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
    1616          72 :     return CE_None;
    1617             : }
    1618             : 
    1619             : /************************************************************************/
    1620             : /*                          GetSpatialRef()                             */
    1621             : /************************************************************************/
    1622             : 
    1623          48 : const OGRSpatialReference *WCSDataset::GetSpatialRef() const
    1624             : 
    1625             : {
    1626          48 :     const auto poSRS = GDALPamDataset::GetSpatialRef();
    1627          48 :     if (poSRS)
    1628           0 :         return poSRS;
    1629             : 
    1630          48 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
    1631             : }
    1632             : 
    1633             : /************************************************************************/
    1634             : /*                            GetFileList()                             */
    1635             : /************************************************************************/
    1636             : 
    1637           0 : char **WCSDataset::GetFileList()
    1638             : 
    1639             : {
    1640           0 :     char **papszFileList = GDALPamDataset::GetFileList();
    1641             : 
    1642             : /* -------------------------------------------------------------------- */
    1643             : /*      ESRI also wishes to include service urls in the file list       */
    1644             : /*      though this is not currently part of the general definition     */
    1645             : /*      of GetFileList() for GDAL.                                      */
    1646             : /* -------------------------------------------------------------------- */
    1647             : #ifdef ESRI_BUILD
    1648             :     std::string file;
    1649             :     file.Printf("%s%s", CPLGetXMLValue(psService, "ServiceURL", ""),
    1650             :                 CPLGetXMLValue(psService, "CoverageName", ""));
    1651             :     papszFileList = CSLAddString(papszFileList, file.c_str());
    1652             : #endif /* def ESRI_BUILD */
    1653             : 
    1654           0 :     return papszFileList;
    1655             : }
    1656             : 
    1657             : /************************************************************************/
    1658             : /*                      GetMetadataDomainList()                         */
    1659             : /************************************************************************/
    1660             : 
    1661           0 : char **WCSDataset::GetMetadataDomainList()
    1662             : {
    1663           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
    1664           0 :                                    TRUE, "xml:CoverageOffering", nullptr);
    1665             : }
    1666             : 
    1667             : /************************************************************************/
    1668             : /*                            GetMetadata()                             */
    1669             : /************************************************************************/
    1670             : 
    1671         348 : char **WCSDataset::GetMetadata(const char *pszDomain)
    1672             : 
    1673             : {
    1674         348 :     if (pszDomain == nullptr || !EQUAL(pszDomain, "xml:CoverageOffering"))
    1675         348 :         return GDALPamDataset::GetMetadata(pszDomain);
    1676             : 
    1677           0 :     CPLXMLNode *psNode = CPLGetXMLNode(psService, "CoverageOffering");
    1678             : 
    1679           0 :     if (psNode == nullptr)
    1680           0 :         psNode = CPLGetXMLNode(psService, "CoverageDescription");
    1681             : 
    1682           0 :     if (psNode == nullptr)
    1683           0 :         return nullptr;
    1684             : 
    1685           0 :     if (apszCoverageOfferingMD[0] == nullptr)
    1686             :     {
    1687           0 :         CPLXMLNode *psNext = psNode->psNext;
    1688           0 :         psNode->psNext = nullptr;
    1689             : 
    1690           0 :         apszCoverageOfferingMD[0] = CPLSerializeXMLTree(psNode);
    1691             : 
    1692           0 :         psNode->psNext = psNext;
    1693             :     }
    1694             : 
    1695           0 :     return apszCoverageOfferingMD;
    1696             : }
    1697             : 
    1698             : /************************************************************************/
    1699             : /*                          GDALRegister_WCS()                          */
    1700             : /************************************************************************/
    1701             : 
    1702        1595 : void GDALRegister_WCS()
    1703             : 
    1704             : {
    1705        1595 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    1706         302 :         return;
    1707             : 
    1708        1293 :     GDALDriver *poDriver = new GDALDriver();
    1709        1293 :     WCSDriverSetCommonMetadata(poDriver);
    1710             : 
    1711        1293 :     poDriver->pfnOpen = WCSDataset::Open;
    1712             : 
    1713        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1714             : }

Generated by: LCOV version 1.14