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

Generated by: LCOV version 1.14