LCOV - code coverage report
Current view: top level - frmts/daas - daasdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 953 1130 84.3 %
Date: 2025-07-06 19:57:03 Functions: 40 41 97.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DAAS driver
       4             :  * Purpose:  DAAS driver
       5             :  * Author:   Even Rouault, <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018-2019, Airbus DS Intelligence
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_http.h"
      14             : #include "gdal_frmts.h"
      15             : #include "gdal_alg.h"
      16             : #include "gdal_priv.h"
      17             : #include "ogr_spatialref.h"
      18             : #include "gdal_mdreader.h"
      19             : #include "memdataset.h"
      20             : 
      21             : #include "cpl_json.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <array>
      25             : #include <memory>
      26             : 
      27             : constexpr int knMIN_BLOCKSIZE = 64;
      28             : constexpr int knDEFAULT_BLOCKSIZE = 512;
      29             : constexpr int knMAX_BLOCKSIZE = 8192;
      30             : 
      31             : constexpr GUInt32 RETRY_PER_BAND = 1;
      32             : constexpr GUInt32 RETRY_SPATIAL_SPLIT = 2;
      33             : 
      34             : // Let's limit to 100 MB uncompressed per request
      35             : constexpr int knDEFAULT_SERVER_BYTE_LIMIT = 100 * 1024 * 1024;
      36             : 
      37             : constexpr int MAIN_MASK_BAND_NUMBER = 0;
      38             : 
      39             : /************************************************************************/
      40             : /*                          GDALDAASBandDesc                            */
      41             : /************************************************************************/
      42             : 
      43             : class GDALDAASBandDesc
      44             : {
      45             :   public:
      46             :     int nIndex = 0;
      47             :     GDALDataType eDT =
      48             :         GDT_Unknown;  // as declared in the GetMetadata response bands[]
      49             :     CPLString osName{};
      50             :     CPLString osDescription{};
      51             :     CPLString osColorInterp{};
      52             :     bool bIsMask = false;
      53             : };
      54             : 
      55             : /************************************************************************/
      56             : /*                          GDALDAASDataset                             */
      57             : /************************************************************************/
      58             : 
      59             : class GDALDAASRasterBand;
      60             : 
      61             : class GDALDAASDataset final : public GDALDataset
      62             : {
      63             :   public:
      64             :     enum class Format
      65             :     {
      66             :         RAW,
      67             :         PNG,
      68             :         JPEG,
      69             :         JPEG2000,
      70             :     };
      71             : 
      72             :   private:
      73             :     friend class GDALDAASRasterBand;
      74             : 
      75             :     CPLString m_osGetMetadataURL{};
      76             : 
      77             :     CPLString m_osAuthURL{};
      78             :     CPLString m_osAccessToken{};
      79             :     time_t m_nExpirationTime = 0;
      80             :     CPLString m_osXForwardUser{};
      81             : 
      82             :     GDALDAASDataset *m_poParentDS = nullptr;
      83             :     // int         m_iOvrLevel = 0;
      84             : 
      85             :     OGRSpatialReference m_oSRS{};
      86             :     CPLString m_osSRSType{};
      87             :     CPLString m_osSRSValue{};
      88             :     bool m_bGotGeoTransform = false;
      89             :     GDALGeoTransform m_gt{};
      90             :     bool m_bRequestInGeoreferencedCoordinates = false;
      91             :     GDALDataType m_eDT = GDT_Unknown;
      92             :     int m_nActualBitDepth = 0;
      93             :     bool m_bHasNoData = false;
      94             :     double m_dfNoDataValue = 0.0;
      95             :     CPLString m_osGetBufferURL{};
      96             :     int m_nBlockSize = knDEFAULT_BLOCKSIZE;
      97             :     Format m_eFormat = Format::RAW;
      98             :     GIntBig m_nServerByteLimit = knDEFAULT_SERVER_BYTE_LIMIT;
      99             :     GDALRIOResampleAlg m_eCurrentResampleAlg = GRIORA_NearestNeighbour;
     100             : 
     101             :     int m_nMainMaskBandIndex = 0;
     102             :     CPLString m_osMainMaskName{};
     103             :     GDALDAASRasterBand *m_poMaskBand = nullptr;
     104             :     std::vector<GDALDAASBandDesc> m_aoBandDesc{};
     105             : 
     106             :     int m_nXOffAdvise = 0;
     107             :     int m_nYOffAdvise = 0;
     108             :     int m_nXSizeAdvise = 0;
     109             :     int m_nYSizeAdvise = 0;
     110             : 
     111             :     int m_nXOffFetched = 0;
     112             :     int m_nYOffFetched = 0;
     113             :     int m_nXSizeFetched = 0;
     114             :     int m_nYSizeFetched = 0;
     115             : 
     116             :     std::vector<std::unique_ptr<GDALDAASDataset>> m_apoOverviewDS{};
     117             : 
     118             :     char **m_papszOpenOptions = nullptr;
     119             : 
     120             :     // Methods
     121             :     bool Open(GDALOpenInfo *poOpenInfo);
     122             :     bool GetAuthorization();
     123             :     bool GetImageMetadata();
     124             :     char **GetHTTPOptions();
     125             :     void ReadSRS(const CPLJSONObject &oProperties);
     126             :     void ReadRPCs(const CPLJSONObject &oProperties);
     127             :     bool SetupServerSideReprojection(const char *pszTargetSRS);
     128             :     void InstantiateBands();
     129             : 
     130             :     CPL_DISALLOW_COPY_ASSIGN(GDALDAASDataset)
     131             : 
     132             :   public:
     133             :     GDALDAASDataset();
     134             :     GDALDAASDataset(GDALDAASDataset *poParentDS, int iOvrLevel);
     135             :     ~GDALDAASDataset();
     136             : 
     137             :     static int Identify(GDALOpenInfo *poOpenInfo);
     138             :     static GDALDataset *OpenStatic(GDALOpenInfo *poOpenInfo);
     139             : 
     140             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     141             :     const OGRSpatialReference *GetSpatialRef() const override;
     142             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
     143             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
     144             :                      GDALDataType eBufType, int nBandCount,
     145             :                      BANDMAP_TYPE panBands, GSpacing nPixelSpace,
     146             :                      GSpacing nLineSpace, GSpacing nBandSpace,
     147             :                      GDALRasterIOExtraArg *psExtraArg) override;
     148             :     CPLErr AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
     149             :                       int /* nBufXSize */, int /* nBufYSize */,
     150             :                       GDALDataType /* eBufType */, int /*nBands*/,
     151             :                       int * /*panBands*/, char ** /* papszOptions */) override;
     152             :     CPLErr FlushCache(bool bAtClosing) override;
     153             : };
     154             : 
     155             : /************************************************************************/
     156             : /*                         GDALDAASRasterBand                            */
     157             : /************************************************************************/
     158             : 
     159             : class GDALDAASRasterBand final : public GDALRasterBand
     160             : {
     161             :     friend class GDALDAASDataset;
     162             : 
     163             :     int m_nSrcIndex = 0;
     164             :     GDALColorInterp m_eColorInterp = GCI_Undefined;
     165             : 
     166             :     CPLErr GetBlocks(int nBlockXOff, int nBlockYOff, int nXBlocks, int nYBlocks,
     167             :                      const std::vector<int> &anRequestedBands, void *pBuffer);
     168             : 
     169             :     GUInt32 PrefetchBlocks(int nXOff, int nYOff, int nXSize, int nYSize,
     170             :                            const std::vector<int> &anRequestedBands);
     171             : 
     172             :   public:
     173             :     GDALDAASRasterBand(GDALDAASDataset *poDS, int nBand,
     174             :                        const GDALDAASBandDesc &oBandDesc);
     175             : 
     176             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
     177             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
     178             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
     179             :                      GDALDataType eBufType, GSpacing nPixelSpace,
     180             :                      GSpacing nLineSpace,
     181             :                      GDALRasterIOExtraArg *psExtraArg) override;
     182             :     CPLErr AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
     183             :                       int /* nBufXSize */, int /* nBufYSize */,
     184             :                       GDALDataType /* eBufType */,
     185             :                       char ** /* papszOptions */) override;
     186             :     double GetNoDataValue(int *pbHasNoData) override;
     187             :     GDALColorInterp GetColorInterpretation() override;
     188             :     GDALRasterBand *GetMaskBand() override;
     189             :     int GetMaskFlags() override;
     190             :     int GetOverviewCount() override;
     191             :     GDALRasterBand *GetOverview(int) override;
     192             : };
     193             : 
     194             : /************************************************************************/
     195             : /*                          GDALDAASDataset()                           */
     196             : /************************************************************************/
     197             : 
     198          45 : GDALDAASDataset::GDALDAASDataset()
     199             :     : m_osAuthURL(CPLGetConfigOption(
     200             :           "GDAL_DAAS_AUTH_URL", "https://authenticate.geoapi-airbusds.com/auth/"
     201          45 :                                 "realms/IDP/protocol/openid-connect/token"))
     202             : {
     203          45 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     204          45 : }
     205             : 
     206             : /************************************************************************/
     207             : /*                          GDALDAASDataset()                           */
     208             : /************************************************************************/
     209             : 
     210           6 : GDALDAASDataset::GDALDAASDataset(GDALDAASDataset *poParentDS, int iOvrLevel)
     211           6 :     : m_osGetMetadataURL(poParentDS->m_osGetMetadataURL),
     212           6 :       m_osAuthURL(poParentDS->m_osAuthURL),
     213             :       m_osAccessToken(CPLString()),   // only used by parent
     214             :       m_nExpirationTime(0),           // only used by parent
     215             :       m_osXForwardUser(CPLString()),  // only used by parent
     216             :       m_poParentDS(poParentDS),
     217             :       // m_iOvrLevel(iOvrLevel),
     218          12 :       m_oSRS(poParentDS->m_oSRS), m_osSRSType(poParentDS->m_osSRSType),
     219           6 :       m_osSRSValue(poParentDS->m_osSRSValue),
     220           6 :       m_bGotGeoTransform(poParentDS->m_bGotGeoTransform),
     221             :       m_bRequestInGeoreferencedCoordinates(
     222           6 :           poParentDS->m_bRequestInGeoreferencedCoordinates),
     223           6 :       m_eDT(poParentDS->m_eDT),
     224           6 :       m_nActualBitDepth(poParentDS->m_nActualBitDepth),
     225           6 :       m_bHasNoData(poParentDS->m_bHasNoData),
     226           6 :       m_dfNoDataValue(poParentDS->m_dfNoDataValue),
     227           6 :       m_osGetBufferURL(poParentDS->m_osGetBufferURL),
     228           6 :       m_eFormat(poParentDS->m_eFormat),
     229           6 :       m_nServerByteLimit(poParentDS->m_nServerByteLimit),
     230           6 :       m_nMainMaskBandIndex(poParentDS->m_nMainMaskBandIndex),
     231           6 :       m_osMainMaskName(poParentDS->m_osMainMaskName), m_poMaskBand(nullptr),
     232           6 :       m_aoBandDesc(poParentDS->m_aoBandDesc)
     233             : {
     234           6 :     nRasterXSize = m_poParentDS->nRasterXSize >> iOvrLevel;
     235           6 :     nRasterYSize = m_poParentDS->nRasterYSize >> iOvrLevel;
     236           6 :     m_gt = m_poParentDS->m_gt;
     237           6 :     m_gt.Rescale(static_cast<double>(m_poParentDS->nRasterXSize) / nRasterXSize,
     238           6 :                  static_cast<double>(m_poParentDS->nRasterYSize) /
     239           6 :                      nRasterYSize);
     240             : 
     241           6 :     InstantiateBands();
     242             : 
     243           6 :     SetMetadata(m_poParentDS->GetMetadata());
     244           6 :     SetMetadata(m_poParentDS->GetMetadata("RPC"), "RPC");
     245           6 : }
     246             : 
     247             : /************************************************************************/
     248             : /*                         ~GDALDAASDataset()                            */
     249             : /************************************************************************/
     250             : 
     251         102 : GDALDAASDataset::~GDALDAASDataset()
     252             : {
     253          51 :     if (m_poParentDS == nullptr)
     254             :     {
     255          45 :         char **papszOptions = nullptr;
     256          45 :         papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT",
     257             :                                        CPLSPrintf("%p", this));
     258          45 :         CPLHTTPDestroyResult(CPLHTTPFetch("", papszOptions));
     259          45 :         CSLDestroy(papszOptions);
     260             :     }
     261             : 
     262          51 :     delete m_poMaskBand;
     263          51 :     CSLDestroy(m_papszOpenOptions);
     264         102 : }
     265             : 
     266             : /************************************************************************/
     267             : /*                          InstantiateBands()                          */
     268             : /************************************************************************/
     269             : 
     270          25 : void GDALDAASDataset::InstantiateBands()
     271             : {
     272          64 :     for (int i = 0; i < static_cast<int>(m_aoBandDesc.size()); i++)
     273             :     {
     274             :         GDALRasterBand *poBand =
     275          39 :             new GDALDAASRasterBand(this, i + 1, m_aoBandDesc[i]);
     276          39 :         SetBand(i + 1, poBand);
     277             :     }
     278             : 
     279          25 :     if (!m_osMainMaskName.empty())
     280             :     {
     281           1 :         GDALDAASBandDesc oDesc;
     282           1 :         oDesc.nIndex = m_nMainMaskBandIndex;
     283           1 :         oDesc.osName = m_osMainMaskName;
     284           1 :         m_poMaskBand = new GDALDAASRasterBand(this, 0, oDesc);
     285             :     }
     286             : 
     287          25 :     if (nBands > 1)
     288             :     {
     289             :         // Hint for users of the driver
     290           7 :         GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     291             :     }
     292          25 : }
     293             : 
     294             : /************************************************************************/
     295             : /*                            Identify()                                */
     296             : /************************************************************************/
     297             : 
     298       56855 : int GDALDAASDataset::Identify(GDALOpenInfo *poOpenInfo)
     299             : {
     300       56855 :     return STARTS_WITH_CI(poOpenInfo->pszFilename, "DAAS:");
     301             : }
     302             : 
     303             : /************************************************************************/
     304             : /*                        GetGeoTransform()                             */
     305             : /************************************************************************/
     306             : 
     307           1 : CPLErr GDALDAASDataset::GetGeoTransform(GDALGeoTransform &gt) const
     308             : {
     309           1 :     gt = m_gt;
     310           1 :     return (m_bGotGeoTransform) ? CE_None : CE_Failure;
     311             : }
     312             : 
     313             : /************************************************************************/
     314             : /*                          GetSpatialRef()                            */
     315             : /************************************************************************/
     316             : 
     317           1 : const OGRSpatialReference *GDALDAASDataset::GetSpatialRef() const
     318             : {
     319           1 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     320             : }
     321             : 
     322             : /********************-****************************************************/
     323             : /*                             URLEscape()                              */
     324             : /************************************************************************/
     325             : 
     326          14 : static CPLString URLEscape(const CPLString &osStr)
     327             : {
     328          14 :     char *pszEscaped = CPLEscapeString(osStr.c_str(), -1, CPLES_URL);
     329          14 :     CPLString osRet(pszEscaped);
     330          14 :     CPLFree(pszEscaped);
     331          14 :     return osRet;
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                             GetHTTPOptions()                         */
     336             : /************************************************************************/
     337             : 
     338          72 : char **GDALDAASDataset::GetHTTPOptions()
     339             : {
     340          72 :     if (m_poParentDS)
     341           1 :         return m_poParentDS->GetHTTPOptions();
     342             : 
     343          71 :     char **papszOptions = nullptr;
     344          71 :     CPLString osHeaders;
     345          71 :     if (!m_osAccessToken.empty())
     346             :     {
     347             :         // Renew token if needed
     348           4 :         if (m_nExpirationTime != 0 && time(nullptr) >= m_nExpirationTime)
     349             :         {
     350           1 :             GetAuthorization();
     351             :         }
     352           4 :         osHeaders += "Authorization: Bearer " + m_osAccessToken;
     353             :     }
     354             :     else
     355             :     {
     356             :         const char *pszAuthorization =
     357          67 :             CPLGetConfigOption("GDAL_DAAS_AUTHORIZATION", nullptr);
     358          67 :         if (pszAuthorization)
     359           0 :             osHeaders += pszAuthorization;
     360             :     }
     361          71 :     if (!m_osXForwardUser.empty())
     362             :     {
     363           1 :         if (!osHeaders.empty())
     364           1 :             osHeaders += "\r\n";
     365           1 :         osHeaders += "X-Forwarded-User: " + m_osXForwardUser;
     366             :     }
     367          71 :     if (!osHeaders.empty())
     368             :     {
     369             :         papszOptions =
     370           4 :             CSLSetNameValue(papszOptions, "HEADERS", osHeaders.c_str());
     371             :     }
     372             :     papszOptions =
     373          71 :         CSLSetNameValue(papszOptions, "PERSISTENT", CPLSPrintf("%p", this));
     374             :     // 30 minutes
     375          71 :     papszOptions = CSLSetNameValue(papszOptions, "TIMEOUT", "1800");
     376          71 :     return papszOptions;
     377             : }
     378             : 
     379             : /************************************************************************/
     380             : /*                          DAASBackoffFactor()                         */
     381             : /************************************************************************/
     382             : 
     383             : /* Add a small amount of random jitter to avoid cyclic server stampedes */
     384           8 : static double DAASBackoffFactor(double base)
     385             : {
     386             :     // We don't need cryptographic quality randomness...
     387             :     return base
     388             : #ifndef __COVERITY__
     389           8 :            + rand() * 0.5 / RAND_MAX
     390             : #endif
     391             :         ;
     392             : }
     393             : 
     394             : /************************************************************************/
     395             : /*                          DAAS_CPLHTTPFetch()                         */
     396             : /************************************************************************/
     397             : 
     398          78 : static CPLHTTPResult *DAAS_CPLHTTPFetch(const char *pszURL, char **papszOptions)
     399             : {
     400             :     CPLHTTPResult *psResult;
     401          78 :     const int RETRY_COUNT = 4;
     402             :     // coverity[tainted_data]
     403             :     double dfRetryDelay =
     404          78 :         CPLAtof(CPLGetConfigOption("GDAL_DAAS_INITIAL_RETRY_DELAY", "1.0"));
     405          86 :     for (int i = 0; i <= RETRY_COUNT; i++)
     406             :     {
     407          86 :         psResult = CPLHTTPFetch(pszURL, papszOptions);
     408          86 :         if (psResult == nullptr)
     409           0 :             break;
     410             : 
     411          86 :         if (psResult->nDataLen != 0 && psResult->nStatus == 0 &&
     412          60 :             psResult->pszErrBuf == nullptr)
     413             :         {
     414             :             /* got a valid response */
     415          57 :             CPLErrorReset();
     416          57 :             break;
     417             :         }
     418             :         else
     419             :         {
     420          29 :             const char *pszErrorText =
     421          29 :                 psResult->pszErrBuf ? psResult->pszErrBuf : "(null)";
     422             : 
     423             :             /* Get HTTP status code */
     424          29 :             int nHTTPStatus = -1;
     425          29 :             if (psResult->pszErrBuf != nullptr &&
     426          28 :                 EQUALN(psResult->pszErrBuf,
     427             :                        "HTTP error code : ", strlen("HTTP error code : ")))
     428             :             {
     429             :                 nHTTPStatus =
     430          28 :                     atoi(psResult->pszErrBuf + strlen("HTTP error code : "));
     431          28 :                 if (psResult->pabyData)
     432           3 :                     pszErrorText =
     433             :                         reinterpret_cast<const char *>(psResult->pabyData);
     434             :             }
     435             : 
     436          29 :             if ((nHTTPStatus == 500 ||
     437           9 :                  (nHTTPStatus >= 502 && nHTTPStatus <= 504)) &&
     438             :                 i < RETRY_COUNT)
     439             :             {
     440           8 :                 CPLError(CE_Warning, CPLE_FileIO,
     441             :                          "Error when downloading %s,"
     442             :                          "HTTP status=%d, retrying in %.2fs : %s",
     443             :                          pszURL, nHTTPStatus, dfRetryDelay, pszErrorText);
     444           8 :                 CPLHTTPDestroyResult(psResult);
     445           8 :                 psResult = nullptr;
     446             : 
     447           8 :                 CPLSleep(dfRetryDelay);
     448           8 :                 dfRetryDelay *= DAASBackoffFactor(4);
     449             :             }
     450             :             else
     451             :             {
     452             :                 break;
     453             :             }
     454             :         }
     455             :     }
     456             : 
     457          78 :     return psResult;
     458             : }
     459             : 
     460             : /************************************************************************/
     461             : /*                           GetAuthorization()                         */
     462             : /************************************************************************/
     463             : 
     464          10 : bool GDALDAASDataset::GetAuthorization()
     465             : {
     466             :     const CPLString osClientId =
     467          10 :         CSLFetchNameValueDef(m_papszOpenOptions, "CLIENT_ID",
     468          20 :                              CPLGetConfigOption("GDAL_DAAS_CLIENT_ID", ""));
     469             :     const CPLString osAPIKey =
     470          10 :         CSLFetchNameValueDef(m_papszOpenOptions, "API_KEY",
     471          20 :                              CPLGetConfigOption("GDAL_DAAS_API_KEY", ""));
     472             :     std::string osAuthorization =
     473          10 :         CSLFetchNameValueDef(m_papszOpenOptions, "ACCESS_TOKEN",
     474          20 :                              CPLGetConfigOption("GDAL_DAAS_ACCESS_TOKEN", ""));
     475             :     m_osXForwardUser = CSLFetchNameValueDef(
     476          10 :         m_papszOpenOptions, "X_FORWARDED_USER",
     477          10 :         CPLGetConfigOption("GDAL_DAAS_X_FORWARDED_USER", ""));
     478             : 
     479          10 :     if (!osAuthorization.empty())
     480             :     {
     481           1 :         if (!osClientId.empty() && !osAPIKey.empty())
     482             :         {
     483           0 :             CPLError(
     484             :                 CE_Warning, CPLE_AppDefined,
     485             :                 "GDAL_DAAS_CLIENT_ID + GDAL_DAAS_API_KEY and "
     486             :                 "GDAL_DAAS_ACCESS_TOKEN defined. Only the later taken into "
     487             :                 "account");
     488             :         }
     489           1 :         m_osAccessToken = std::move(osAuthorization);
     490           1 :         return true;
     491             :     }
     492             : 
     493           9 :     if (osClientId.empty() && osAPIKey.empty())
     494             :     {
     495           0 :         CPLDebug("DAAS",
     496             :                  "Neither GDAL_DAAS_CLIENT_ID, GDAL_DAAS_API_KEY "
     497             :                  "nor GDAL_DAAS_ACCESS_TOKEN is defined. Trying without "
     498             :                  "authorization");
     499           0 :         return true;
     500             :     }
     501             : 
     502           9 :     if (osClientId.empty())
     503             :     {
     504           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     505             :                  "GDAL_DAAS_API_KEY defined, but GDAL_DAAS_CLIENT_ID missing.");
     506           1 :         return false;
     507             :     }
     508             : 
     509           8 :     if (osAPIKey.empty())
     510             :     {
     511           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     512             :                  "GDAL_DAAS_CLIENT_ID defined, but GDAL_DAAS_API_KEY missing.");
     513           1 :         return false;
     514             :     }
     515             : 
     516          14 :     CPLString osPostContent;
     517           7 :     osPostContent += "client_id=" + URLEscape(osClientId);
     518           7 :     osPostContent += "&apikey=" + URLEscape(osAPIKey);
     519           7 :     osPostContent += "&grant_type=api_key";
     520             : 
     521           7 :     char **papszOptions = nullptr;
     522             :     papszOptions =
     523           7 :         CSLSetNameValue(papszOptions, "POSTFIELDS", osPostContent.c_str());
     524          14 :     CPLString osHeaders("Content-Type: application/x-www-form-urlencoded");
     525           7 :     papszOptions = CSLSetNameValue(papszOptions, "HEADERS", osHeaders.c_str());
     526             :     // FIXME for server side: make sure certificates are valid
     527           7 :     papszOptions = CSLSetNameValue(papszOptions, "UNSAFESSL", "YES");
     528           7 :     CPLHTTPResult *psResult = DAAS_CPLHTTPFetch(m_osAuthURL, papszOptions);
     529           7 :     CSLDestroy(papszOptions);
     530             : 
     531           7 :     if (psResult == nullptr)
     532             :     {
     533           0 :         return false;
     534             :     }
     535             : 
     536           7 :     if (psResult->pszErrBuf != nullptr)
     537             :     {
     538           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Get request %s failed: %s",
     539             :                  m_osAuthURL.c_str(),
     540           2 :                  psResult->pabyData ? CPLSPrintf("%s: %s", psResult->pszErrBuf,
     541             :                                                  reinterpret_cast<const char *>(
     542           1 :                                                      psResult->pabyData))
     543             :                                     : psResult->pszErrBuf);
     544           1 :         CPLHTTPDestroyResult(psResult);
     545           1 :         return false;
     546             :     }
     547             : 
     548           6 :     if (psResult->pabyData == nullptr)
     549             :     {
     550           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     551             :                  "Authorization request failed: "
     552             :                  "Empty content returned by server");
     553           0 :         CPLHTTPDestroyResult(psResult);
     554           0 :         return false;
     555             :     }
     556             : 
     557             :     CPLString osAuthorizationResponse(
     558          12 :         reinterpret_cast<char *>(psResult->pabyData));
     559           6 :     CPLHTTPDestroyResult(psResult);
     560             : 
     561          12 :     CPLJSONDocument oDoc;
     562           6 :     if (!oDoc.LoadMemory(osAuthorizationResponse))
     563             :     {
     564           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     565             :                  "Cannont parse GetAuthorization response");
     566           1 :         return false;
     567             :     }
     568             : 
     569           5 :     m_osAccessToken = oDoc.GetRoot().GetString("access_token");
     570           5 :     if (m_osAccessToken.empty())
     571             :     {
     572           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot retrieve access_token");
     573           1 :         return false;
     574             :     }
     575             : 
     576           4 :     int nExpiresIn = oDoc.GetRoot().GetInteger("expires_in");
     577           4 :     if (nExpiresIn > 0)
     578             :     {
     579           2 :         m_nExpirationTime = time(nullptr) + nExpiresIn - 60;
     580             :     }
     581             : 
     582           4 :     return true;
     583             : }
     584             : 
     585             : /************************************************************************/
     586             : /*                           GetObject()                                */
     587             : /************************************************************************/
     588             : 
     589         359 : static CPLJSONObject GetObject(const CPLJSONObject &oContainer,
     590             :                                const char *pszPath,
     591             :                                CPLJSONObject::Type eExpectedType,
     592             :                                const char *pszExpectedType, bool bVerboseError,
     593             :                                bool &bError)
     594             : {
     595         718 :     CPLJSONObject oObj = oContainer.GetObj(pszPath);
     596         359 :     if (!oObj.IsValid())
     597             :     {
     598         156 :         if (bVerboseError)
     599             :         {
     600           3 :             CPLError(CE_Failure, CPLE_AppDefined, "%s missing", pszPath);
     601             :         }
     602         156 :         bError = true;
     603         156 :         oObj.Deinit();
     604         156 :         return oObj;
     605             :     }
     606         203 :     if (oObj.GetType() != eExpectedType)
     607             :     {
     608           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s not %s", pszPath,
     609             :                  pszExpectedType);
     610           1 :         bError = true;
     611           1 :         oObj.Deinit();
     612           1 :         return oObj;
     613             :     }
     614         202 :     return oObj;
     615             : }
     616             : 
     617             : /************************************************************************/
     618             : /*                          GetInteger()                                */
     619             : /************************************************************************/
     620             : 
     621          84 : static int GetInteger(const CPLJSONObject &oContainer, const char *pszPath,
     622             :                       bool bVerboseError, bool &bError)
     623             : {
     624             :     CPLJSONObject oObj =
     625             :         GetObject(oContainer, pszPath, CPLJSONObject::Type::Integer,
     626         168 :                   "an integer", bVerboseError, bError);
     627          84 :     if (!oObj.IsValid())
     628             :     {
     629          29 :         return 0;
     630             :     }
     631          55 :     return oObj.ToInteger();
     632             : }
     633             : 
     634             : /************************************************************************/
     635             : /*                          GetDouble()                                */
     636             : /************************************************************************/
     637             : 
     638          92 : static double GetDouble(const CPLJSONObject &oContainer, const char *pszPath,
     639             :                         bool bVerboseError, bool &bError)
     640             : {
     641         276 :     CPLJSONObject oObj = oContainer.GetObj(pszPath);
     642          92 :     if (!oObj.IsValid())
     643             :     {
     644          66 :         if (bVerboseError)
     645             :         {
     646          10 :             CPLError(CE_Failure, CPLE_AppDefined, "%s missing", pszPath);
     647             :         }
     648          66 :         bError = true;
     649          66 :         return 0.0;
     650             :     }
     651          28 :     if (oObj.GetType() != CPLJSONObject::Type::Integer &&
     652           2 :         oObj.GetType() != CPLJSONObject::Type::Double)
     653             :     {
     654           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s not a double", pszPath);
     655           0 :         bError = true;
     656           0 :         return 0.0;
     657             :     }
     658          26 :     return oObj.ToDouble();
     659             : }
     660             : 
     661             : /************************************************************************/
     662             : /*                          GetString()                                 */
     663             : /************************************************************************/
     664             : 
     665         275 : static CPLString GetString(const CPLJSONObject &oContainer, const char *pszPath,
     666             :                            bool bVerboseError, bool &bError)
     667             : {
     668             :     CPLJSONObject oObj =
     669             :         GetObject(oContainer, pszPath, CPLJSONObject::Type::String, "a string",
     670         550 :                   bVerboseError, bError);
     671         275 :     if (!oObj.IsValid())
     672             :     {
     673         128 :         return CPLString();
     674             :     }
     675         294 :     return oObj.ToString();
     676             : }
     677             : 
     678             : /************************************************************************/
     679             : /*                     GetGDALDataTypeFromDAASPixelType()               */
     680             : /************************************************************************/
     681             : 
     682             : static GDALDataType
     683          46 : GetGDALDataTypeFromDAASPixelType(const CPLString &osPixelType)
     684             : {
     685             :     const struct
     686             :     {
     687             :         const char *pszName;
     688             :         GDALDataType eDT;
     689          46 :     } asDataTypes[] = {
     690             :         {"Byte", GDT_Byte},       {"UInt16", GDT_UInt16},
     691             :         {"Int16", GDT_Int16},     {"UInt32", GDT_UInt32},
     692             :         {"Int32", GDT_Int32},     {"Float32", GDT_Float32},
     693             :         {"Float64", GDT_Float64},
     694             :     };
     695             : 
     696          71 :     for (size_t i = 0; i < CPL_ARRAYSIZE(asDataTypes); ++i)
     697             :     {
     698          69 :         if (osPixelType == asDataTypes[i].pszName)
     699             :         {
     700          44 :             return asDataTypes[i].eDT;
     701             :         }
     702             :     }
     703           2 :     return GDT_Unknown;
     704             : }
     705             : 
     706             : /************************************************************************/
     707             : /*                         GetImageMetadata()                           */
     708             : /************************************************************************/
     709             : 
     710          39 : bool GDALDAASDataset::GetImageMetadata()
     711             : {
     712          39 :     char **papszOptions = GetHTTPOptions();
     713             :     CPLHTTPResult *psResult =
     714          39 :         DAAS_CPLHTTPFetch(m_osGetMetadataURL, papszOptions);
     715          39 :     CSLDestroy(papszOptions);
     716          39 :     if (psResult == nullptr)
     717           0 :         return false;
     718             : 
     719          39 :     if (psResult->pszErrBuf != nullptr)
     720             :     {
     721           8 :         CPLError(CE_Failure, CPLE_AppDefined, "Get request %s failed: %s",
     722             :                  m_osGetMetadataURL.c_str(),
     723           9 :                  psResult->pabyData ? CPLSPrintf("%s: %s", psResult->pszErrBuf,
     724             :                                                  reinterpret_cast<const char *>(
     725           1 :                                                      psResult->pabyData))
     726             :                                     : psResult->pszErrBuf);
     727           8 :         CPLHTTPDestroyResult(psResult);
     728           8 :         return false;
     729             :     }
     730             : 
     731          31 :     if (psResult->pabyData == nullptr)
     732             :     {
     733           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     734             :                  "Get request %s failed: "
     735             :                  "Empty content returned by server",
     736             :                  m_osGetMetadataURL.c_str());
     737           1 :         CPLHTTPDestroyResult(psResult);
     738           1 :         return false;
     739             :     }
     740             : 
     741          60 :     CPLString osResponse(reinterpret_cast<char *>(psResult->pabyData));
     742          30 :     CPLHTTPDestroyResult(psResult);
     743             : 
     744          60 :     CPLJSONDocument oDoc;
     745          30 :     CPLDebug("DAAS", "%s", osResponse.c_str());
     746          30 :     if (!oDoc.LoadMemory(osResponse))
     747             :     {
     748           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     749             :                  "Cannont parse GetImageMetadata response");
     750           1 :         return false;
     751             :     }
     752             : 
     753          29 :     CPLJSONObject oProperties = oDoc.GetRoot().GetObj(
     754          87 :         "response/payload/payload/imageMetadata/properties");
     755          29 :     if (!oProperties.IsValid())
     756             :     {
     757          11 :         oProperties = oDoc.GetRoot().GetObj("properties");
     758          11 :         if (!oProperties.IsValid())
     759             :         {
     760           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     761             :                      "Cannont find response/payload/payload/imageMetadata/"
     762             :                      "properties nor properties in GetImageMetadata response");
     763           1 :             return false;
     764             :         }
     765             :     }
     766             : 
     767          28 :     bool bError = false;
     768          28 :     nRasterXSize = GetInteger(oProperties, "width", true, bError);
     769          28 :     nRasterYSize = GetInteger(oProperties, "height", true, bError);
     770          28 :     if (!bError && !GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
     771             :     {
     772           1 :         bError = true;
     773             :     }
     774             : 
     775          28 :     bool bIgnoredError = false;
     776             : 
     777          28 :     m_nActualBitDepth =
     778          28 :         GetInteger(oProperties, "actualBitDepth", false, bIgnoredError);
     779             : 
     780          28 :     bool bNoDataError = false;
     781          28 :     m_dfNoDataValue =
     782          28 :         GetDouble(oProperties, "noDataValue", false, bNoDataError);
     783          28 :     m_bHasNoData = !bNoDataError;
     784             : 
     785          84 :     CPLJSONObject oGetBufferObj = oProperties.GetObj("_links/getBuffer");
     786          28 :     if (!oGetBufferObj.IsValid())
     787             :     {
     788           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s missing", "_links/getBuffer");
     789           1 :         bError = true;
     790             :     }
     791          56 :     CPLJSONObject oGetBufferDict;
     792          28 :     oGetBufferDict.Deinit();
     793          28 :     if (oGetBufferObj.GetType() == CPLJSONObject::Type::Array)
     794             :     {
     795           0 :         auto array = oGetBufferObj.ToArray();
     796           0 :         if (array.Size() > 0)
     797             :         {
     798           0 :             oGetBufferDict = array[0];
     799             :         }
     800             :     }
     801          28 :     else if (oGetBufferObj.GetType() == CPLJSONObject::Type::Object)
     802             :     {
     803          27 :         oGetBufferDict = oGetBufferObj;
     804             :     }
     805          28 :     CPL_IGNORE_RET_VAL(oGetBufferObj);
     806          28 :     if (!oGetBufferDict.IsValid())
     807             :     {
     808           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s missing",
     809             :                  "_links/getBuffer/href");
     810           1 :         bError = true;
     811             :     }
     812             :     else
     813             :     {
     814          27 :         m_osGetBufferURL = GetString(oGetBufferDict, "href", true, bError);
     815             :     }
     816             : 
     817             : #ifndef REMOVE_THAT_LEGACY_CODE
     818          28 :     if (!STARTS_WITH_CI(m_osGetMetadataURL, "https://192.168.") &&
     819          56 :         !STARTS_WITH_CI(m_osGetMetadataURL, "http://192.168.") &&
     820          28 :         STARTS_WITH_CI(m_osGetBufferURL, "http://192.168."))
     821             :     {
     822           0 :         size_t nPosDaas = m_osGetMetadataURL.find("/daas/");
     823           0 :         size_t nPosImages = m_osGetMetadataURL.find("/images/");
     824           0 :         if (nPosDaas != std::string::npos && nPosImages != std::string::npos)
     825             :         {
     826             :             m_osGetBufferURL =
     827           0 :                 m_osGetMetadataURL.substr(0, nPosDaas) + "/daas/images/" +
     828           0 :                 m_osGetMetadataURL.substr(nPosImages + strlen("/images/")) +
     829           0 :                 "/buffer";
     830             :         }
     831             :     }
     832             : #endif
     833             : 
     834          84 :     CPLJSONArray oGTArray = oProperties.GetArray("geotransform");
     835          28 :     if (oGTArray.IsValid() && oGTArray.Size() == 6)
     836             :     {
     837           3 :         m_bGotGeoTransform = true;
     838          21 :         for (int i = 0; i < 6; i++)
     839             :         {
     840          18 :             m_gt[i] = oGTArray[i].ToDouble();
     841             :         }
     842             :     }
     843             : 
     844          84 :     CPLJSONArray oBandArray = oProperties.GetArray("bands");
     845          28 :     if (!oBandArray.IsValid() || oBandArray.Size() == 0)
     846             :     {
     847           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing or empty bands array");
     848           1 :         bError = true;
     849             :     }
     850             :     else
     851             :     {
     852          69 :         for (int i = 0; i < oBandArray.Size(); ++i)
     853             :         {
     854          42 :             CPLJSONObject oBandObj = oBandArray[i];
     855          42 :             if (oBandObj.GetType() == CPLJSONObject::Type::Object)
     856             :             {
     857          42 :                 GDALDAASBandDesc oDesc;
     858          42 :                 oDesc.nIndex = i + 1;
     859          42 :                 oDesc.osName = GetString(oBandObj, "name", true, bError);
     860             :                 oDesc.osDescription =
     861          42 :                     GetString(oBandObj, "description", false, bIgnoredError);
     862          84 :                 oDesc.osColorInterp = GetString(oBandObj, "colorInterpretation",
     863          42 :                                                 false, bIgnoredError);
     864          42 :                 oDesc.bIsMask = oBandObj.GetBool("isMask");
     865             : 
     866             :                 const CPLString osPixelType(
     867          42 :                     GetString(oBandObj, "pixelType", true, bError));
     868          42 :                 oDesc.eDT = GetGDALDataTypeFromDAASPixelType(osPixelType);
     869          42 :                 if (oDesc.eDT == GDT_Unknown)
     870             :                 {
     871           2 :                     CPLError(CE_Failure, CPLE_NotSupported,
     872             :                              "Unsupported value pixelType = '%s'",
     873             :                              osPixelType.c_str());
     874           2 :                     bError = true;
     875             :                 }
     876          42 :                 if (i == 0)
     877             :                 {
     878          27 :                     m_eDT = oDesc.eDT;
     879             :                 }
     880             : 
     881          42 :                 if (!CPLFetchBool(m_papszOpenOptions, "MASKS", true) &&
     882           0 :                     oDesc.bIsMask)
     883             :                 {
     884           0 :                     continue;
     885             :                 }
     886          43 :                 if (oDesc.osColorInterp == "MAIN_MASK" &&
     887           1 :                     m_osMainMaskName.empty())
     888             :                 {
     889           1 :                     m_nMainMaskBandIndex = i + 1;
     890           1 :                     m_osMainMaskName = oDesc.osName;
     891             :                 }
     892             :                 else
     893             :                 {
     894          41 :                     m_aoBandDesc.push_back(std::move(oDesc));
     895             :                 }
     896             :             }
     897             :             else
     898             :             {
     899           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     900             :                          "Invalid bands[] element");
     901           0 :                 bError = true;
     902             :             }
     903             :         }
     904             :     }
     905             : 
     906          28 :     ReadSRS(oProperties);
     907             : 
     908          28 :     ReadRPCs(oProperties);
     909             : 
     910             :     // Collect other metadata
     911         183 :     for (const auto &oObj : oProperties.GetChildren())
     912             :     {
     913         310 :         const CPLString &osName(oObj.GetName());
     914         155 :         const auto &oType(oObj.GetType());
     915         465 :         if (osName != "aoiFactor" && osName != "crsCode" &&
     916         465 :             osName != "nbBands" && osName != "nbBits" && osName != "nBits" &&
     917         434 :             osName != "actualBitDepth" && osName != "width" &&
     918         322 :             osName != "height" && osName != "noDataValue" && osName != "step" &&
     919         194 :             osName != "pixelType" && oObj.IsValid() &&
     920          97 :             oType != CPLJSONObject::Type::Null &&
     921         377 :             oType != CPLJSONObject::Type::Array &&
     922          67 :             oType != CPLJSONObject::Type::Object)
     923             :         {
     924           8 :             SetMetadataItem(osName.c_str(), oObj.ToString().c_str());
     925             :         }
     926             :     }
     927             : 
     928             :     // Metadata for IMAGERY domain
     929             :     CPLString osAcquisitionDate(
     930          56 :         GetString(oProperties, "acquisitionDate", false, bIgnoredError));
     931          28 :     if (!osAcquisitionDate.empty())
     932             :     {
     933           2 :         int iYear = 0;
     934           2 :         int iMonth = 0;
     935           2 :         int iDay = 0;
     936           2 :         int iHours = 0;
     937           2 :         int iMin = 0;
     938           2 :         int iSec = 0;
     939             :         const int r =
     940           2 :             sscanf(osAcquisitionDate.c_str(), "%d-%d-%dT%d:%d:%d.%*dZ", &iYear,
     941             :                    &iMonth, &iDay, &iHours, &iMin, &iSec);
     942           2 :         if (r == 6)
     943             :         {
     944           2 :             SetMetadataItem(MD_NAME_ACQDATETIME,
     945             :                             CPLSPrintf("%04d-%02d-%02d %02d:%02d:%02d", iYear,
     946             :                                        iMonth, iDay, iHours, iMin, iSec),
     947           2 :                             MD_DOMAIN_IMAGERY);
     948             :         }
     949             :     }
     950             : 
     951          28 :     bIgnoredError = false;
     952             :     double dfCloudCover =
     953          28 :         GetDouble(oProperties, "cloudCover", false, bIgnoredError);
     954          28 :     if (!bIgnoredError)
     955             :     {
     956           2 :         SetMetadataItem(MD_NAME_CLOUDCOVER, CPLSPrintf("%.2f", dfCloudCover),
     957           2 :                         MD_DOMAIN_IMAGERY);
     958             :     }
     959             : 
     960             :     CPLString osSatellite(
     961          28 :         GetString(oProperties, "satellite", false, bIgnoredError));
     962          28 :     if (!osSatellite.empty())
     963             :     {
     964           2 :         SetMetadataItem(MD_NAME_SATELLITE, osSatellite.c_str(),
     965           2 :                         MD_DOMAIN_IMAGERY);
     966             :     }
     967             : 
     968          28 :     return !bError;
     969             : }
     970             : 
     971             : /************************************************************************/
     972             : /*                            ReadSRS()                                 */
     973             : /************************************************************************/
     974             : 
     975          28 : void GDALDAASDataset::ReadSRS(const CPLJSONObject &oProperties)
     976             : {
     977          84 :     CPLJSONArray oSRSArray = oProperties.GetArray("srsExpression/names");
     978          28 :     if (oSRSArray.IsValid())
     979             :     {
     980          12 :         for (int i = 0; i < oSRSArray.Size(); ++i)
     981             :         {
     982          20 :             CPLJSONObject oSRSObj = oSRSArray[i];
     983          10 :             if (oSRSObj.GetType() == CPLJSONObject::Type::Object)
     984             :             {
     985          10 :                 bool bError = false;
     986             :                 const std::string osType(
     987          20 :                     GetString(oSRSObj, "type", true, bError));
     988             :                 const std::string osValue(
     989          20 :                     GetString(oSRSObj, "value", true, bError));
     990             :                 // Use urn in priority
     991          10 :                 if (osType == "urn" && !osValue.empty())
     992             :                 {
     993           2 :                     m_osSRSType = osType;
     994           2 :                     m_osSRSValue = osValue;
     995             :                 }
     996             :                 // Use proj4 if urn not already set
     997          12 :                 else if (osType == "proj4" && !osValue.empty() &&
     998           4 :                          m_osSRSType != "urn")
     999             :                 {
    1000           2 :                     m_osSRSType = osType;
    1001           2 :                     m_osSRSValue = osValue;
    1002             :                 }
    1003             :                 // If no SRS set, take the first one
    1004           8 :                 else if (m_osSRSValue.empty() && !osType.empty() &&
    1005           2 :                          !osValue.empty())
    1006             :                 {
    1007           2 :                     m_osSRSType = osType;
    1008           2 :                     m_osSRSValue = osValue;
    1009             :                 }
    1010             :             }
    1011             :         }
    1012             :     }
    1013             :     else
    1014             :     {
    1015          78 :         auto osCrsCode = oProperties.GetString("crsCode");
    1016          26 :         if (!osCrsCode.empty())
    1017             :         {
    1018           0 :             m_osSRSType = "urn";
    1019           0 :             m_osSRSValue = osCrsCode;
    1020             :         }
    1021             :     }
    1022             : 
    1023          28 :     if (m_osSRSType == "urn" || m_osSRSType == "proj4")
    1024             :     {
    1025           2 :         m_oSRS.SetFromUserInput(
    1026             :             m_osSRSValue,
    1027             :             OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
    1028             :     }
    1029          28 : }
    1030             : 
    1031             : /************************************************************************/
    1032             : /*                            ReadRPCs()                                */
    1033             : /************************************************************************/
    1034             : 
    1035          28 : void GDALDAASDataset::ReadRPCs(const CPLJSONObject &oProperties)
    1036             : {
    1037          84 :     CPLJSONObject oRPC = oProperties.GetObj("rpc");
    1038          28 :     if (oRPC.IsValid())
    1039             :     {
    1040           3 :         bool bRPCError = false;
    1041           6 :         CPLStringList aoRPC;
    1042             : 
    1043             :         const struct
    1044             :         {
    1045             :             const char *pszJsonName;
    1046             :             const char *pszGDALName;
    1047           3 :         } asRPCSingleValues[] = {
    1048             :             {"errBias", RPC_ERR_BIAS},     {"errRand", RPC_ERR_RAND},
    1049             :             {"sampOff", RPC_SAMP_OFF},     {"lineOff", RPC_LINE_OFF},
    1050             :             {"latOff", RPC_LAT_OFF},       {"longOff", RPC_LONG_OFF},
    1051             :             {"heightOff", RPC_HEIGHT_OFF}, {"lineScale", RPC_LINE_SCALE},
    1052             :             {"sampScale", RPC_SAMP_SCALE}, {"latScale", RPC_LAT_SCALE},
    1053             :             {"longScale", RPC_LONG_SCALE}, {"heightScale", RPC_HEIGHT_SCALE},
    1054             :         };
    1055             : 
    1056          39 :         for (size_t i = 0; i < CPL_ARRAYSIZE(asRPCSingleValues); ++i)
    1057             :         {
    1058          36 :             bool bRPCErrorTmp = false;
    1059          36 :             const bool bVerboseError =
    1060          69 :                 !(strcmp(asRPCSingleValues[i].pszGDALName, RPC_ERR_BIAS) == 0 ||
    1061          33 :                   strcmp(asRPCSingleValues[i].pszGDALName, RPC_ERR_RAND) == 0);
    1062          36 :             double dfRPCVal = GetDouble(oRPC, asRPCSingleValues[i].pszJsonName,
    1063             :                                         bVerboseError, bRPCErrorTmp);
    1064          36 :             if (bRPCErrorTmp)
    1065             :             {
    1066          14 :                 if (bVerboseError)
    1067             :                 {
    1068          10 :                     bRPCError = true;
    1069             :                 }
    1070          14 :                 continue;
    1071             :             }
    1072          22 :             aoRPC.SetNameValue(asRPCSingleValues[i].pszGDALName,
    1073          22 :                                CPLSPrintf("%.17g", dfRPCVal));
    1074             :         }
    1075             : 
    1076             :         const struct
    1077             :         {
    1078             :             const char *pszJsonName;
    1079             :             const char *pszGDALName;
    1080           3 :         } asRPCArrayValues[] = {
    1081             :             {"lineNumCoeff", RPC_LINE_NUM_COEFF},
    1082             :             {"lineDenCoeff", RPC_LINE_DEN_COEFF},
    1083             :             {"sampNumCoeff", RPC_SAMP_NUM_COEFF},
    1084             :             {"sampDenCoeff", RPC_SAMP_DEN_COEFF},
    1085             :         };
    1086             : 
    1087          15 :         for (size_t i = 0; i < CPL_ARRAYSIZE(asRPCArrayValues); ++i)
    1088             :         {
    1089             :             CPLJSONArray oRPCArray =
    1090          36 :                 oRPC.GetArray(asRPCArrayValues[i].pszJsonName);
    1091          12 :             if (oRPCArray.IsValid() && oRPCArray.Size() == 20)
    1092             :             {
    1093          16 :                 CPLString osVal;
    1094         168 :                 for (int j = 0; j < 20; j++)
    1095             :                 {
    1096         160 :                     if (j > 0)
    1097         152 :                         osVal += " ";
    1098         160 :                     osVal += CPLSPrintf("%.17g", oRPCArray[j].ToDouble());
    1099             :                 }
    1100           8 :                 aoRPC.SetNameValue(asRPCArrayValues[i].pszGDALName,
    1101           8 :                                    osVal.c_str());
    1102             :             }
    1103             :             else
    1104             :             {
    1105           4 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    1106           4 :                          asRPCArrayValues[i].pszJsonName);
    1107             :             }
    1108             :         }
    1109           3 :         if (!bRPCError)
    1110             :         {
    1111           2 :             SetMetadata(aoRPC.List(), "RPC");
    1112             :         }
    1113             :     }
    1114          28 : }
    1115             : 
    1116             : /************************************************************************/
    1117             : /*                      SetupServerSideReprojection()                   */
    1118             : /************************************************************************/
    1119             : 
    1120           0 : bool GDALDAASDataset::SetupServerSideReprojection(const char *pszTargetSRS)
    1121             : {
    1122           0 :     if (m_oSRS.IsEmpty() || !m_bGotGeoTransform)
    1123             :     {
    1124           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1125             :                  "TARGET_SRS is specified, but projection and/or "
    1126             :                  "geotransform are missing in image metadata");
    1127           0 :         return false;
    1128             :     }
    1129             : 
    1130           0 :     OGRSpatialReference oSRS;
    1131           0 :     if (oSRS.SetFromUserInput(
    1132             :             pszTargetSRS,
    1133           0 :             OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
    1134             :         OGRERR_NONE)
    1135             :     {
    1136           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid TARGET_SRS value");
    1137           0 :         return false;
    1138             :     }
    1139             : 
    1140             :     // Check that we can find the EPSG code as we will need to
    1141             :     // provide as a urn to getBuffer
    1142           0 :     const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
    1143           0 :     const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
    1144           0 :     if (pszAuthorityName == nullptr || !EQUAL(pszAuthorityName, "EPSG") ||
    1145             :         pszAuthorityCode == nullptr)
    1146             :     {
    1147           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1148             :                  "TARGET_SRS cannot be identified to a EPSG code");
    1149           0 :         return false;
    1150             :     }
    1151             : 
    1152           0 :     CPLString osTargetEPSGCode = CPLString("epsg:") + pszAuthorityCode;
    1153             : 
    1154           0 :     char *pszWKT = nullptr;
    1155           0 :     oSRS.exportToWkt(&pszWKT);
    1156           0 :     char **papszTO = CSLSetNameValue(nullptr, "DST_SRS", pszWKT);
    1157           0 :     CPLFree(pszWKT);
    1158             : 
    1159             :     void *hTransformArg =
    1160           0 :         GDALCreateGenImgProjTransformer2(this, nullptr, papszTO);
    1161           0 :     if (hTransformArg == nullptr)
    1162             :     {
    1163           0 :         CSLDestroy(papszTO);
    1164           0 :         return false;
    1165             :     }
    1166             : 
    1167           0 :     GDALTransformerInfo *psInfo =
    1168             :         static_cast<GDALTransformerInfo *>(hTransformArg);
    1169             :     double adfExtent[4];
    1170             :     int nXSize, nYSize;
    1171             : 
    1172           0 :     if (GDALSuggestedWarpOutput2(this, psInfo->pfnTransform, hTransformArg,
    1173             :                                  m_gt.data(), &nXSize, &nYSize, adfExtent,
    1174           0 :                                  0) != CE_None)
    1175             :     {
    1176           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1177             :                  "Cannot find extent in specified TARGET_SRS");
    1178           0 :         CSLDestroy(papszTO);
    1179           0 :         GDALDestroyGenImgProjTransformer(hTransformArg);
    1180           0 :         return false;
    1181             :     }
    1182             : 
    1183           0 :     GDALDestroyGenImgProjTransformer(hTransformArg);
    1184             : 
    1185           0 :     m_bRequestInGeoreferencedCoordinates = true;
    1186           0 :     m_osSRSType = "epsg";
    1187           0 :     m_osSRSValue = std::move(osTargetEPSGCode);
    1188           0 :     m_oSRS = std::move(oSRS);
    1189           0 :     nRasterXSize = nXSize;
    1190           0 :     nRasterYSize = nYSize;
    1191           0 :     return true;
    1192             : }
    1193             : 
    1194             : /************************************************************************/
    1195             : /*                              Open()                                  */
    1196             : /************************************************************************/
    1197             : 
    1198          45 : bool GDALDAASDataset::Open(GDALOpenInfo *poOpenInfo)
    1199             : {
    1200          45 :     m_papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
    1201             :     m_osGetMetadataURL =
    1202          45 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "GET_METADATA_URL",
    1203          45 :                              poOpenInfo->pszFilename + strlen("DAAS:"));
    1204          45 :     if (m_osGetMetadataURL.empty())
    1205             :     {
    1206           1 :         CPLError(CE_Failure, CPLE_AppDefined, "GET_METADATA_URL is missing");
    1207           1 :         return false;
    1208             :     }
    1209          44 :     m_nBlockSize =
    1210          44 :         std::max(knMIN_BLOCKSIZE,
    1211             :                  std::min(knMAX_BLOCKSIZE,
    1212          44 :                           atoi(CSLFetchNameValueDef(
    1213          44 :                               poOpenInfo->papszOpenOptions, "BLOCK_SIZE",
    1214          44 :                               CPLSPrintf("%d", m_nBlockSize)))));
    1215          44 :     m_nServerByteLimit =
    1216          44 :         atoi(CPLGetConfigOption("GDAL_DAAS_SERVER_BYTE_LIMIT",
    1217             :                                 CPLSPrintf("%d", knDEFAULT_SERVER_BYTE_LIMIT)));
    1218             : 
    1219          53 :     if (CPLTestBool(CPLGetConfigOption("GDAL_DAAS_PERFORM_AUTH", "YES")) &&
    1220           9 :         !GetAuthorization())
    1221           5 :         return false;
    1222          39 :     if (!GetImageMetadata())
    1223          19 :         return false;
    1224             : 
    1225          20 :     const char *pszFormat = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
    1226             :                                                  "PIXEL_ENCODING", "AUTO");
    1227          20 :     if (EQUAL(pszFormat, "AUTO"))
    1228             :     {
    1229           9 :         if ((m_aoBandDesc.size() == 1 || m_aoBandDesc.size() == 3 ||
    1230          14 :              m_aoBandDesc.size() == 4) &&
    1231           7 :             m_eDT == GDT_Byte)
    1232             :         {
    1233           7 :             m_eFormat = Format::PNG;
    1234             :         }
    1235             :         else
    1236             :         {
    1237           0 :             m_eFormat = Format::RAW;
    1238             :         }
    1239             :     }
    1240          13 :     else if (EQUAL(pszFormat, "RAW"))
    1241             :     {
    1242           6 :         m_eFormat = Format::RAW;
    1243             :     }
    1244           7 :     else if (EQUAL(pszFormat, "PNG"))
    1245             :     {
    1246           3 :         if ((m_aoBandDesc.size() == 1 || m_aoBandDesc.size() == 3 ||
    1247           4 :              m_aoBandDesc.size() == 4) &&
    1248           2 :             m_eDT == GDT_Byte)
    1249             :         {
    1250           1 :             m_eFormat = Format::PNG;
    1251             :         }
    1252             :         else
    1253             :         {
    1254           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    1255             :                      "PNG only supported for 1, 3 or 4-band Byte dataset. "
    1256             :                      "Falling back to RAW");
    1257           1 :             m_eFormat = Format::RAW;
    1258             :         }
    1259             :     }
    1260           5 :     else if (EQUAL(pszFormat, "JPEG"))
    1261             :     {
    1262           4 :         if ((m_aoBandDesc.size() == 1 || m_aoBandDesc.size() == 3) &&
    1263           2 :             m_eDT == GDT_Byte)
    1264             :         {
    1265           1 :             m_eFormat = Format::JPEG;
    1266             :         }
    1267             :         else
    1268             :         {
    1269           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    1270             :                      "JPEG only supported for 1 or 3-band Byte dataset. "
    1271             :                      "Falling back to RAW");
    1272           1 :             m_eFormat = Format::RAW;
    1273             :         }
    1274             :     }
    1275           3 :     else if (EQUAL(pszFormat, "JPEG2000"))
    1276             :     {
    1277           2 :         if (m_eDT != GDT_Float32 && m_eDT != GDT_Float64)
    1278             :         {
    1279           1 :             m_eFormat = Format::JPEG2000;
    1280             :         }
    1281             :         else
    1282             :         {
    1283           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    1284             :                      "JPEG2000 only supported for integer datatype dataset. "
    1285             :                      "Falling back to RAW");
    1286           1 :             m_eFormat = Format::RAW;
    1287             :         }
    1288             :     }
    1289             :     else
    1290             :     {
    1291           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported PIXEL_ENCODING=%s",
    1292             :                  pszFormat);
    1293           1 :         return false;
    1294             :     }
    1295             : 
    1296             :     const char *pszTargetSRS =
    1297          19 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TARGET_SRS");
    1298          19 :     if (pszTargetSRS)
    1299             :     {
    1300           0 :         if (!SetupServerSideReprojection(pszTargetSRS))
    1301             :         {
    1302           0 :             return false;
    1303             :         }
    1304             :     }
    1305             : 
    1306          19 :     InstantiateBands();
    1307             : 
    1308             :     // Instantiate overviews
    1309          19 :     int iOvr = 0;
    1310          25 :     while ((nRasterXSize >> iOvr) > 256 || (nRasterYSize >> iOvr) > 256)
    1311             :     {
    1312           6 :         iOvr++;
    1313           6 :         if ((nRasterXSize >> iOvr) == 0 || (nRasterYSize >> iOvr) == 0)
    1314             :         {
    1315             :             break;
    1316             :         }
    1317           6 :         m_apoOverviewDS.push_back(
    1318          12 :             std::make_unique<GDALDAASDataset>(this, iOvr));
    1319             :     }
    1320             : 
    1321          19 :     return true;
    1322             : }
    1323             : 
    1324          45 : GDALDataset *GDALDAASDataset::OpenStatic(GDALOpenInfo *poOpenInfo)
    1325             : {
    1326          45 :     if (!Identify(poOpenInfo))
    1327           0 :         return nullptr;
    1328             : 
    1329          90 :     auto poDS = std::make_unique<GDALDAASDataset>();
    1330          45 :     if (!poDS->Open(poOpenInfo))
    1331          26 :         return nullptr;
    1332          19 :     return poDS.release();
    1333             : }
    1334             : 
    1335             : /************************************************************************/
    1336             : /*                       GDALDAASRasterBand()                          */
    1337             : /************************************************************************/
    1338             : 
    1339          40 : GDALDAASRasterBand::GDALDAASRasterBand(GDALDAASDataset *poDSIn, int nBandIn,
    1340          40 :                                        const GDALDAASBandDesc &oBandDesc)
    1341             : {
    1342          40 :     poDS = poDSIn;
    1343          40 :     nBand = nBandIn;
    1344          40 :     eDataType = poDSIn->m_eDT;
    1345          40 :     nRasterXSize = poDSIn->GetRasterXSize();
    1346          40 :     nRasterYSize = poDSIn->GetRasterYSize();
    1347          40 :     nBlockXSize = poDSIn->m_nBlockSize;
    1348          40 :     nBlockYSize = poDSIn->m_nBlockSize;
    1349          40 :     m_nSrcIndex = oBandDesc.nIndex;
    1350             : 
    1351          40 :     SetDescription(oBandDesc.osName);
    1352          40 :     if (!oBandDesc.osDescription.empty())
    1353             :     {
    1354           4 :         SetMetadataItem("DESCRIPTION", oBandDesc.osDescription);
    1355             :     }
    1356             : 
    1357             :     const struct
    1358             :     {
    1359             :         const char *pszName;
    1360             :         GDALColorInterp eColorInterp;
    1361          40 :     } asColorInterpretations[] = {
    1362             :         {"RED", GCI_RedBand},     {"GREEN", GCI_GreenBand},
    1363             :         {"BLUE", GCI_BlueBand},   {"GRAY", GCI_GrayIndex},
    1364             :         {"ALPHA", GCI_AlphaBand}, {"UNDEFINED", GCI_Undefined},
    1365             :     };
    1366             : 
    1367         268 :     for (size_t i = 0; i < CPL_ARRAYSIZE(asColorInterpretations); ++i)
    1368             :     {
    1369         232 :         if (EQUAL(oBandDesc.osColorInterp, asColorInterpretations[i].pszName))
    1370             :         {
    1371           4 :             m_eColorInterp = asColorInterpretations[i].eColorInterp;
    1372           4 :             break;
    1373             :         }
    1374             :     }
    1375          40 :     if (!oBandDesc.osColorInterp.empty() &&
    1376          44 :         !EQUAL(oBandDesc.osColorInterp, "UNDEFINED") &&
    1377           4 :         m_eColorInterp != GCI_Undefined)
    1378             :     {
    1379           4 :         SetMetadataItem("COLOR_INTERPRETATION", oBandDesc.osColorInterp);
    1380             :     }
    1381             : 
    1382          40 :     if (poDSIn->m_nActualBitDepth != 0 && poDSIn->m_nActualBitDepth != 8 &&
    1383           2 :         poDSIn->m_nActualBitDepth != 16 && poDSIn->m_nActualBitDepth != 32 &&
    1384           2 :         poDSIn->m_nActualBitDepth != 64)
    1385             :     {
    1386           2 :         SetMetadataItem("NBITS", CPLSPrintf("%d", poDSIn->m_nActualBitDepth),
    1387             :                         "IMAGE_STRUCTURE");
    1388             :     }
    1389          40 : }
    1390             : 
    1391             : /************************************************************************/
    1392             : /*                         GetNoDataValue()                             */
    1393             : /************************************************************************/
    1394             : 
    1395           2 : double GDALDAASRasterBand::GetNoDataValue(int *pbHasNoData)
    1396             : {
    1397           2 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1398           2 :     if (poGDS->m_bHasNoData)
    1399             :     {
    1400           1 :         if (pbHasNoData)
    1401           1 :             *pbHasNoData = true;
    1402           1 :         return poGDS->m_dfNoDataValue;
    1403             :     }
    1404           1 :     if (pbHasNoData)
    1405           1 :         *pbHasNoData = false;
    1406           1 :     return 0.0;
    1407             : }
    1408             : 
    1409             : /************************************************************************/
    1410             : /*                       GetColorInterpretation()                       */
    1411             : /************************************************************************/
    1412             : 
    1413           1 : GDALColorInterp GDALDAASRasterBand::GetColorInterpretation()
    1414             : {
    1415           1 :     return m_eColorInterp;
    1416             : }
    1417             : 
    1418             : /************************************************************************/
    1419             : /*                            GetMaskBand()                             */
    1420             : /************************************************************************/
    1421             : 
    1422           3 : GDALRasterBand *GDALDAASRasterBand::GetMaskBand()
    1423             : {
    1424           3 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1425           3 :     if (poGDS->m_poMaskBand)
    1426           3 :         return poGDS->m_poMaskBand;
    1427           0 :     return GDALRasterBand::GetMaskBand();
    1428             : }
    1429             : 
    1430             : /************************************************************************/
    1431             : /*                           GetMaskFlags()                             */
    1432             : /************************************************************************/
    1433             : 
    1434           1 : int GDALDAASRasterBand::GetMaskFlags()
    1435             : {
    1436           1 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1437           1 :     if (poGDS->m_poMaskBand)
    1438           1 :         return GMF_PER_DATASET;
    1439           0 :     return GDALRasterBand::GetMaskFlags();
    1440             : }
    1441             : 
    1442             : /************************************************************************/
    1443             : /*                      CanSpatiallySplit()                             */
    1444             : /************************************************************************/
    1445             : 
    1446          32 : static bool CanSpatiallySplit(GUInt32 nRetryFlags, int nXOff, int nYOff,
    1447             :                               int nXSize, int nYSize, int nBufXSize,
    1448             :                               int nBufYSize, int nBlockXSize, int nBlockYSize,
    1449             :                               GSpacing nPixelSpace, GSpacing nLineSpace,
    1450             :                               int &nXOff1, int &nYOff1, int &nXSize1,
    1451             :                               int &nYSize1, int &nXOff2, int &nYOff2,
    1452             :                               int &nXSize2, int &nYSize2, GSpacing &nDataShift2)
    1453             : {
    1454          32 :     if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
    1455           1 :         nYSize == nBufYSize && nYSize > nBlockYSize)
    1456             :     {
    1457             :         int nHalf =
    1458           0 :             std::max(nBlockYSize, ((nYSize / 2) / nBlockYSize) * nBlockYSize);
    1459           0 :         nXOff1 = nXOff;
    1460           0 :         nYOff1 = nYOff;
    1461           0 :         nXSize1 = nXSize;
    1462           0 :         nYSize1 = nHalf;
    1463           0 :         nXOff2 = nXOff;
    1464           0 :         nYOff2 = nYOff + nHalf;
    1465           0 :         nXSize2 = nXSize;
    1466           0 :         nYSize2 = nYSize - nHalf;
    1467           0 :         nDataShift2 = nHalf * nLineSpace;
    1468           0 :         return true;
    1469             :     }
    1470          32 :     else if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
    1471           1 :              nYSize == nBufYSize && nXSize > nBlockXSize)
    1472             :     {
    1473             :         int nHalf =
    1474           1 :             std::max(nBlockXSize, ((nXSize / 2) / nBlockXSize) * nBlockXSize);
    1475           1 :         nXOff1 = nXOff;
    1476           1 :         nYOff1 = nYOff;
    1477           1 :         nXSize1 = nHalf;
    1478           1 :         nYSize1 = nYSize;
    1479           1 :         nXOff2 = nXOff + nHalf;
    1480           1 :         nYOff2 = nYOff;
    1481           1 :         nXSize2 = nXSize - nHalf;
    1482           1 :         nYSize2 = nYSize;
    1483           1 :         nDataShift2 = nHalf * nPixelSpace;
    1484           1 :         return true;
    1485             :     }
    1486          31 :     return false;
    1487             : }
    1488             : 
    1489             : /************************************************************************/
    1490             : /*                           IRasterIO()                                */
    1491             : /************************************************************************/
    1492             : 
    1493           4 : CPLErr GDALDAASDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    1494             :                                   int nXSize, int nYSize, void *pData,
    1495             :                                   int nBufXSize, int nBufYSize,
    1496             :                                   GDALDataType eBufType, int nBandCount,
    1497             :                                   BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
    1498             :                                   GSpacing nLineSpace, GSpacing nBandSpace,
    1499             :                                   GDALRasterIOExtraArg *psExtraArg)
    1500             : {
    1501           4 :     m_eCurrentResampleAlg = psExtraArg->eResampleAlg;
    1502             : 
    1503             :     /* ==================================================================== */
    1504             :     /*      Do we have overviews that would be appropriate to satisfy       */
    1505             :     /*      this request?                                                   */
    1506             :     /* ==================================================================== */
    1507           4 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) &&
    1508           8 :         GetRasterBand(1)->GetOverviewCount() > 0 && eRWFlag == GF_Read)
    1509             :     {
    1510             :         GDALRasterIOExtraArg sExtraArg;
    1511           0 :         GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
    1512             : 
    1513           0 :         const int nOverview = GDALBandGetBestOverviewLevel2(
    1514             :             GetRasterBand(1), nXOff, nYOff, nXSize, nYSize, nBufXSize,
    1515             :             nBufYSize, &sExtraArg);
    1516           0 :         if (nOverview >= 0)
    1517             :         {
    1518             :             GDALRasterBand *poOverviewBand =
    1519           0 :                 GetRasterBand(1)->GetOverview(nOverview);
    1520           0 :             if (poOverviewBand == nullptr ||
    1521           0 :                 poOverviewBand->GetDataset() == nullptr)
    1522             :             {
    1523           0 :                 return CE_Failure;
    1524             :             }
    1525             : 
    1526           0 :             return poOverviewBand->GetDataset()->RasterIO(
    1527             :                 eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
    1528             :                 nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
    1529           0 :                 nLineSpace, nBandSpace, &sExtraArg);
    1530             :         }
    1531             :     }
    1532             : 
    1533             :     GDALDAASRasterBand *poBand =
    1534           4 :         cpl::down_cast<GDALDAASRasterBand *>(GetRasterBand(1));
    1535             : 
    1536           8 :     std::vector<int> anRequestedBands;
    1537           4 :     if (m_poMaskBand)
    1538           1 :         anRequestedBands.push_back(0);
    1539           8 :     for (int i = 1; i <= GetRasterCount(); i++)
    1540           4 :         anRequestedBands.push_back(i);
    1541             :     GUInt32 nRetryFlags =
    1542           4 :         poBand->PrefetchBlocks(nXOff, nYOff, nXSize, nYSize, anRequestedBands);
    1543             :     int nBlockXSize, nBlockYSize;
    1544           4 :     poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1545           4 :     int nXOff1 = 0;
    1546           4 :     int nYOff1 = 0;
    1547           4 :     int nXSize1 = 0;
    1548           4 :     int nYSize1 = 0;
    1549           4 :     int nXOff2 = 0;
    1550           4 :     int nYOff2 = 0;
    1551           4 :     int nXSize2 = 0;
    1552           4 :     int nYSize2 = 0;
    1553           4 :     GSpacing nDataShift2 = 0;
    1554           4 :     if (CanSpatiallySplit(nRetryFlags, nXOff, nYOff, nXSize, nYSize, nBufXSize,
    1555             :                           nBufYSize, nBlockXSize, nBlockYSize, nPixelSpace,
    1556             :                           nLineSpace, nXOff1, nYOff1, nXSize1, nYSize1, nXOff2,
    1557             :                           nYOff2, nXSize2, nYSize2, nDataShift2))
    1558             :     {
    1559             :         GDALRasterIOExtraArg sExtraArg;
    1560           0 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1561             : 
    1562             :         CPLErr eErr =
    1563           0 :             IRasterIO(eRWFlag, nXOff1, nYOff1, nXSize1, nYSize1, pData, nXSize1,
    1564             :                       nYSize1, eBufType, nBandCount, panBandMap, nPixelSpace,
    1565             :                       nLineSpace, nBandSpace, &sExtraArg);
    1566           0 :         if (eErr == CE_None)
    1567             :         {
    1568           0 :             eErr = IRasterIO(eRWFlag, nXOff2, nYOff2, nXSize2, nYSize2,
    1569           0 :                              static_cast<GByte *>(pData) + nDataShift2, nXSize2,
    1570             :                              nYSize2, eBufType, nBandCount, panBandMap,
    1571             :                              nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
    1572             :         }
    1573           0 :         return eErr;
    1574             :     }
    1575           4 :     else if ((nRetryFlags & RETRY_PER_BAND) && nBands > 1)
    1576             :     {
    1577           0 :         for (int iBand = 1; iBand <= nBands; iBand++)
    1578             :         {
    1579           0 :             poBand = cpl::down_cast<GDALDAASRasterBand *>(GetRasterBand(iBand));
    1580           0 :             CPL_IGNORE_RET_VAL(poBand->PrefetchBlocks(
    1581           0 :                 nXOff, nYOff, nXSize, nYSize, std::vector<int>{iBand}));
    1582             :         }
    1583             :     }
    1584             : 
    1585           4 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1586             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
    1587             :                                   panBandMap, nPixelSpace, nLineSpace,
    1588           4 :                                   nBandSpace, psExtraArg);
    1589             : }
    1590             : 
    1591             : /************************************************************************/
    1592             : /*                          AdviseRead()                                */
    1593             : /************************************************************************/
    1594             : 
    1595           1 : CPLErr GDALDAASDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
    1596             :                                    int nBufXSize, int nBufYSize,
    1597             :                                    GDALDataType /* eBufType */, int /*nBands*/,
    1598             :                                    int * /*panBands*/,
    1599             :                                    char ** /* papszOptions */)
    1600             : {
    1601           1 :     if (nXSize == nBufXSize && nYSize == nBufYSize)
    1602             :     {
    1603           1 :         m_nXOffAdvise = nXOff;
    1604           1 :         m_nYOffAdvise = nYOff;
    1605           1 :         m_nXSizeAdvise = nXSize;
    1606           1 :         m_nYSizeAdvise = nYSize;
    1607             :     }
    1608           1 :     return CE_None;
    1609             : }
    1610             : 
    1611             : /************************************************************************/
    1612             : /*                          FlushCache()                                */
    1613             : /************************************************************************/
    1614             : 
    1615          10 : CPLErr GDALDAASDataset::FlushCache(bool bAtClosing)
    1616             : {
    1617          10 :     CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
    1618          10 :     m_nXOffFetched = 0;
    1619          10 :     m_nYOffFetched = 0;
    1620          10 :     m_nXSizeFetched = 0;
    1621          10 :     m_nYSizeFetched = 0;
    1622          10 :     return eErr;
    1623             : }
    1624             : 
    1625             : /************************************************************************/
    1626             : /*                           GetOverviewCount()                         */
    1627             : /************************************************************************/
    1628             : 
    1629           4 : int GDALDAASRasterBand::GetOverviewCount()
    1630             : {
    1631           4 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1632           4 :     return static_cast<int>(poGDS->m_apoOverviewDS.size());
    1633             : }
    1634             : 
    1635             : /************************************************************************/
    1636             : /*                              GetOverview()                           */
    1637             : /************************************************************************/
    1638             : 
    1639           5 : GDALRasterBand *GDALDAASRasterBand::GetOverview(int iIndex)
    1640             : {
    1641           5 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1642           5 :     if (iIndex >= 0 && iIndex < static_cast<int>(poGDS->m_apoOverviewDS.size()))
    1643             :     {
    1644           3 :         return poGDS->m_apoOverviewDS[iIndex]->GetRasterBand(nBand);
    1645             :     }
    1646           2 :     return nullptr;
    1647             : }
    1648             : 
    1649             : /************************************************************************/
    1650             : /*                          IReadBlock()                                */
    1651             : /************************************************************************/
    1652             : 
    1653          11 : CPLErr GDALDAASRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1654             :                                       void *pImage)
    1655             : {
    1656          22 :     return GetBlocks(nBlockXOff, nBlockYOff, 1, 1, std::vector<int>{nBand},
    1657          22 :                      pImage);
    1658             : }
    1659             : 
    1660             : /************************************************************************/
    1661             : /*                           IRasterIO()                                */
    1662             : /************************************************************************/
    1663             : 
    1664          29 : CPLErr GDALDAASRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    1665             :                                      int nXSize, int nYSize, void *pData,
    1666             :                                      int nBufXSize, int nBufYSize,
    1667             :                                      GDALDataType eBufType,
    1668             :                                      GSpacing nPixelSpace, GSpacing nLineSpace,
    1669             :                                      GDALRasterIOExtraArg *psExtraArg)
    1670             : {
    1671          29 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1672             : 
    1673          29 :     poGDS->m_eCurrentResampleAlg = psExtraArg->eResampleAlg;
    1674             : 
    1675             :     /* ==================================================================== */
    1676             :     /*      Do we have overviews that would be appropriate to satisfy       */
    1677             :     /*      this request?                                                   */
    1678             :     /* ==================================================================== */
    1679          29 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0 &&
    1680             :         eRWFlag == GF_Read)
    1681             :     {
    1682             :         GDALRasterIOExtraArg sExtraArg;
    1683           1 :         GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
    1684             : 
    1685             :         const int nOverview =
    1686           1 :             GDALBandGetBestOverviewLevel2(this, nXOff, nYOff, nXSize, nYSize,
    1687             :                                           nBufXSize, nBufYSize, &sExtraArg);
    1688           1 :         if (nOverview >= 0)
    1689             :         {
    1690           1 :             GDALRasterBand *poOverviewBand = GetOverview(nOverview);
    1691           1 :             if (poOverviewBand == nullptr)
    1692           1 :                 return CE_Failure;
    1693             : 
    1694           1 :             return poOverviewBand->RasterIO(
    1695             :                 eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
    1696           1 :                 nBufYSize, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
    1697             :         }
    1698             :     }
    1699             : 
    1700          56 :     std::vector<int> anRequestedBands;
    1701          28 :     if (poGDS->m_poMaskBand)
    1702           3 :         anRequestedBands.push_back(0);
    1703          88 :     for (int i = 1; i <= poGDS->GetRasterCount(); i++)
    1704          60 :         anRequestedBands.push_back(i);
    1705             :     GUInt32 nRetryFlags =
    1706          28 :         PrefetchBlocks(nXOff, nYOff, nXSize, nYSize, anRequestedBands);
    1707          28 :     int nXOff1 = 0;
    1708          28 :     int nYOff1 = 0;
    1709          28 :     int nXSize1 = 0;
    1710          28 :     int nYSize1 = 0;
    1711          28 :     int nXOff2 = 0;
    1712          28 :     int nYOff2 = 0;
    1713          28 :     int nXSize2 = 0;
    1714          28 :     int nYSize2 = 0;
    1715          28 :     GSpacing nDataShift2 = 0;
    1716          28 :     if (CanSpatiallySplit(nRetryFlags, nXOff, nYOff, nXSize, nYSize, nBufXSize,
    1717             :                           nBufYSize, nBlockXSize, nBlockYSize, nPixelSpace,
    1718             :                           nLineSpace, nXOff1, nYOff1, nXSize1, nYSize1, nXOff2,
    1719             :                           nYOff2, nXSize2, nYSize2, nDataShift2))
    1720             :     {
    1721             :         GDALRasterIOExtraArg sExtraArg;
    1722           1 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1723             : 
    1724             :         CPLErr eErr =
    1725           1 :             IRasterIO(eRWFlag, nXOff1, nYOff1, nXSize1, nYSize1, pData, nXSize1,
    1726             :                       nYSize1, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
    1727           1 :         if (eErr == CE_None)
    1728             :         {
    1729           1 :             eErr = IRasterIO(eRWFlag, nXOff2, nYOff2, nXSize2, nYSize2,
    1730           1 :                              static_cast<GByte *>(pData) + nDataShift2, nXSize2,
    1731             :                              nYSize2, eBufType, nPixelSpace, nLineSpace,
    1732             :                              &sExtraArg);
    1733             :         }
    1734           1 :         return eErr;
    1735             :     }
    1736          27 :     else if ((nRetryFlags & RETRY_PER_BAND) && poGDS->nBands > 1)
    1737             :     {
    1738           0 :         CPL_IGNORE_RET_VAL(PrefetchBlocks(nXOff, nYOff, nXSize, nYSize,
    1739           0 :                                           std::vector<int>{nBand}));
    1740             :     }
    1741             : 
    1742          27 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1743             :                                      pData, nBufXSize, nBufYSize, eBufType,
    1744          27 :                                      nPixelSpace, nLineSpace, psExtraArg);
    1745             : }
    1746             : 
    1747             : /************************************************************************/
    1748             : /*                          AdviseRead()                                */
    1749             : /************************************************************************/
    1750             : 
    1751           2 : CPLErr GDALDAASRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize,
    1752             :                                       int nYSize, int nBufXSize, int nBufYSize,
    1753             :                                       GDALDataType /* eBufType */,
    1754             :                                       char ** /* papszOptions */)
    1755             : {
    1756           2 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1757           2 :     if (nXSize == nBufXSize && nYSize == nBufYSize)
    1758             :     {
    1759           2 :         poGDS->m_nXOffAdvise = nXOff;
    1760           2 :         poGDS->m_nYOffAdvise = nYOff;
    1761           2 :         poGDS->m_nXSizeAdvise = nXSize;
    1762           2 :         poGDS->m_nYSizeAdvise = nYSize;
    1763             :     }
    1764           2 :     return CE_None;
    1765             : }
    1766             : 
    1767             : /************************************************************************/
    1768             : /*                          PrefetchBlocks()                            */
    1769             : /************************************************************************/
    1770             : 
    1771             : // Return or'ed flags among 0, RETRY_PER_BAND, RETRY_SPATIAL_SPLIT if the user
    1772             : // should try to split the request in smaller chunks
    1773             : 
    1774             : GUInt32
    1775          32 : GDALDAASRasterBand::PrefetchBlocks(int nXOff, int nYOff, int nXSize, int nYSize,
    1776             :                                    const std::vector<int> &anRequestedBands)
    1777             : {
    1778          32 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1779             : 
    1780          32 :     if (anRequestedBands.size() > 1)
    1781             :     {
    1782          20 :         if (poGDS->m_nXOffFetched == nXOff && poGDS->m_nYOffFetched == nYOff &&
    1783          20 :             poGDS->m_nXSizeFetched == nXSize &&
    1784           5 :             poGDS->m_nYSizeFetched == nYSize)
    1785             :         {
    1786           5 :             return 0;
    1787             :         }
    1788          15 :         poGDS->m_nXOffFetched = nXOff;
    1789          15 :         poGDS->m_nYOffFetched = nYOff;
    1790          15 :         poGDS->m_nXSizeFetched = nXSize;
    1791          15 :         poGDS->m_nYSizeFetched = nYSize;
    1792             :     }
    1793             : 
    1794          27 :     int nBlockXOff = nXOff / nBlockXSize;
    1795          27 :     int nBlockYOff = nYOff / nBlockYSize;
    1796          27 :     int nXBlocks = (nXOff + nXSize - 1) / nBlockXSize - nBlockXOff + 1;
    1797          27 :     int nYBlocks = (nYOff + nYSize - 1) / nBlockYSize - nBlockYOff + 1;
    1798             : 
    1799          27 :     int nTotalDataTypeSize = 0;
    1800          27 :     const int nQueriedBands = static_cast<int>(anRequestedBands.size());
    1801          82 :     for (int i = 0; i < nQueriedBands; i++)
    1802             :     {
    1803          55 :         const int iBand = anRequestedBands[i];
    1804          55 :         if (iBand > 0 && iBand <= poGDS->GetRasterCount())
    1805             :         {
    1806          53 :             nTotalDataTypeSize += GDALGetDataTypeSizeBytes(
    1807             :                 poGDS->GetRasterBand(iBand)->GetRasterDataType());
    1808             :         }
    1809             :         else
    1810             :         {
    1811           2 :             nTotalDataTypeSize += GDALGetDataTypeSizeBytes(
    1812           2 :                 poGDS->m_poMaskBand->GetRasterDataType());
    1813             :         }
    1814             :     }
    1815             : 
    1816             :     // If AdviseRead() was called before, and the current requested area is
    1817             :     // in it, check if we can prefetch the whole advised area
    1818          27 :     const GIntBig nCacheMax = GDALGetCacheMax64() / 2;
    1819          27 :     if (poGDS->m_nXSizeAdvise > 0 && nXOff >= poGDS->m_nXOffAdvise &&
    1820          10 :         nYOff >= poGDS->m_nYOffAdvise &&
    1821          10 :         nXOff + nXSize <= poGDS->m_nXOffAdvise + poGDS->m_nXSizeAdvise &&
    1822          10 :         nYOff + nYSize <= poGDS->m_nYOffAdvise + poGDS->m_nYSizeAdvise)
    1823             :     {
    1824          10 :         int nBlockXOffAdvise = poGDS->m_nXOffAdvise / nBlockXSize;
    1825          10 :         int nBlockYOffAdvise = poGDS->m_nYOffAdvise / nBlockYSize;
    1826          10 :         int nXBlocksAdvise =
    1827          10 :             (poGDS->m_nXOffAdvise + poGDS->m_nXSizeAdvise - 1) / nBlockXSize -
    1828             :             nBlockXOffAdvise + 1;
    1829          10 :         int nYBlocksAdvise =
    1830          10 :             (poGDS->m_nYOffAdvise + poGDS->m_nYSizeAdvise - 1) / nBlockYSize -
    1831             :             nBlockYOffAdvise + 1;
    1832          10 :         const GIntBig nUncompressedSize = static_cast<GIntBig>(nXBlocksAdvise) *
    1833          10 :                                           nYBlocksAdvise * nBlockXSize *
    1834          10 :                                           nBlockYSize * nTotalDataTypeSize;
    1835          10 :         if (nUncompressedSize <= nCacheMax &&
    1836          10 :             nUncompressedSize <= poGDS->m_nServerByteLimit)
    1837             :         {
    1838           7 :             CPLDebug("DAAS", "Using advise read");
    1839           7 :             nBlockXOff = nBlockXOffAdvise;
    1840           7 :             nBlockYOff = nBlockYOffAdvise;
    1841           7 :             nXBlocks = nXBlocksAdvise;
    1842           7 :             nYBlocks = nYBlocksAdvise;
    1843           7 :             if (anRequestedBands.size() > 1)
    1844             :             {
    1845           0 :                 poGDS->m_nXOffAdvise = 0;
    1846           0 :                 poGDS->m_nYOffAdvise = 0;
    1847           0 :                 poGDS->m_nXSizeAdvise = 0;
    1848           0 :                 poGDS->m_nYSizeAdvise = 0;
    1849             :             }
    1850             :         }
    1851             :     }
    1852             : 
    1853             :     // Check the number of already cached blocks, and remove fully
    1854             :     // cached lines at the top of the area of interest from the queried
    1855             :     // blocks
    1856          27 :     int nBlocksCached = 0;
    1857          27 :     int nBlocksCachedForThisBand = 0;
    1858          27 :     bool bAllLineCached = true;
    1859          54 :     for (int iYBlock = 0; iYBlock < nYBlocks;)
    1860             :     {
    1861          62 :         for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
    1862             :         {
    1863          98 :             for (int i = 0; i < nQueriedBands; i++)
    1864             :             {
    1865          63 :                 const int iBand = anRequestedBands[i];
    1866          63 :                 GDALRasterBlock *poBlock = nullptr;
    1867             :                 GDALDAASRasterBand *poIterBand;
    1868          63 :                 if (iBand > 0 && iBand <= poGDS->GetRasterCount())
    1869             :                     poIterBand = reinterpret_cast<GDALDAASRasterBand *>(
    1870          61 :                         poGDS->GetRasterBand(iBand));
    1871             :                 else
    1872           2 :                     poIterBand = poGDS->m_poMaskBand;
    1873             : 
    1874         126 :                 poBlock = poIterBand->TryGetLockedBlockRef(
    1875          63 :                     nBlockXOff + iXBlock, nBlockYOff + iYBlock);
    1876          63 :                 if (poBlock != nullptr)
    1877             :                 {
    1878          11 :                     nBlocksCached++;
    1879          11 :                     if (iBand == nBand)
    1880          10 :                         nBlocksCachedForThisBand++;
    1881          11 :                     poBlock->DropLock();
    1882          11 :                     continue;
    1883             :                 }
    1884             :                 else
    1885             :                 {
    1886          52 :                     bAllLineCached = false;
    1887             :                 }
    1888             :             }
    1889             :         }
    1890             : 
    1891          27 :         if (bAllLineCached)
    1892             :         {
    1893           5 :             nBlocksCached -= nXBlocks * nQueriedBands;
    1894           5 :             nBlocksCachedForThisBand -= nXBlocks;
    1895           5 :             nBlockYOff++;
    1896           5 :             nYBlocks--;
    1897             :         }
    1898             :         else
    1899             :         {
    1900          22 :             iYBlock++;
    1901             :         }
    1902             :     }
    1903             : 
    1904          27 :     if (nXBlocks > 0 && nYBlocks > 0)
    1905             :     {
    1906          22 :         bool bMustReturn = false;
    1907          22 :         GUInt32 nRetryFlags = 0;
    1908             : 
    1909             :         // Get the blocks if the number of already cached blocks is lesser
    1910             :         // than 25% of the to be queried blocks
    1911          22 :         if (nBlocksCached > (nQueriedBands * nXBlocks * nYBlocks) / 4)
    1912             :         {
    1913           1 :             if (nBlocksCachedForThisBand <= (nXBlocks * nYBlocks) / 4)
    1914             :             {
    1915           1 :                 nRetryFlags |= RETRY_PER_BAND;
    1916             :             }
    1917             :             else
    1918             :             {
    1919           0 :                 bMustReturn = true;
    1920             :             }
    1921             :         }
    1922             : 
    1923             :         // Make sure that we have enough cache (with a margin of 50%)
    1924             :         // and the number of queried pixels isn't too big w.r.t server
    1925             :         // limit
    1926          22 :         const GIntBig nUncompressedSize = static_cast<GIntBig>(nXBlocks) *
    1927          22 :                                           nYBlocks * nBlockXSize * nBlockYSize *
    1928          22 :                                           nTotalDataTypeSize;
    1929          22 :         if (nUncompressedSize > nCacheMax ||
    1930          22 :             nUncompressedSize > poGDS->m_nServerByteLimit)
    1931             :         {
    1932           3 :             if (anRequestedBands.size() > 1 && poGDS->GetRasterCount() > 1)
    1933             :             {
    1934           0 :                 const int nThisDTSize = GDALGetDataTypeSizeBytes(eDataType);
    1935           0 :                 const GIntBig nUncompressedSizeThisBand =
    1936           0 :                     static_cast<GIntBig>(nXBlocks) * nYBlocks * nBlockXSize *
    1937           0 :                     nBlockYSize * nThisDTSize;
    1938           0 :                 if (nUncompressedSizeThisBand <= poGDS->m_nServerByteLimit &&
    1939             :                     nUncompressedSizeThisBand <= nCacheMax)
    1940             :                 {
    1941           0 :                     nRetryFlags |= RETRY_PER_BAND;
    1942             :                 }
    1943             :             }
    1944           3 :             if (nXBlocks > 1 || nYBlocks > 1)
    1945             :             {
    1946           1 :                 nRetryFlags |= RETRY_SPATIAL_SPLIT;
    1947             :             }
    1948           3 :             return nRetryFlags;
    1949             :         }
    1950          19 :         if (bMustReturn)
    1951           0 :             return nRetryFlags;
    1952             : 
    1953          19 :         GetBlocks(nBlockXOff, nBlockYOff, nXBlocks, nYBlocks, anRequestedBands,
    1954             :                   nullptr);
    1955             :     }
    1956             : 
    1957          24 :     return 0;
    1958             : }
    1959             : 
    1960             : /************************************************************************/
    1961             : /*                           GetBlocks()                                */
    1962             : /************************************************************************/
    1963             : 
    1964          34 : CPLErr GDALDAASRasterBand::GetBlocks(int nBlockXOff, int nBlockYOff,
    1965             :                                      int nXBlocks, int nYBlocks,
    1966             :                                      const std::vector<int> &anRequestedBands,
    1967             :                                      void *pDstBuffer)
    1968             : {
    1969          34 :     GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
    1970             : 
    1971          34 :     CPLAssert(!anRequestedBands.empty());
    1972          34 :     if (pDstBuffer)
    1973             :     {
    1974          11 :         CPLAssert(nXBlocks == 1 && nYBlocks == 1 &&
    1975             :                   anRequestedBands.size() == 1);
    1976             :     }
    1977             : 
    1978             :     // Detect if there is a mix of non-mask and mask bands
    1979          34 :     if (anRequestedBands.size() > 1)
    1980             :     {
    1981          15 :         std::vector<int> anNonMasks;
    1982          15 :         std::vector<int> anMasks;
    1983          58 :         for (auto &iBand : anRequestedBands)
    1984             :         {
    1985          84 :             if (iBand == MAIN_MASK_BAND_NUMBER ||
    1986          41 :                 poGDS->m_aoBandDesc[iBand - 1].bIsMask)
    1987           2 :                 anMasks.push_back(iBand);
    1988             :             else
    1989          41 :                 anNonMasks.push_back(iBand);
    1990             :         }
    1991          15 :         if (!anNonMasks.empty() && !anMasks.empty())
    1992             :         {
    1993           2 :             return GetBlocks(nBlockXOff, nBlockYOff, nXBlocks, nYBlocks,
    1994           2 :                              anNonMasks, nullptr) == CE_None &&
    1995           2 :                            GetBlocks(nBlockXOff, nBlockYOff, nXBlocks, nYBlocks,
    1996             :                                      anMasks, nullptr) == CE_None
    1997           4 :                        ? CE_None
    1998           2 :                        : CE_Failure;
    1999             :         }
    2000             :     }
    2001             : 
    2002          32 :     char **papszOptions = poGDS->GetHTTPOptions();
    2003             : 
    2004          64 :     CPLString osHeaders = CSLFetchNameValueDef(papszOptions, "HEADERS", "");
    2005          32 :     if (!osHeaders.empty())
    2006           0 :         osHeaders += "\r\n";
    2007          32 :     osHeaders += "Content-Type: application/json";
    2008          32 :     osHeaders += "\r\n";
    2009          64 :     CPLString osDataContentType("application/octet-stream");
    2010          32 :     GDALDAASDataset::Format eRequestFormat(GDALDAASDataset::Format::RAW);
    2011          41 :     if (poGDS->m_eFormat == GDALDAASDataset::Format::PNG &&
    2012           9 :         (anRequestedBands.size() == 1 || anRequestedBands.size() == 3 ||
    2013           0 :          anRequestedBands.size() == 4))
    2014             :     {
    2015           6 :         eRequestFormat = poGDS->m_eFormat;
    2016           6 :         osDataContentType = "image/png";
    2017             :     }
    2018          28 :     else if (poGDS->m_eFormat == GDALDAASDataset::Format::JPEG &&
    2019           2 :              (anRequestedBands.size() == 1 || anRequestedBands.size() == 3))
    2020             :     {
    2021           1 :         eRequestFormat = poGDS->m_eFormat;
    2022           1 :         osDataContentType = "image/jpeg";
    2023             :     }
    2024          25 :     else if (poGDS->m_eFormat == GDALDAASDataset::Format::JPEG2000)
    2025             :     {
    2026           1 :         eRequestFormat = poGDS->m_eFormat;
    2027           1 :         osDataContentType = "image/jp2";
    2028             :     }
    2029          32 :     osHeaders += "Accept: " + osDataContentType;
    2030          32 :     papszOptions = CSLSetNameValue(papszOptions, "HEADERS", osHeaders);
    2031             : 
    2032             :     // Build request JSon document
    2033          64 :     CPLJSONDocument oDoc;
    2034          64 :     CPLJSONObject oBBox;
    2035             : 
    2036          32 :     if (poGDS->m_bRequestInGeoreferencedCoordinates)
    2037             :     {
    2038           0 :         CPLJSONObject oSRS;
    2039           0 :         oSRS.Add("type", poGDS->m_osSRSType);
    2040           0 :         oSRS.Add("value", poGDS->m_osSRSValue);
    2041           0 :         oBBox.Add("srs", oSRS);
    2042             :     }
    2043             :     else
    2044             :     {
    2045          32 :         CPLJSONObject oSRS;
    2046          32 :         oSRS.Add("type", "image");
    2047          32 :         oBBox.Add("srs", oSRS);
    2048             :     }
    2049             : 
    2050          32 :     const int nMainXSize = poGDS->m_poParentDS
    2051          32 :                                ? poGDS->m_poParentDS->GetRasterXSize()
    2052          32 :                                : nRasterXSize;
    2053          32 :     const int nMainYSize = poGDS->m_poParentDS
    2054          32 :                                ? poGDS->m_poParentDS->GetRasterYSize()
    2055          32 :                                : nRasterYSize;
    2056          32 :     const int nULX = nBlockXOff * nBlockXSize;
    2057          32 :     const int nULY = nBlockYOff * nBlockYSize;
    2058             :     const int nLRX =
    2059          32 :         std::min(nRasterXSize, (nBlockXOff + nXBlocks) * nBlockXSize);
    2060             :     const int nLRY =
    2061          32 :         std::min(nRasterYSize, (nBlockYOff + nYBlocks) * nBlockYSize);
    2062             : 
    2063          64 :     CPLJSONObject oUL;
    2064          64 :     CPLJSONObject oLR;
    2065          32 :     if (poGDS->m_bRequestInGeoreferencedCoordinates)
    2066             :     {
    2067             :         double dfULX, dfULY;
    2068           0 :         GDALApplyGeoTransform(poGDS->m_gt.data(), nULX, nULY, &dfULX, &dfULY);
    2069           0 :         oUL.Add("x", dfULX);
    2070           0 :         oUL.Add("y", dfULY);
    2071             : 
    2072             :         double dfLRX, dfLRY;
    2073           0 :         GDALApplyGeoTransform(poGDS->m_gt.data(), nLRX, nLRY, &dfLRX, &dfLRY);
    2074           0 :         oLR.Add("x", dfLRX);
    2075           0 :         oLR.Add("y", dfLRY);
    2076             :     }
    2077             :     else
    2078             :     {
    2079          32 :         oUL.Add("x",
    2080          32 :                 static_cast<int>((static_cast<GIntBig>(nULX) * nMainXSize) /
    2081          32 :                                  nRasterXSize));
    2082          32 :         oUL.Add("y",
    2083          32 :                 static_cast<int>((static_cast<GIntBig>(nULY) * nMainYSize) /
    2084          32 :                                  nRasterYSize));
    2085             : 
    2086          33 :         oLR.Add("x", (nLRX == nRasterXSize)
    2087             :                          ? nMainXSize
    2088             :                          : static_cast<int>(
    2089           1 :                                (static_cast<GIntBig>(nLRX) * nMainXSize) /
    2090           1 :                                nRasterXSize));
    2091          32 :         oLR.Add("y", (nLRY == nRasterYSize)
    2092             :                          ? nMainYSize
    2093             :                          : static_cast<int>(
    2094           0 :                                (static_cast<GIntBig>(nLRY) * nMainYSize) /
    2095           0 :                                nRasterYSize));
    2096             :     }
    2097          32 :     oBBox.Add("ul", oUL);
    2098          32 :     oBBox.Add("lr", oLR);
    2099          32 :     oDoc.GetRoot().Add("bbox", oBBox);
    2100             : 
    2101          64 :     CPLJSONObject oTargetModel;
    2102             : 
    2103          64 :     CPLJSONObject oStepTargetModel;
    2104          32 :     if (poGDS->m_bRequestInGeoreferencedCoordinates)
    2105             :     {
    2106           0 :         oStepTargetModel.Add("x", poGDS->m_gt[1]);
    2107           0 :         oStepTargetModel.Add("y", fabs(poGDS->m_gt[5]));
    2108             :     }
    2109             :     else
    2110             :     {
    2111          32 :         oStepTargetModel.Add("x", 0);
    2112          32 :         oStepTargetModel.Add("y", 0);
    2113             :     }
    2114          32 :     oTargetModel.Add("step", oStepTargetModel);
    2115             : 
    2116          64 :     CPLJSONObject oSize;
    2117          32 :     int nRequestWidth = nLRX - nULX;
    2118          32 :     int nRequestHeight = nLRY - nULY;
    2119          32 :     oSize.Add("columns", nRequestWidth);
    2120          32 :     oSize.Add("lines", nRequestHeight);
    2121          32 :     oTargetModel.Add("size", oSize);
    2122             : 
    2123          32 :     if (poGDS->m_eCurrentResampleAlg == GRIORA_NearestNeighbour)
    2124             :     {
    2125          32 :         oTargetModel.Add("sampling-algo", "NEAREST");
    2126             :     }
    2127           0 :     else if (poGDS->m_eCurrentResampleAlg == GRIORA_Bilinear)
    2128             :     {
    2129           0 :         oTargetModel.Add("sampling-algo", "BILINEAR");
    2130             :     }
    2131           0 :     else if (poGDS->m_eCurrentResampleAlg == GRIORA_Cubic)
    2132             :     {
    2133           0 :         oTargetModel.Add("sampling-algo", "BICUBIC");
    2134             :     }
    2135           0 :     else if (poGDS->m_eCurrentResampleAlg == GRIORA_Average)
    2136             :     {
    2137           0 :         oTargetModel.Add("sampling-algo", "AVERAGE");
    2138             :     }
    2139             :     else
    2140             :     {
    2141             :         // Defaults to BILINEAR for other GDAL methods not supported by
    2142             :         // server
    2143           0 :         oTargetModel.Add("sampling-algo", "BILINEAR");
    2144             :     }
    2145             : 
    2146          32 :     oTargetModel.Add("strictOutputSize", true);
    2147             : 
    2148          32 :     if (!poGDS->m_bRequestInGeoreferencedCoordinates)
    2149             :     {
    2150          32 :         CPLJSONObject oSRS;
    2151          32 :         oSRS.Add("type", "image");
    2152          32 :         oTargetModel.Add("srs", oSRS);
    2153             :     }
    2154             : 
    2155          32 :     oDoc.GetRoot().Add("target-model", oTargetModel);
    2156             : 
    2157          64 :     CPLJSONArray oBands;
    2158          32 :     bool bOK = true;
    2159          90 :     for (const int iBand : anRequestedBands)
    2160             :     {
    2161             :         auto desc = (iBand == MAIN_MASK_BAND_NUMBER)
    2162          58 :                         ? poGDS->m_poMaskBand->GetDescription()
    2163          56 :                         : poGDS->GetRasterBand(iBand)->GetDescription();
    2164          58 :         if (EQUAL(desc, ""))
    2165           0 :             bOK = false;
    2166             :         else
    2167          58 :             oBands.Add(desc);
    2168             :     }
    2169          32 :     if (bOK)
    2170             :     {
    2171          32 :         oDoc.GetRoot().Add("bands", oBands);
    2172             :     }
    2173             : 
    2174          32 :     papszOptions = CSLSetNameValue(
    2175             :         papszOptions, "POSTFIELDS",
    2176          64 :         oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty).c_str());
    2177             : 
    2178             :     CPLString osURL(CPLGetConfigOption("GDAL_DAAS_GET_BUFFER_URL",
    2179          64 :                                        poGDS->m_osGetBufferURL.c_str()));
    2180          32 :     CPLHTTPResult *psResult = DAAS_CPLHTTPFetch(osURL, papszOptions);
    2181          32 :     CSLDestroy(papszOptions);
    2182          32 :     if (psResult == nullptr)
    2183           0 :         return CE_Failure;
    2184             : 
    2185          32 :     if (psResult->pszErrBuf != nullptr)
    2186             :     {
    2187          11 :         CPLError(CE_Failure, CPLE_AppDefined, "Get request %s failed: %s",
    2188             :                  osURL.c_str(),
    2189          12 :                  psResult->pabyData ? CPLSPrintf("%s: %s", psResult->pszErrBuf,
    2190             :                                                  reinterpret_cast<const char *>(
    2191           1 :                                                      psResult->pabyData))
    2192             :                                     : psResult->pszErrBuf);
    2193          11 :         CPLHTTPDestroyResult(psResult);
    2194          11 :         return CE_Failure;
    2195             :     }
    2196             : 
    2197          21 :     if (psResult->nDataLen == 0)
    2198             :     {
    2199             :         // Presumably HTTP 204 empty
    2200           0 :         CPLHTTPDestroyResult(psResult);
    2201             : 
    2202           0 :         for (int iYBlock = 0; iYBlock < nYBlocks; iYBlock++)
    2203             :         {
    2204           0 :             for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
    2205             :             {
    2206           0 :                 for (const int iBand : anRequestedBands)
    2207             :                 {
    2208           0 :                     GByte *pabyDstBuffer = nullptr;
    2209             :                     GDALDAASRasterBand *poIterBand;
    2210           0 :                     if (iBand == MAIN_MASK_BAND_NUMBER)
    2211             :                     {
    2212           0 :                         poIterBand = poGDS->m_poMaskBand;
    2213             :                     }
    2214             :                     else
    2215             :                     {
    2216             :                         poIterBand = reinterpret_cast<GDALDAASRasterBand *>(
    2217           0 :                             poGDS->GetRasterBand(iBand));
    2218             :                     }
    2219             : 
    2220           0 :                     GDALRasterBlock *poBlock = nullptr;
    2221           0 :                     if (pDstBuffer != nullptr)
    2222             :                     {
    2223           0 :                         pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    2224             :                     }
    2225             :                     else
    2226             :                     {
    2227             :                         // Check if the same block in other bands is already in
    2228             :                         // the GDAL block cache
    2229           0 :                         poBlock = poIterBand->TryGetLockedBlockRef(
    2230           0 :                             nBlockXOff + iXBlock, nBlockYOff + iYBlock);
    2231           0 :                         if (poBlock != nullptr)
    2232             :                         {
    2233             :                             // Yes, no need to do further work
    2234           0 :                             poBlock->DropLock();
    2235           0 :                             continue;
    2236             :                         }
    2237             :                         // Instantiate the block
    2238           0 :                         poBlock = poIterBand->GetLockedBlockRef(
    2239           0 :                             nBlockXOff + iXBlock, nBlockYOff + iYBlock, TRUE);
    2240           0 :                         if (poBlock == nullptr)
    2241             :                         {
    2242           0 :                             continue;
    2243             :                         }
    2244             :                         pabyDstBuffer =
    2245           0 :                             static_cast<GByte *>(poBlock->GetDataRef());
    2246             :                     }
    2247             : 
    2248           0 :                     const int nDTSize = GDALGetDataTypeSizeBytes(
    2249             :                         poIterBand->GetRasterDataType());
    2250           0 :                     double dfNoDataValue = poIterBand->GetNoDataValue(nullptr);
    2251           0 :                     GDALCopyWords(&dfNoDataValue, GDT_Float64, 0, pabyDstBuffer,
    2252             :                                   poIterBand->GetRasterDataType(), nDTSize,
    2253           0 :                                   nBlockXSize * nBlockYSize);
    2254           0 :                     if (poBlock)
    2255           0 :                         poBlock->DropLock();
    2256             :                 }
    2257             :             }
    2258             :         }
    2259             : 
    2260           0 :         return CE_None;
    2261             :     }
    2262             : 
    2263             : #ifdef DEBUG_VERBOSE
    2264             :     CPLDebug("DAAS", "Response = '%s'",
    2265             :              reinterpret_cast<const char *>(psResult->pabyData));
    2266             : #endif
    2267          21 :     if (!CPLHTTPParseMultipartMime(psResult))
    2268             :     {
    2269           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2270             :                  "Get request %s failed: "
    2271             :                  "Invalid content returned by server",
    2272             :                  osURL.c_str());
    2273           1 :         CPLHTTPDestroyResult(psResult);
    2274           1 :         return CE_Failure;
    2275             :     }
    2276          20 :     int iMetadataPart = -1;
    2277          20 :     int iDataPart = -1;
    2278             :     // Identify metadata and data parts
    2279          58 :     for (int i = 0; i < psResult->nMimePartCount; i++)
    2280             :     {
    2281          76 :         const char *pszContentType = CSLFetchNameValue(
    2282          38 :             psResult->pasMimePart[i].papszHeaders, "Content-Type");
    2283          76 :         const char *pszContentDisposition = CSLFetchNameValue(
    2284          38 :             psResult->pasMimePart[i].papszHeaders, "Content-Disposition");
    2285          38 :         if (pszContentType)
    2286             :         {
    2287          38 :             if (EQUAL(pszContentType, "application/json"))
    2288             :             {
    2289          19 :                 iMetadataPart = i;
    2290             :             }
    2291          19 :             else if (EQUAL(pszContentType, osDataContentType))
    2292             :             {
    2293          19 :                 iDataPart = i;
    2294             :             }
    2295             :         }
    2296          38 :         if (pszContentDisposition)
    2297             :         {
    2298          38 :             if (EQUAL(pszContentDisposition, "form-data; name=\"Data\";"))
    2299             :             {
    2300          19 :                 iDataPart = i;
    2301             :             }
    2302             :         }
    2303             :     }
    2304          20 :     if (iDataPart < 0)
    2305             :     {
    2306           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2307             :                  "Cannot find part with Content-Type: %s in GetBuffer response",
    2308             :                  osDataContentType.c_str());
    2309           1 :         CPLHTTPDestroyResult(psResult);
    2310           1 :         return CE_Failure;
    2311             :     }
    2312          19 :     if (iMetadataPart < 0)
    2313             :     {
    2314           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2315             :                  "Cannot find part with Content-Type: %s in GetBuffer response",
    2316             :                  "application/json");
    2317           1 :         CPLHTTPDestroyResult(psResult);
    2318           1 :         return CE_Failure;
    2319             :     }
    2320             : 
    2321          36 :     CPLString osJson;
    2322             :     osJson.assign(reinterpret_cast<const char *>(
    2323          18 :                       psResult->pasMimePart[iMetadataPart].pabyData),
    2324          18 :                   psResult->pasMimePart[iMetadataPart].nDataLen);
    2325          18 :     CPLDebug("DAAS", "GetBuffer metadata response: %s", osJson.c_str());
    2326          18 :     if (!oDoc.LoadMemory(osJson))
    2327             :     {
    2328           0 :         CPLHTTPDestroyResult(psResult);
    2329           0 :         return CE_Failure;
    2330             :     }
    2331          36 :     auto oDocRoot = oDoc.GetRoot();
    2332          18 :     int nGotHeight = oDocRoot.GetInteger("properties/height");
    2333          18 :     int nGotWidth = oDocRoot.GetInteger("properties/width");
    2334          18 :     if (nGotHeight != nRequestHeight || nGotWidth != nRequestWidth)
    2335             :     {
    2336           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2337             :                  "Got buffer of size %dx%d, whereas %dx%d was expected",
    2338             :                  nGotWidth, nGotHeight, nRequestWidth, nRequestHeight);
    2339           1 :         CPLHTTPDestroyResult(psResult);
    2340           1 :         return CE_Failure;
    2341             :     }
    2342             : 
    2343             :     // Get the actual data type of the buffer response
    2344             :     GDALDataType eBufferDataType =
    2345          17 :         anRequestedBands[0] == MAIN_MASK_BAND_NUMBER
    2346          32 :             ? GDT_Byte
    2347          15 :             : poGDS->m_aoBandDesc[anRequestedBands[0] - 1].eDT;
    2348          51 :     auto oBandArray = oDocRoot.GetArray("properties/bands");
    2349          17 :     if (oBandArray.IsValid() && oBandArray.Size() >= 1)
    2350             :     {
    2351             :         bool bIgnored;
    2352           4 :         auto oBandProperties = oBandArray[0];
    2353             :         auto osPixelType =
    2354           4 :             GetString(oBandProperties, "pixelType", false, bIgnored);
    2355           4 :         if (!osPixelType.empty())
    2356             :         {
    2357           4 :             eBufferDataType = GetGDALDataTypeFromDAASPixelType(osPixelType);
    2358           4 :             if (eBufferDataType == GDT_Unknown)
    2359             :             {
    2360           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid pixelType: %s",
    2361             :                          osPixelType.c_str());
    2362           0 :                 CPLHTTPDestroyResult(psResult);
    2363           0 :                 return CE_Failure;
    2364             :             }
    2365             :         }
    2366             :     }
    2367             : 
    2368          17 :     const int nBufferDTSize = GDALGetDataTypeSizeBytes(eBufferDataType);
    2369          17 :     std::shared_ptr<GDALDataset> poTileDS;
    2370          17 :     if (eRequestFormat == GDALDAASDataset::Format::RAW)
    2371             :     {
    2372          22 :         int nExpectedBytes = nGotHeight * nGotWidth * nBufferDTSize *
    2373          11 :                              static_cast<int>(anRequestedBands.size());
    2374          11 :         if (psResult->pasMimePart[iDataPart].nDataLen != nExpectedBytes)
    2375             :         {
    2376           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2377             :                      "Got buffer of %d bytes, whereas %d were expected",
    2378           1 :                      psResult->pasMimePart[iDataPart].nDataLen, nExpectedBytes);
    2379           1 :             CPLHTTPDestroyResult(psResult);
    2380           1 :             return CE_Failure;
    2381             :         }
    2382             : 
    2383          10 :         GByte *pabySrcData = psResult->pasMimePart[iDataPart].pabyData;
    2384             : #ifdef CPL_MSB
    2385             :         GDALSwapWords(pabySrcData, nBufferDTSize,
    2386             :                       nGotHeight * nGotWidth *
    2387             :                           static_cast<int>(anRequestedBands.size()),
    2388             :                       nBufferDTSize);
    2389             : #endif
    2390             : 
    2391          10 :         auto poMEMDS = MEMDataset::Create("", nRequestWidth, nRequestHeight, 0,
    2392             :                                           eBufferDataType, nullptr);
    2393          10 :         poTileDS.reset(poMEMDS);
    2394          22 :         for (int i = 0; i < static_cast<int>(anRequestedBands.size()); i++)
    2395             :         {
    2396          24 :             auto hBand = MEMCreateRasterBandEx(
    2397             :                 poMEMDS, i + 1,
    2398          12 :                 pabySrcData + i * nGotHeight * nGotWidth * nBufferDTSize,
    2399             :                 eBufferDataType, 0, 0, false);
    2400          12 :             poMEMDS->AddMEMBand(hBand);
    2401             :         }
    2402             :     }
    2403             :     else
    2404             :     {
    2405           6 :         const CPLString osTmpMemFile = VSIMemGenerateHiddenFilename("daas");
    2406           6 :         VSIFCloseL(VSIFileFromMemBuffer(
    2407           6 :             osTmpMemFile, psResult->pasMimePart[iDataPart].pabyData,
    2408           6 :             psResult->pasMimePart[iDataPart].nDataLen, false));
    2409           6 :         poTileDS.reset(GDALDataset::Open(osTmpMemFile,
    2410             :                                          GDAL_OF_RASTER | GDAL_OF_INTERNAL,
    2411             :                                          nullptr, nullptr, nullptr));
    2412           6 :         if (!poTileDS)
    2413             :         {
    2414           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot decode image");
    2415           1 :             VSIUnlink(osTmpMemFile);
    2416           1 :             CPLHTTPDestroyResult(psResult);
    2417           1 :             return CE_Failure;
    2418             :         }
    2419             :     }
    2420             : 
    2421          15 :     CPLErr eErr = CE_None;
    2422          15 :     poTileDS->MarkSuppressOnClose();
    2423             : 
    2424             :     bool bExpectedImageCharacteristics =
    2425          29 :         (poTileDS->GetRasterXSize() == nRequestWidth &&
    2426          14 :          poTileDS->GetRasterYSize() == nRequestHeight);
    2427          15 :     if (bExpectedImageCharacteristics)
    2428             :     {
    2429          28 :         if (poTileDS->GetRasterCount() ==
    2430          14 :             static_cast<int>(anRequestedBands.size()))
    2431             :         {
    2432             :             // ok
    2433             :         }
    2434           1 :         else if (eRequestFormat == GDALDAASDataset::Format::PNG &&
    2435           2 :                  anRequestedBands.size() == 1 &&
    2436           1 :                  poTileDS->GetRasterCount() == 4)
    2437             :         {
    2438             :             // ok
    2439             :         }
    2440             :         else
    2441             :         {
    2442           0 :             bExpectedImageCharacteristics = false;
    2443             :         }
    2444             :     }
    2445             : 
    2446          15 :     if (!bExpectedImageCharacteristics)
    2447             :     {
    2448           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2449             :                  "Got tile of size %dx%dx%d, whereas %dx%dx%d was expected",
    2450             :                  poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
    2451             :                  poTileDS->GetRasterCount(), nRequestWidth, nRequestHeight,
    2452           1 :                  static_cast<int>(anRequestedBands.size()));
    2453           1 :         CPLHTTPDestroyResult(psResult);
    2454           1 :         return CE_Failure;
    2455             :     }
    2456             : 
    2457          28 :     for (int iYBlock = 0; eErr == CE_None && iYBlock < nYBlocks; iYBlock++)
    2458             :     {
    2459             :         int nBlockActualYSize = std::min(
    2460          14 :             nBlockYSize, nRasterYSize - (iYBlock + nBlockYOff) * nBlockYSize);
    2461          30 :         for (int iXBlock = 0; eErr == CE_None && iXBlock < nXBlocks; iXBlock++)
    2462             :         {
    2463             :             int nBlockActualXSize =
    2464          16 :                 std::min(nBlockXSize,
    2465          16 :                          nRasterXSize - (iXBlock + nBlockXOff) * nBlockXSize);
    2466             : 
    2467          40 :             for (int i = 0; i < static_cast<int>(anRequestedBands.size()); i++)
    2468             :             {
    2469          24 :                 const int iBand = anRequestedBands[i];
    2470          24 :                 GByte *pabyDstBuffer = nullptr;
    2471             :                 GDALDAASRasterBand *poIterBand;
    2472          24 :                 if (iBand == MAIN_MASK_BAND_NUMBER)
    2473             :                 {
    2474           2 :                     poIterBand = poGDS->m_poMaskBand;
    2475             :                 }
    2476             :                 else
    2477             :                 {
    2478             :                     poIterBand = reinterpret_cast<GDALDAASRasterBand *>(
    2479          22 :                         poGDS->GetRasterBand(iBand));
    2480             :                 }
    2481             : 
    2482          24 :                 GDALRasterBlock *poBlock = nullptr;
    2483          24 :                 if (pDstBuffer != nullptr)
    2484           2 :                     pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    2485             :                 else
    2486             :                 {
    2487             :                     // Check if the same block in other bands is already in
    2488             :                     // the GDAL block cache
    2489          44 :                     poBlock = poIterBand->TryGetLockedBlockRef(
    2490          22 :                         nBlockXOff + iXBlock, nBlockYOff + iYBlock);
    2491          22 :                     if (poBlock != nullptr)
    2492             :                     {
    2493             :                         // Yes, no need to do further work
    2494           1 :                         poBlock->DropLock();
    2495           1 :                         continue;
    2496             :                     }
    2497             :                     // Instantiate the block
    2498          42 :                     poBlock = poIterBand->GetLockedBlockRef(
    2499          21 :                         nBlockXOff + iXBlock, nBlockYOff + iYBlock, TRUE);
    2500          21 :                     if (poBlock == nullptr)
    2501             :                     {
    2502           0 :                         continue;
    2503             :                     }
    2504          21 :                     pabyDstBuffer = static_cast<GByte *>(poBlock->GetDataRef());
    2505             :                 }
    2506             : 
    2507          23 :                 GDALRasterBand *poTileBand = poTileDS->GetRasterBand(i + 1);
    2508          23 :                 const auto eIterBandDT = poIterBand->GetRasterDataType();
    2509          23 :                 const int nDTSize = GDALGetDataTypeSizeBytes(eIterBandDT);
    2510          46 :                 eErr = poTileBand->RasterIO(
    2511          23 :                     GF_Read, iXBlock * nBlockXSize, iYBlock * nBlockYSize,
    2512             :                     nBlockActualXSize, nBlockActualYSize, pabyDstBuffer,
    2513             :                     nBlockActualXSize, nBlockActualYSize, eIterBandDT, nDTSize,
    2514          23 :                     static_cast<GSpacing>(nDTSize) * nBlockXSize, nullptr);
    2515             : 
    2516          23 :                 if (poBlock)
    2517          21 :                     poBlock->DropLock();
    2518          23 :                 if (eErr != CE_None)
    2519           0 :                     break;
    2520             :             }
    2521             :         }
    2522             :     }
    2523             : 
    2524          14 :     CPLHTTPDestroyResult(psResult);
    2525          14 :     return eErr;
    2526             : }
    2527             : 
    2528             : /************************************************************************/
    2529             : /*                       GDALRegister_DAAS()                            */
    2530             : /************************************************************************/
    2531             : 
    2532        1928 : void GDALRegister_DAAS()
    2533             : 
    2534             : {
    2535        1928 :     if (GDALGetDriverByName("DAAS") != nullptr)
    2536         282 :         return;
    2537             : 
    2538        1646 :     GDALDriver *poDriver = new GDALDriver();
    2539             : 
    2540        1646 :     poDriver->SetDescription("DAAS");
    2541        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    2542        1646 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Airbus DS Intelligence "
    2543        1646 :                                                  "Data As A Service driver");
    2544        1646 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/daas.html");
    2545             : 
    2546        1646 :     poDriver->SetMetadataItem(
    2547             :         GDAL_DMD_OPENOPTIONLIST,
    2548             :         "<OpenOptionList>"
    2549             :         "  <Option name='GET_METADATA_URL' type='string' "
    2550             :         "description='URL to GetImageMetadata' "
    2551             :         "required='true'/>"
    2552             :         "  <Option name='API_KEY' alt_config_option='GDAL_DAAS_API_KEY' "
    2553             :         "type='string' "
    2554             :         "description='API key'/>"
    2555             :         "  <Option name='CLIENT_ID' alt_config_option='GDAL_DAAS_CLIENT_ID' "
    2556             :         "type='string' description='Client id'/>"
    2557             :         "  <Option name='ACCESS_TOKEN' "
    2558             :         "alt_config_option='GDAL_DAAS_ACCESS_TOKEN' "
    2559             :         "type='string' description='Authorization access token'/>"
    2560             :         "  <Option name='X_FORWARDED_USER' "
    2561             :         "alt_config_option='GDAL_DAAS_X_FORWARDED_USER' type='string' "
    2562             :         "description='User from which the request originates from'/>"
    2563             :         "  <Option name='BLOCK_SIZE' type='integer' "
    2564             :         "description='Size of a block' default='512'/>"
    2565             :         "  <Option name='PIXEL_ENCODING' type='string-select' "
    2566             :         "description='Format in which pixels are queried'>"
    2567             :         "       <Value>AUTO</Value>"
    2568             :         "       <Value>RAW</Value>"
    2569             :         "       <Value>PNG</Value>"
    2570             :         "       <Value>JPEG</Value>"
    2571             :         "       <Value>JPEG2000</Value>"
    2572             :         "   </Option>"
    2573             :         "  <Option name='TARGET_SRS' type='string' description="
    2574             :         "'SRS name for server-side reprojection.'/>"
    2575             :         "  <Option name='MASKS' type='boolean' "
    2576             :         "description='Whether to expose mask bands' default='YES'/>"
    2577        1646 :         "</OpenOptionList>");
    2578             : 
    2579        1646 :     poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "DAAS:");
    2580             : 
    2581        1646 :     poDriver->pfnIdentify = GDALDAASDataset::Identify;
    2582        1646 :     poDriver->pfnOpen = GDALDAASDataset::OpenStatic;
    2583             : 
    2584        1646 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2585             : }

Generated by: LCOV version 1.14