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

Generated by: LCOV version 1.14