LCOV - code coverage report
Current view: top level - frmts/eeda - eedaidataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 483 643 75.1 %
Date: 2025-07-05 13:22:42 Functions: 24 26 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Earth Engine Data API Images driver
       4             :  * Purpose:  Earth Engine Data API Images driver
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2017-2018, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_priv.h"
      14             : #include "cpl_http.h"
      15             : #include "cpl_conv.h"
      16             : #include "ogrlibjsonutils.h"
      17             : #include "eeda.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <vector>
      21             : #include <map>
      22             : #include <limits>
      23             : 
      24             : extern "C" void GDALRegister_EEDAI();
      25             : 
      26             : static const int DEFAULT_BLOCK_SIZE = 256;
      27             : 
      28             : const GUInt32 RETRY_PER_BAND = 1;
      29             : const GUInt32 RETRY_SPATIAL_SPLIT = 2;
      30             : 
      31             : // Eart engine server only allows up to 16 MB per request
      32             : const int SERVER_BYTE_LIMIT = 16 * 1024 * 1024;
      33             : const int SERVER_SIMUTANEOUS_BAND_LIMIT = 100;
      34             : const int SERVER_DIMENSION_LIMIT = 10000;
      35             : 
      36             : /************************************************************************/
      37             : /*                          GDALEEDAIDataset                            */
      38             : /************************************************************************/
      39             : 
      40             : class GDALEEDAIDataset final : public GDALEEDABaseDataset
      41             : {
      42             :     CPL_DISALLOW_COPY_ASSIGN(GDALEEDAIDataset)
      43             : 
      44             :     friend class GDALEEDAIRasterBand;
      45             : 
      46             :     int m_nBlockSize;
      47             :     CPLString m_osAsset{};
      48             :     CPLString m_osAssetName{};
      49             :     GDALEEDAIDataset *m_poParentDS;
      50             : #ifdef DEBUG_VERBOSE
      51             :     int m_iOvrLevel;
      52             : #endif
      53             :     CPLString m_osPixelEncoding{};
      54             :     bool m_bQueryMultipleBands;
      55             :     OGRSpatialReference m_oSRS{};
      56             :     GDALGeoTransform m_gt{};
      57             :     std::vector<GDALEEDAIDataset *> m_apoOverviewDS{};
      58             : 
      59             :     GDALEEDAIDataset(GDALEEDAIDataset *poParentDS, int iOvrLevel);
      60             : 
      61             :     void
      62             :     SetMetadataFromProperties(json_object *poProperties,
      63             :                               const std::map<CPLString, int> &aoMapBandNames);
      64             : 
      65             :   public:
      66             :     GDALEEDAIDataset();
      67             :     virtual ~GDALEEDAIDataset();
      68             : 
      69             :     const OGRSpatialReference *GetSpatialRef() const override;
      70             :     virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      71             : 
      72             :     virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
      73             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
      74             :                              int nBufYSize, GDALDataType eBufType,
      75             :                              int nBandCount, BANDMAP_TYPE panBandMap,
      76             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
      77             :                              GSpacing nBandSpace,
      78             :                              GDALRasterIOExtraArg *psExtraArg) override;
      79             : 
      80             :     bool ComputeQueryStrategy();
      81             : 
      82             :     bool Open(GDALOpenInfo *poOpenInfo);
      83             : };
      84             : 
      85             : /************************************************************************/
      86             : /*                        GDALEEDAIRasterBand                           */
      87             : /************************************************************************/
      88             : 
      89             : class GDALEEDAIRasterBand final : public GDALRasterBand
      90             : {
      91             :     CPL_DISALLOW_COPY_ASSIGN(GDALEEDAIRasterBand)
      92             : 
      93             :     friend class GDALEEDAIDataset;
      94             : 
      95             :     GDALColorInterp m_eInterp;
      96             : 
      97             :     bool DecodeNPYArray(const GByte *pabyData, int nDataLen,
      98             :                         bool bQueryAllBands, void *pDstBuffer, int nBlockXOff,
      99             :                         int nBlockYOff, int nXBlocks, int nYBlocks,
     100             :                         int nReqXSize, int nReqYSize) const;
     101             :     bool DecodeGDALDataset(const GByte *pabyData, int nDataLen,
     102             :                            bool bQueryAllBands, void *pDstBuffer,
     103             :                            int nBlockXOff, int nBlockYOff, int nXBlocks,
     104             :                            int nYBlocks, int nReqXSize, int nReqYSize);
     105             : 
     106             :     CPLErr GetBlocks(int nBlockXOff, int nBlockYOff, int nXBlocks, int nYBlocks,
     107             :                      bool bQueryAllBands, void *pBuffer);
     108             :     GUInt32 PrefetchBlocks(int nXOff, int nYOff, int nXSize, int nYSize,
     109             :                            int nBufXSize, int nBufYSize, bool bQueryAllBands);
     110             : 
     111             :   public:
     112             :     GDALEEDAIRasterBand(GDALEEDAIDataset *poDSIn, GDALDataType eDT);
     113             :     virtual ~GDALEEDAIRasterBand();
     114             : 
     115             :     virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     116             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
     117             :                              int nBufYSize, GDALDataType eBufType,
     118             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
     119             :                              GDALRasterIOExtraArg *psExtraArg) CPL_OVERRIDE;
     120             : 
     121             :     virtual CPLErr IReadBlock(int, int, void *) CPL_OVERRIDE;
     122             :     virtual int GetOverviewCount() CPL_OVERRIDE;
     123             :     virtual GDALRasterBand *GetOverview(int) CPL_OVERRIDE;
     124             : 
     125           0 :     virtual CPLErr SetColorInterpretation(GDALColorInterp eInterp) CPL_OVERRIDE
     126             :     {
     127           0 :         m_eInterp = eInterp;
     128           0 :         return CE_None;
     129             :     }
     130             : 
     131           7 :     virtual GDALColorInterp GetColorInterpretation() CPL_OVERRIDE
     132             :     {
     133           7 :         return m_eInterp;
     134             :     }
     135             : };
     136             : 
     137             : /************************************************************************/
     138             : /*                         GDALEEDAIDataset()                           */
     139             : /************************************************************************/
     140             : 
     141           9 : GDALEEDAIDataset::GDALEEDAIDataset()
     142             :     : m_nBlockSize(DEFAULT_BLOCK_SIZE), m_poParentDS(nullptr),
     143             : #ifdef DEBUG_VERBOSE
     144             :       m_iOvrLevel(0),
     145             : #endif
     146           9 :       m_bQueryMultipleBands(false)
     147             : {
     148           9 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     149           9 : }
     150             : 
     151             : /************************************************************************/
     152             : /*                         GDALEEDAIDataset()                           */
     153             : /************************************************************************/
     154             : 
     155          27 : GDALEEDAIDataset::GDALEEDAIDataset(GDALEEDAIDataset *poParentDS, int iOvrLevel)
     156          27 :     : m_nBlockSize(poParentDS->m_nBlockSize), m_osAsset(poParentDS->m_osAsset),
     157          27 :       m_osAssetName(poParentDS->m_osAssetName), m_poParentDS(poParentDS),
     158             : #ifdef DEBUG_VERBOSE
     159             :       m_iOvrLevel(iOvrLevel),
     160             : #endif
     161          27 :       m_osPixelEncoding(poParentDS->m_osPixelEncoding),
     162          27 :       m_bQueryMultipleBands(poParentDS->m_bQueryMultipleBands),
     163          27 :       m_oSRS(poParentDS->m_oSRS)
     164             : {
     165          27 :     m_osBaseURL = poParentDS->m_osBaseURL;
     166          27 :     nRasterXSize = m_poParentDS->nRasterXSize >> iOvrLevel;
     167          27 :     nRasterYSize = m_poParentDS->nRasterYSize >> iOvrLevel;
     168          27 :     m_gt[0] = m_poParentDS->m_gt[0];
     169          27 :     m_gt[1] = m_poParentDS->m_gt[1] * m_poParentDS->nRasterXSize / nRasterXSize;
     170          27 :     m_gt[2] = m_poParentDS->m_gt[2];
     171          27 :     m_gt[3] = m_poParentDS->m_gt[3];
     172          27 :     m_gt[4] = m_poParentDS->m_gt[4];
     173          27 :     m_gt[5] = m_poParentDS->m_gt[5] * m_poParentDS->nRasterYSize / nRasterYSize;
     174          27 : }
     175             : 
     176             : /************************************************************************/
     177             : /*                        ~GDALEEDAIDataset()                           */
     178             : /************************************************************************/
     179             : 
     180          72 : GDALEEDAIDataset::~GDALEEDAIDataset()
     181             : {
     182          63 :     for (size_t i = 0; i < m_apoOverviewDS.size(); i++)
     183             :     {
     184          27 :         delete m_apoOverviewDS[i];
     185             :     }
     186          72 : }
     187             : 
     188             : /************************************************************************/
     189             : /*                        GDALEEDAIRasterBand()                         */
     190             : /************************************************************************/
     191             : 
     192          63 : GDALEEDAIRasterBand::GDALEEDAIRasterBand(GDALEEDAIDataset *poDSIn,
     193          63 :                                          GDALDataType eDT)
     194          63 :     : m_eInterp(GCI_Undefined)
     195             : {
     196          63 :     eDataType = eDT;
     197          63 :     nBlockXSize = poDSIn->m_nBlockSize;
     198          63 :     nBlockYSize = poDSIn->m_nBlockSize;
     199          63 : }
     200             : 
     201             : /************************************************************************/
     202             : /*                       ~GDALEEDAIRasterBand()                         */
     203             : /************************************************************************/
     204             : 
     205         126 : GDALEEDAIRasterBand::~GDALEEDAIRasterBand()
     206             : {
     207         126 : }
     208             : 
     209             : /************************************************************************/
     210             : /*                           GetOverviewCount()                         */
     211             : /************************************************************************/
     212             : 
     213          14 : int GDALEEDAIRasterBand::GetOverviewCount()
     214             : {
     215          14 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     216          14 :     return static_cast<int>(poGDS->m_apoOverviewDS.size());
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                              GetOverview()                           */
     221             : /************************************************************************/
     222             : 
     223          12 : GDALRasterBand *GDALEEDAIRasterBand::GetOverview(int iIndex)
     224             : {
     225          12 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     226          12 :     if (iIndex >= 0 && iIndex < static_cast<int>(poGDS->m_apoOverviewDS.size()))
     227             :     {
     228          10 :         return poGDS->m_apoOverviewDS[iIndex]->GetRasterBand(nBand);
     229             :     }
     230           2 :     return nullptr;
     231             : }
     232             : 
     233             : /************************************************************************/
     234             : /*                            DecodeNPYArray()                          */
     235             : /************************************************************************/
     236             : 
     237           1 : bool GDALEEDAIRasterBand::DecodeNPYArray(const GByte *pabyData, int nDataLen,
     238             :                                          bool bQueryAllBands, void *pDstBuffer,
     239             :                                          int nBlockXOff, int nBlockYOff,
     240             :                                          int nXBlocks, int nYBlocks,
     241             :                                          int nReqXSize, int nReqYSize) const
     242             : {
     243           1 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     244             : 
     245             :     // See https://docs.scipy.org/doc/numpy-1.13.0/neps/npy-format.html
     246             :     // for description of NPY array serialization format
     247           1 :     if (nDataLen < 10)
     248             :     {
     249           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Non NPY array returned");
     250           0 :         return false;
     251             :     }
     252             : 
     253           1 :     if (memcmp(pabyData, "\x93NUMPY", 6) != 0)
     254             :     {
     255           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Non NPY array returned");
     256           0 :         return false;
     257             :     }
     258           1 :     const int nVersionMajor = pabyData[6];
     259           1 :     if (nVersionMajor != 1)
     260             :     {
     261           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     262             :                  "Only version 1 of NPY array supported. Here found %d",
     263             :                  nVersionMajor);
     264           0 :         return false;
     265             :     }
     266             :     // Ignore version minor
     267           1 :     const int nHeaderLen = pabyData[8] | (pabyData[9] << 8);
     268           1 :     if (nDataLen < 10 + nHeaderLen)
     269             :     {
     270           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     271             :                  "Corrupted NPY array returned: not enough bytes for header");
     272           0 :         return false;
     273             :     }
     274             : 
     275             : #ifdef DEBUG
     276           2 :     CPLString osDescr;
     277           1 :     osDescr.assign(reinterpret_cast<const char *>(pabyData) + 10, nHeaderLen);
     278             :     // Should be something like
     279             :     // {'descr': [('B2', '<u2'), ('B3', '<u2'), ('B4', '<u2'), ('B8', '<u2'),
     280             :     // ('QA10', '<u2')], 'fortran_order': False, 'shape': (256, 256), }
     281           1 :     CPLDebug("EEDAI", "NPY descr: %s", osDescr.c_str());
     282             :     // TODO: validate that the descr is the one expected
     283             : #endif
     284             : 
     285           1 :     int nTotalDataTypeSize = 0;
     286           3 :     for (int i = 1; i <= poGDS->GetRasterCount(); i++)
     287             :     {
     288           2 :         if (bQueryAllBands || i == nBand)
     289             :         {
     290           2 :             nTotalDataTypeSize += GDALGetDataTypeSizeBytes(
     291             :                 poGDS->GetRasterBand(i)->GetRasterDataType());
     292             :         }
     293             :     }
     294           1 :     int nDataSize = nTotalDataTypeSize * nReqXSize * nReqYSize;
     295           1 :     if (nDataLen < 10 + nHeaderLen + nDataSize)
     296             :     {
     297           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     298             :                  "Corrupted NPY array returned: not enough bytes for payload. "
     299             :                  "%d needed, only %d found",
     300           0 :                  10 + nHeaderLen + nDataSize, nDataLen);
     301           0 :         return false;
     302             :     }
     303           1 :     else if (nDataLen > 10 + nHeaderLen + nDataSize)
     304             :     {
     305           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     306             :                  "Possibly corrupted NPY array returned: "
     307             :                  "expected bytes for payload. "
     308             :                  "%d needed, got %d found",
     309           0 :                  10 + nHeaderLen + nDataSize, nDataLen);
     310             :     }
     311             : 
     312           2 :     for (int iYBlock = 0; iYBlock < nYBlocks; iYBlock++)
     313             :     {
     314           1 :         int nBlockActualYSize = nBlockYSize;
     315           1 :         if ((iYBlock + nBlockYOff + 1) * nBlockYSize > nRasterYSize)
     316             :         {
     317           1 :             nBlockActualYSize =
     318           1 :                 nRasterYSize - (iYBlock + nBlockYOff) * nBlockYSize;
     319             :         }
     320             : 
     321           2 :         for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
     322             :         {
     323           1 :             int nBlockActualXSize = nBlockXSize;
     324           1 :             if ((iXBlock + nBlockXOff + 1) * nBlockXSize > nRasterXSize)
     325             :             {
     326           1 :                 nBlockActualXSize =
     327           1 :                     nRasterXSize - (iXBlock + nBlockXOff) * nBlockXSize;
     328             :             }
     329             : 
     330           1 :             int nOffsetBand =
     331           1 :                 10 + nHeaderLen +
     332           1 :                 (iYBlock * nBlockYSize * nReqXSize + iXBlock * nBlockXSize) *
     333             :                     nTotalDataTypeSize;
     334             : 
     335           3 :             for (int i = 1; i <= poGDS->GetRasterCount(); i++)
     336             :             {
     337           2 :                 GDALRasterBlock *poBlock = nullptr;
     338             :                 GByte *pabyDstBuffer;
     339           2 :                 if (i == nBand && pDstBuffer != nullptr)
     340           0 :                     pabyDstBuffer = reinterpret_cast<GByte *>(pDstBuffer);
     341           2 :                 else if (bQueryAllBands ||
     342           0 :                          (i == nBand && pDstBuffer == nullptr))
     343             :                 {
     344             :                     GDALEEDAIRasterBand *poOtherBand =
     345             :                         reinterpret_cast<GDALEEDAIRasterBand *>(
     346           2 :                             poGDS->GetRasterBand(i));
     347           4 :                     poBlock = poOtherBand->TryGetLockedBlockRef(
     348           2 :                         nBlockXOff + iXBlock, nBlockYOff + iYBlock);
     349           2 :                     if (poBlock != nullptr)
     350             :                     {
     351           0 :                         poBlock->DropLock();
     352           0 :                         continue;
     353             :                     }
     354           4 :                     poBlock = poOtherBand->GetLockedBlockRef(
     355           2 :                         nBlockXOff + iXBlock, nBlockYOff + iYBlock, TRUE);
     356           2 :                     if (poBlock == nullptr)
     357             :                     {
     358           0 :                         continue;
     359             :                     }
     360             :                     pabyDstBuffer =
     361           2 :                         reinterpret_cast<GByte *>(poBlock->GetDataRef());
     362             :                 }
     363             :                 else
     364             :                 {
     365           0 :                     continue;
     366             :                 }
     367             : 
     368           2 :                 GDALDataType eDT = poGDS->GetRasterBand(i)->GetRasterDataType();
     369           2 :                 const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
     370             : 
     371          80 :                 for (int iLine = 0; iLine < nBlockActualYSize; iLine++)
     372             :                 {
     373          78 :                     GByte *pabyLineDest =
     374          78 :                         pabyDstBuffer + iLine * nDTSize * nBlockXSize;
     375          78 :                     GDALCopyWords(const_cast<GByte *>(pabyData) + nOffsetBand +
     376          78 :                                       iLine * nTotalDataTypeSize * nReqXSize,
     377             :                                   eDT, nTotalDataTypeSize, pabyLineDest, eDT,
     378             :                                   nDTSize, nBlockActualXSize);
     379             : #ifdef CPL_MSB
     380             :                     if (nDTSize > 1)
     381             :                     {
     382             :                         GDALSwapWords(pabyLineDest, nDTSize, nBlockActualXSize,
     383             :                                       nDTSize);
     384             :                     }
     385             : #endif
     386             :                 }
     387             : 
     388           2 :                 nOffsetBand += nDTSize;
     389             : 
     390           2 :                 if (poBlock)
     391           2 :                     poBlock->DropLock();
     392             :             }
     393             :         }
     394             :     }
     395           1 :     return true;
     396             : }
     397             : 
     398             : /************************************************************************/
     399             : /*                            DecodeGDALDataset()                         */
     400             : /************************************************************************/
     401             : 
     402           4 : bool GDALEEDAIRasterBand::DecodeGDALDataset(const GByte *pabyData, int nDataLen,
     403             :                                             bool bQueryAllBands,
     404             :                                             void *pDstBuffer, int nBlockXOff,
     405             :                                             int nBlockYOff, int nXBlocks,
     406             :                                             int nYBlocks, int nReqXSize,
     407             :                                             int nReqYSize)
     408             : {
     409           4 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     410             : 
     411           8 :     const CPLString osTmpFilename(VSIMemGenerateHiddenFilename("eedai"));
     412           4 :     VSIFCloseL(VSIFileFromMemBuffer(
     413             :         osTmpFilename, const_cast<GByte *>(pabyData), nDataLen, false));
     414           4 :     const char *const apszDrivers[] = {"PNG", "JPEG", "GTIFF", nullptr};
     415           4 :     GDALDataset *poTileDS = GDALDataset::FromHandle(GDALOpenEx(
     416             :         osTmpFilename, GDAL_OF_RASTER, apszDrivers, nullptr, nullptr));
     417           4 :     if (poTileDS == nullptr)
     418             :     {
     419           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     420             :                  "Cannot decode buffer returned by the "
     421             :                  "server as a PNG, JPEG or GeoTIFF image");
     422           0 :         VSIUnlink(osTmpFilename);
     423           0 :         return false;
     424             :     }
     425           4 :     if (poTileDS->GetRasterXSize() != nReqXSize ||
     426           8 :         poTileDS->GetRasterYSize() != nReqYSize ||
     427             :         // The server might return a RGBA image even if only 3 bands are
     428             :         // requested
     429           4 :         poTileDS->GetRasterCount() <
     430           4 :             (bQueryAllBands ? poGDS->GetRasterCount() : 1))
     431             :     {
     432           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     433             :                  "Bad dimensions/band count for image returned "
     434             :                  "by server: %dx%dx%d",
     435             :                  poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
     436             :                  poTileDS->GetRasterCount());
     437           0 :         delete poTileDS;
     438           0 :         VSIUnlink(osTmpFilename);
     439           0 :         return false;
     440             :     }
     441             : 
     442           8 :     for (int iYBlock = 0; iYBlock < nYBlocks; iYBlock++)
     443             :     {
     444           4 :         int nBlockActualYSize = nBlockYSize;
     445           4 :         if ((iYBlock + nBlockYOff + 1) * nBlockYSize > nRasterYSize)
     446             :         {
     447           0 :             nBlockActualYSize =
     448           0 :                 nRasterYSize - (iYBlock + nBlockYOff) * nBlockYSize;
     449             :         }
     450             : 
     451           8 :         for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
     452             :         {
     453           4 :             int nBlockActualXSize = nBlockXSize;
     454           4 :             if ((iXBlock + nBlockXOff + 1) * nBlockXSize > nRasterXSize)
     455             :             {
     456           0 :                 nBlockActualXSize =
     457           0 :                     nRasterXSize - (iXBlock + nBlockXOff) * nBlockXSize;
     458             :             }
     459             : 
     460          14 :             for (int i = 1; i <= poGDS->GetRasterCount(); i++)
     461             :             {
     462          10 :                 GDALRasterBlock *poBlock = nullptr;
     463             :                 GByte *pabyDstBuffer;
     464          10 :                 if (i == nBand && pDstBuffer != nullptr)
     465           0 :                     pabyDstBuffer = reinterpret_cast<GByte *>(pDstBuffer);
     466          10 :                 else if (bQueryAllBands ||
     467           0 :                          (i == nBand && pDstBuffer == nullptr))
     468             :                 {
     469             :                     GDALEEDAIRasterBand *poOtherBand =
     470             :                         reinterpret_cast<GDALEEDAIRasterBand *>(
     471          10 :                             poGDS->GetRasterBand(i));
     472          20 :                     poBlock = poOtherBand->TryGetLockedBlockRef(
     473          10 :                         nBlockXOff + iXBlock, nBlockYOff + iYBlock);
     474          10 :                     if (poBlock != nullptr)
     475             :                     {
     476           0 :                         poBlock->DropLock();
     477           0 :                         continue;
     478             :                     }
     479          20 :                     poBlock = poOtherBand->GetLockedBlockRef(
     480          10 :                         nBlockXOff + iXBlock, nBlockYOff + iYBlock, TRUE);
     481          10 :                     if (poBlock == nullptr)
     482             :                     {
     483           0 :                         continue;
     484             :                     }
     485             :                     pabyDstBuffer =
     486          10 :                         reinterpret_cast<GByte *>(poBlock->GetDataRef());
     487             :                 }
     488             :                 else
     489             :                 {
     490           0 :                     continue;
     491             :                 }
     492             : 
     493          10 :                 GDALDataType eDT = poGDS->GetRasterBand(i)->GetRasterDataType();
     494          10 :                 const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
     495          10 :                 const int nTileBand = bQueryAllBands ? i : 1;
     496          10 :                 CPLErr eErr = poTileDS->GetRasterBand(nTileBand)->RasterIO(
     497          10 :                     GF_Read, iXBlock * nBlockXSize, iYBlock * nBlockYSize,
     498             :                     nBlockActualXSize, nBlockActualYSize, pabyDstBuffer,
     499             :                     nBlockActualXSize, nBlockActualYSize, eDT, nDTSize,
     500          10 :                     static_cast<GSpacing>(nDTSize) * nBlockXSize, nullptr);
     501             : 
     502          10 :                 if (poBlock)
     503          10 :                     poBlock->DropLock();
     504          10 :                 if (eErr != CE_None)
     505             :                 {
     506           0 :                     delete poTileDS;
     507           0 :                     VSIUnlink(osTmpFilename);
     508           0 :                     return false;
     509             :                 }
     510             :             }
     511             :         }
     512             :     }
     513             : 
     514           4 :     delete poTileDS;
     515           4 :     VSIUnlink(osTmpFilename);
     516           4 :     return true;
     517             : }
     518             : 
     519           5 : CPLErr GDALEEDAIRasterBand::GetBlocks(int nBlockXOff, int nBlockYOff,
     520             :                                       int nXBlocks, int nYBlocks,
     521             :                                       bool bQueryAllBands, void *pBuffer)
     522             : {
     523           5 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     524             : 
     525             :     // Build request content
     526           5 :     json_object *poReq = json_object_new_object();
     527           5 :     json_object_object_add(poReq, "fileFormat",
     528             :                            json_object_new_string(poGDS->m_osPixelEncoding));
     529           5 :     json_object *poBands = json_object_new_array();
     530          17 :     for (int i = 1; i <= poGDS->GetRasterCount(); i++)
     531             :     {
     532          12 :         if (bQueryAllBands || i == nBand)
     533             :         {
     534          12 :             json_object_array_add(
     535             :                 poBands, json_object_new_string(
     536          12 :                              poGDS->GetRasterBand(i)->GetDescription()));
     537             :         }
     538             :     }
     539           5 :     json_object_object_add(poReq, "bandIds", poBands);
     540             : 
     541           5 :     int nReqXSize = nBlockXSize * nXBlocks;
     542           5 :     if ((nBlockXOff + nXBlocks) * nBlockXSize > nRasterXSize)
     543           1 :         nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
     544           5 :     int nReqYSize = nBlockYSize * nYBlocks;
     545           5 :     if ((nBlockYOff + nYBlocks) * nBlockYSize > nRasterYSize)
     546           1 :         nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
     547             :     const double dfX0 =
     548           5 :         poGDS->m_gt[0] + nBlockXOff * nBlockXSize * poGDS->m_gt[1];
     549             :     const double dfY0 =
     550           5 :         poGDS->m_gt[3] + nBlockYOff * nBlockYSize * poGDS->m_gt[5];
     551             : #ifdef DEBUG_VERBOSE
     552             :     CPLDebug("EEDAI",
     553             :              "nBlockYOff=%d nBlockYOff=%d "
     554             :              "nXBlocks=%d nYBlocks=%d nReqXSize=%d nReqYSize=%d",
     555             :              nBlockYOff, nBlockYOff, nXBlocks, nYBlocks, nReqXSize, nReqYSize);
     556             : #endif
     557             : 
     558           5 :     json_object *poPixelGrid = json_object_new_object();
     559             : 
     560           5 :     json_object *poAffineTransform = json_object_new_object();
     561           5 :     json_object_object_add(
     562             :         poAffineTransform, "translateX",
     563             :         json_object_new_double_with_significant_figures(dfX0, 18));
     564           5 :     json_object_object_add(
     565             :         poAffineTransform, "translateY",
     566             :         json_object_new_double_with_significant_figures(dfY0, 18));
     567           5 :     json_object_object_add(
     568             :         poAffineTransform, "scaleX",
     569           5 :         json_object_new_double_with_significant_figures(poGDS->m_gt[1], 18));
     570           5 :     json_object_object_add(
     571             :         poAffineTransform, "scaleY",
     572           5 :         json_object_new_double_with_significant_figures(poGDS->m_gt[5], 18));
     573           5 :     json_object_object_add(
     574             :         poAffineTransform, "shearX",
     575             :         json_object_new_double_with_significant_figures(0.0, 18));
     576           5 :     json_object_object_add(
     577             :         poAffineTransform, "shearY",
     578             :         json_object_new_double_with_significant_figures(0.0, 18));
     579           5 :     json_object_object_add(poPixelGrid, "affineTransform", poAffineTransform);
     580             : 
     581           5 :     json_object *poDimensions = json_object_new_object();
     582           5 :     json_object_object_add(poDimensions, "width",
     583             :                            json_object_new_int(nReqXSize));
     584           5 :     json_object_object_add(poDimensions, "height",
     585             :                            json_object_new_int(nReqYSize));
     586           5 :     json_object_object_add(poPixelGrid, "dimensions", poDimensions);
     587           5 :     json_object_object_add(poReq, "grid", poPixelGrid);
     588             : 
     589          10 :     CPLString osPostContent = json_object_get_string(poReq);
     590           5 :     json_object_put(poReq);
     591             : 
     592             :     // Issue request
     593           5 :     char **papszOptions = (poGDS->m_poParentDS)
     594           5 :                               ? poGDS->m_poParentDS->GetBaseHTTPOptions()
     595           4 :                               : poGDS->GetBaseHTTPOptions();
     596           5 :     papszOptions = CSLSetNameValue(papszOptions, "CUSTOMREQUEST", "POST");
     597          10 :     CPLString osHeaders = CSLFetchNameValueDef(papszOptions, "HEADERS", "");
     598           5 :     if (!osHeaders.empty())
     599           5 :         osHeaders += "\r\n";
     600           5 :     osHeaders += "Content-Type: application/json";
     601           5 :     papszOptions = CSLSetNameValue(papszOptions, "HEADERS", osHeaders);
     602           5 :     papszOptions = CSLSetNameValue(papszOptions, "POSTFIELDS", osPostContent);
     603           5 :     CPLHTTPResult *psResult = EEDAHTTPFetch(
     604          10 :         (poGDS->m_osBaseURL + poGDS->m_osAssetName + ":getPixels").c_str(),
     605             :         papszOptions);
     606           5 :     CSLDestroy(papszOptions);
     607           5 :     if (psResult == nullptr)
     608           0 :         return CE_Failure;
     609             : 
     610           5 :     if (psResult->pszErrBuf != nullptr)
     611             :     {
     612           0 :         if (psResult->pabyData)
     613             :         {
     614           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s: %s", psResult->pszErrBuf,
     615           0 :                      reinterpret_cast<const char *>(psResult->pabyData));
     616             :         }
     617             :         else
     618             :         {
     619           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf);
     620             :         }
     621           0 :         CPLHTTPDestroyResult(psResult);
     622           0 :         return CE_Failure;
     623             :     }
     624             : 
     625           5 :     if (psResult->pabyData == nullptr)
     626             :     {
     627           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     628             :                  "Empty content returned by server");
     629           0 :         CPLHTTPDestroyResult(psResult);
     630           0 :         return CE_Failure;
     631             :     }
     632             : #ifdef DEBUG_VERBOSE
     633             :     CPLDebug("EEADI", "Result: %s (%d bytes)",
     634             :              reinterpret_cast<const char *>(psResult->pabyData),
     635             :              psResult->nDataLen);
     636             : #endif
     637             : 
     638           5 :     if (EQUAL(poGDS->m_osPixelEncoding, "NPY"))
     639             :     {
     640           1 :         if (!DecodeNPYArray(psResult->pabyData, psResult->nDataLen,
     641             :                             bQueryAllBands, pBuffer, nBlockXOff, nBlockYOff,
     642             :                             nXBlocks, nYBlocks, nReqXSize, nReqYSize))
     643             :         {
     644           0 :             CPLHTTPDestroyResult(psResult);
     645           0 :             return CE_Failure;
     646             :         }
     647             :     }
     648             :     else
     649             :     {
     650           4 :         if (!DecodeGDALDataset(psResult->pabyData, psResult->nDataLen,
     651             :                                bQueryAllBands, pBuffer, nBlockXOff, nBlockYOff,
     652             :                                nXBlocks, nYBlocks, nReqXSize, nReqYSize))
     653             :         {
     654           0 :             CPLHTTPDestroyResult(psResult);
     655           0 :             return CE_Failure;
     656             :         }
     657             :     }
     658             : 
     659           5 :     CPLHTTPDestroyResult(psResult);
     660             : 
     661           5 :     return CE_None;
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                             IReadBlock()                             */
     666             : /************************************************************************/
     667             : 
     668           0 : CPLErr GDALEEDAIRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     669             :                                        void *pBuffer)
     670             : {
     671           0 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     672             : #ifdef DEBUG_VERBOSE
     673             :     CPLDebug("EEDAI", "ReadBlock x=%d y=%d band=%d level=%d", nBlockXOff,
     674             :              nBlockYOff, nBand, poGDS->m_iOvrLevel);
     675             : #endif
     676             : 
     677           0 :     return GetBlocks(nBlockXOff, nBlockYOff, 1, 1, poGDS->m_bQueryMultipleBands,
     678           0 :                      pBuffer);
     679             : }
     680             : 
     681             : /************************************************************************/
     682             : /*                          PrefetchBlocks()                            */
     683             : /************************************************************************/
     684             : 
     685             : // Return or'ed flags among 0, RETRY_PER_BAND, RETRY_SPATIAL_SPLIT if the user
     686             : // should try to split the request in smaller chunks
     687             : 
     688          17 : GUInt32 GDALEEDAIRasterBand::PrefetchBlocks(int nXOff, int nYOff, int nXSize,
     689             :                                             int nYSize, int nBufXSize,
     690             :                                             int nBufYSize, bool bQueryAllBands)
     691             : {
     692          17 :     CPL_IGNORE_RET_VAL(nBufXSize);
     693          17 :     CPL_IGNORE_RET_VAL(nBufYSize);
     694             : 
     695          17 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     696          17 :     int nBlockXOff = nXOff / nBlockXSize;
     697          17 :     int nBlockYOff = nYOff / nBlockYSize;
     698          17 :     int nXBlocks = (nXOff + nXSize - 1) / nBlockXSize - nBlockXOff + 1;
     699          17 :     int nYBlocks = (nYOff + nYSize - 1) / nBlockYSize - nBlockYOff + 1;
     700             : 
     701          17 :     const int nThisDTSize = GDALGetDataTypeSizeBytes(GetRasterDataType());
     702          17 :     int nTotalDataTypeSize = 0;
     703          17 :     int nQueriedBands = 0;
     704          64 :     for (int i = 1; i <= poGDS->GetRasterCount(); i++)
     705             :     {
     706          47 :         if (bQueryAllBands || i == nBand)
     707             :         {
     708          47 :             nQueriedBands++;
     709          47 :             nTotalDataTypeSize += GDALGetDataTypeSizeBytes(
     710             :                 poGDS->GetRasterBand(i)->GetRasterDataType());
     711             :         }
     712             :     }
     713             : 
     714             :     // Check the number of already cached blocks, and remove fully
     715             :     // cached lines at the top of the area of interest from the queried
     716             :     // blocks
     717          17 :     int nBlocksCached = 0;
     718          17 :     int nBlocksCachedForThisBand = 0;
     719          17 :     bool bAllLineCached = true;
     720          34 :     for (int iYBlock = 0; iYBlock < nYBlocks;)
     721             :     {
     722          34 :         for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
     723             :         {
     724          64 :             for (int i = 1; i <= poGDS->GetRasterCount(); i++)
     725             :             {
     726          47 :                 GDALRasterBlock *poBlock = nullptr;
     727          47 :                 if (bQueryAllBands || i == nBand)
     728             :                 {
     729             :                     GDALEEDAIRasterBand *poOtherBand =
     730             :                         reinterpret_cast<GDALEEDAIRasterBand *>(
     731          47 :                             poGDS->GetRasterBand(i));
     732          94 :                     poBlock = poOtherBand->TryGetLockedBlockRef(
     733          47 :                         nBlockXOff + iXBlock, nBlockYOff + iYBlock);
     734          47 :                     if (poBlock != nullptr)
     735             :                     {
     736          35 :                         nBlocksCached++;
     737          35 :                         if (i == nBand)
     738          12 :                             nBlocksCachedForThisBand++;
     739          35 :                         poBlock->DropLock();
     740          35 :                         continue;
     741             :                     }
     742             :                     else
     743             :                     {
     744          12 :                         bAllLineCached = false;
     745             :                     }
     746             :                 }
     747             :             }
     748             :         }
     749             : 
     750          17 :         if (bAllLineCached)
     751             :         {
     752          12 :             nBlocksCached -= nXBlocks * nQueriedBands;
     753          12 :             nBlocksCachedForThisBand -= nXBlocks;
     754          12 :             nBlockYOff++;
     755          12 :             nYBlocks--;
     756             :         }
     757             :         else
     758             :         {
     759           5 :             iYBlock++;
     760             :         }
     761             :     }
     762             : 
     763          17 :     if (nXBlocks > 0 && nYBlocks > 0)
     764             :     {
     765           5 :         bool bMustReturn = false;
     766           5 :         GUInt32 nRetryFlags = 0;
     767             : 
     768             :         // Get the blocks if the number of already cached blocks is lesser
     769             :         // than 25% of the to be queried blocks
     770           5 :         if (nBlocksCached > (nQueriedBands * nXBlocks * nYBlocks) / 4)
     771             :         {
     772           0 :             if (nBlocksCachedForThisBand <= (nXBlocks * nYBlocks) / 4)
     773             :             {
     774           0 :                 nRetryFlags |= RETRY_PER_BAND;
     775             :             }
     776             :             else
     777             :             {
     778           0 :                 bMustReturn = true;
     779             :             }
     780             :         }
     781             : 
     782             :         // Don't request too many pixels in one dimension
     783           5 :         if (nXBlocks * nBlockXSize > SERVER_DIMENSION_LIMIT ||
     784           5 :             nYBlocks * nBlockYSize > SERVER_DIMENSION_LIMIT)
     785             :         {
     786           0 :             bMustReturn = true;
     787           0 :             nRetryFlags |= RETRY_SPATIAL_SPLIT;
     788             :         }
     789             : 
     790             :         // Make sure that we have enough cache (with a margin of 50%)
     791             :         // and the number of queried pixels isn't too big w.r.t server
     792             :         // limit
     793           5 :         const GIntBig nUncompressedSize = static_cast<GIntBig>(nXBlocks) *
     794           5 :                                           nYBlocks * nBlockXSize * nBlockYSize *
     795           5 :                                           nTotalDataTypeSize;
     796           5 :         const GIntBig nCacheMax = GDALGetCacheMax64() / 2;
     797           5 :         if (nUncompressedSize > nCacheMax ||
     798             :             nUncompressedSize > SERVER_BYTE_LIMIT)
     799             :         {
     800           0 :             if (bQueryAllBands && poGDS->GetRasterCount() > 1)
     801             :             {
     802           0 :                 const GIntBig nUncompressedSizeThisBand =
     803           0 :                     static_cast<GIntBig>(nXBlocks) * nYBlocks * nBlockXSize *
     804           0 :                     nBlockYSize * nThisDTSize;
     805           0 :                 if (nUncompressedSizeThisBand <= SERVER_BYTE_LIMIT &&
     806             :                     nUncompressedSizeThisBand <= nCacheMax)
     807             :                 {
     808           0 :                     nRetryFlags |= RETRY_PER_BAND;
     809             :                 }
     810             :             }
     811           0 :             if (nXBlocks > 1 || nYBlocks > 1)
     812             :             {
     813           0 :                 nRetryFlags |= RETRY_SPATIAL_SPLIT;
     814             :             }
     815           0 :             return nRetryFlags;
     816             :         }
     817           5 :         if (bMustReturn)
     818           0 :             return nRetryFlags;
     819             : 
     820           5 :         GetBlocks(nBlockXOff, nBlockYOff, nXBlocks, nYBlocks, bQueryAllBands,
     821             :                   nullptr);
     822             :     }
     823             : 
     824          17 :     return 0;
     825             : }
     826             : 
     827             : /************************************************************************/
     828             : /*                              IRasterIO()                             */
     829             : /************************************************************************/
     830             : 
     831          15 : CPLErr GDALEEDAIRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     832             :                                       int nXSize, int nYSize, void *pData,
     833             :                                       int nBufXSize, int nBufYSize,
     834             :                                       GDALDataType eBufType,
     835             :                                       GSpacing nPixelSpace, GSpacing nLineSpace,
     836             :                                       GDALRasterIOExtraArg *psExtraArg)
     837             : 
     838             : {
     839             : 
     840             :     /* ==================================================================== */
     841             :     /*      Do we have overviews that would be appropriate to satisfy       */
     842             :     /*      this request?                                                   */
     843             :     /* ==================================================================== */
     844          15 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0 &&
     845             :         eRWFlag == GF_Read)
     846             :     {
     847             :         GDALRasterIOExtraArg sExtraArg;
     848           1 :         GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
     849             : 
     850             :         const int nOverview =
     851           1 :             GDALBandGetBestOverviewLevel2(this, nXOff, nYOff, nXSize, nYSize,
     852             :                                           nBufXSize, nBufYSize, &sExtraArg);
     853           1 :         if (nOverview >= 0)
     854             :         {
     855           1 :             GDALRasterBand *poOverviewBand = GetOverview(nOverview);
     856           1 :             if (poOverviewBand == nullptr)
     857           1 :                 return CE_Failure;
     858             : 
     859           1 :             return poOverviewBand->RasterIO(
     860             :                 eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
     861           1 :                 nBufYSize, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
     862             :         }
     863             :     }
     864             : 
     865          14 :     GDALEEDAIDataset *poGDS = reinterpret_cast<GDALEEDAIDataset *>(poDS);
     866             :     GUInt32 nRetryFlags =
     867          28 :         PrefetchBlocks(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
     868          14 :                        poGDS->m_bQueryMultipleBands);
     869          14 :     if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
     870           0 :         nYSize == nBufYSize && nYSize > nBlockYSize)
     871             :     {
     872             :         GDALRasterIOExtraArg sExtraArg;
     873           0 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
     874             : 
     875             :         int nHalf =
     876           0 :             std::max(nBlockYSize, ((nYSize / 2) / nBlockYSize) * nBlockYSize);
     877             :         CPLErr eErr =
     878           0 :             IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nHalf, pData, nXSize,
     879             :                       nHalf, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
     880           0 :         if (eErr == CE_None)
     881             :         {
     882           0 :             eErr = IRasterIO(
     883             :                 eRWFlag, nXOff, nYOff + nHalf, nXSize, nYSize - nHalf,
     884           0 :                 static_cast<GByte *>(pData) + nHalf * nLineSpace, nXSize,
     885             :                 nYSize - nHalf, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
     886             :         }
     887           0 :         return eErr;
     888             :     }
     889          14 :     else if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
     890           0 :              nYSize == nBufYSize && nXSize > nBlockXSize)
     891             :     {
     892             :         GDALRasterIOExtraArg sExtraArg;
     893           0 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
     894             : 
     895             :         int nHalf =
     896           0 :             std::max(nBlockXSize, ((nXSize / 2) / nBlockXSize) * nBlockXSize);
     897             :         CPLErr eErr =
     898           0 :             IRasterIO(eRWFlag, nXOff, nYOff, nHalf, nYSize, pData, nHalf,
     899             :                       nYSize, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
     900           0 :         if (eErr == CE_None)
     901             :         {
     902             :             eErr =
     903           0 :                 IRasterIO(eRWFlag, nXOff + nHalf, nYOff, nXSize - nHalf, nYSize,
     904           0 :                           static_cast<GByte *>(pData) + nHalf * nPixelSpace,
     905             :                           nXSize - nHalf, nYSize, eBufType, nPixelSpace,
     906             :                           nLineSpace, &sExtraArg);
     907             :         }
     908           0 :         return eErr;
     909             :     }
     910          14 :     else if ((nRetryFlags & RETRY_PER_BAND) && poGDS->m_bQueryMultipleBands &&
     911           0 :              poGDS->nBands > 1)
     912             :     {
     913           0 :         CPL_IGNORE_RET_VAL(PrefetchBlocks(nXOff, nYOff, nXSize, nYSize,
     914             :                                           nBufXSize, nBufYSize, false));
     915             :     }
     916             : 
     917          14 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     918             :                                      pData, nBufXSize, nBufYSize, eBufType,
     919          14 :                                      nPixelSpace, nLineSpace, psExtraArg);
     920             : }
     921             : 
     922             : /************************************************************************/
     923             : /*                              IRasterIO()                             */
     924             : /************************************************************************/
     925             : 
     926           4 : CPLErr GDALEEDAIDataset::IRasterIO(
     927             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     928             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
     929             :     int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
     930             :     GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
     931             : {
     932             : 
     933             :     /* ==================================================================== */
     934             :     /*      Do we have overviews that would be appropriate to satisfy       */
     935             :     /*      this request?                                                   */
     936             :     /* ==================================================================== */
     937           3 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) &&
     938           7 :         GetRasterBand(1)->GetOverviewCount() > 0 && eRWFlag == GF_Read)
     939             :     {
     940             :         GDALRasterIOExtraArg sExtraArg;
     941           1 :         GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
     942             : 
     943           1 :         const int nOverview = GDALBandGetBestOverviewLevel2(
     944             :             GetRasterBand(1), nXOff, nYOff, nXSize, nYSize, nBufXSize,
     945             :             nBufYSize, &sExtraArg);
     946           1 :         if (nOverview >= 0)
     947             :         {
     948             :             GDALRasterBand *poOverviewBand =
     949           1 :                 GetRasterBand(1)->GetOverview(nOverview);
     950           2 :             if (poOverviewBand == nullptr ||
     951           1 :                 poOverviewBand->GetDataset() == nullptr)
     952             :             {
     953           1 :                 return CE_Failure;
     954             :             }
     955             : 
     956           1 :             return poOverviewBand->GetDataset()->RasterIO(
     957             :                 eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
     958             :                 nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
     959           1 :                 nLineSpace, nBandSpace, &sExtraArg);
     960             :         }
     961             :     }
     962             : 
     963             :     GDALEEDAIRasterBand *poBand =
     964           3 :         cpl::down_cast<GDALEEDAIRasterBand *>(GetRasterBand(1));
     965             : 
     966             :     GUInt32 nRetryFlags =
     967           6 :         poBand->PrefetchBlocks(nXOff, nYOff, nXSize, nYSize, nBufXSize,
     968           3 :                                nBufYSize, m_bQueryMultipleBands);
     969             :     int nBlockXSize, nBlockYSize;
     970           3 :     poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     971           3 :     if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
     972           0 :         nYSize == nBufYSize && nYSize > nBlockYSize)
     973             :     {
     974             :         GDALRasterIOExtraArg sExtraArg;
     975           0 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
     976             : 
     977             :         int nHalf =
     978           0 :             std::max(nBlockYSize, ((nYSize / 2) / nBlockYSize) * nBlockYSize);
     979             :         CPLErr eErr =
     980           0 :             IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nHalf, pData, nXSize,
     981             :                       nHalf, eBufType, nBandCount, panBandMap, nPixelSpace,
     982             :                       nLineSpace, nBandSpace, &sExtraArg);
     983           0 :         if (eErr == CE_None)
     984             :         {
     985           0 :             eErr = IRasterIO(
     986             :                 eRWFlag, nXOff, nYOff + nHalf, nXSize, nYSize - nHalf,
     987           0 :                 static_cast<GByte *>(pData) + nHalf * nLineSpace, nXSize,
     988             :                 nYSize - nHalf, eBufType, nBandCount, panBandMap, nPixelSpace,
     989             :                 nLineSpace, nBandSpace, &sExtraArg);
     990             :         }
     991           0 :         return eErr;
     992             :     }
     993           3 :     else if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
     994           0 :              nYSize == nBufYSize && nXSize > nBlockXSize)
     995             :     {
     996             :         GDALRasterIOExtraArg sExtraArg;
     997           0 :         INIT_RASTERIO_EXTRA_ARG(sExtraArg);
     998             : 
     999             :         int nHalf =
    1000           0 :             std::max(nBlockXSize, ((nXSize / 2) / nBlockXSize) * nBlockXSize);
    1001             :         CPLErr eErr =
    1002           0 :             IRasterIO(eRWFlag, nXOff, nYOff, nHalf, nYSize, pData, nHalf,
    1003             :                       nYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
    1004             :                       nLineSpace, nBandSpace, &sExtraArg);
    1005           0 :         if (eErr == CE_None)
    1006             :         {
    1007           0 :             eErr = IRasterIO(
    1008             :                 eRWFlag, nXOff + nHalf, nYOff, nXSize - nHalf, nYSize,
    1009           0 :                 static_cast<GByte *>(pData) + nHalf * nPixelSpace,
    1010             :                 nXSize - nHalf, nYSize, eBufType, nBandCount, panBandMap,
    1011             :                 nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
    1012             :         }
    1013           0 :         return eErr;
    1014             :     }
    1015           3 :     else if ((nRetryFlags & RETRY_PER_BAND) && m_bQueryMultipleBands &&
    1016           0 :              nBands > 1)
    1017             :     {
    1018           0 :         for (int iBand = 1; iBand <= nBands; iBand++)
    1019             :         {
    1020             :             poBand =
    1021           0 :                 cpl::down_cast<GDALEEDAIRasterBand *>(GetRasterBand(iBand));
    1022           0 :             CPL_IGNORE_RET_VAL(poBand->PrefetchBlocks(
    1023             :                 nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, false));
    1024             :         }
    1025             :     }
    1026             : 
    1027           3 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1028             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
    1029             :                                   panBandMap, nPixelSpace, nLineSpace,
    1030           3 :                                   nBandSpace, psExtraArg);
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                        ComputeQueryStrategy()                        */
    1035             : /************************************************************************/
    1036             : 
    1037          35 : bool GDALEEDAIDataset::ComputeQueryStrategy()
    1038             : {
    1039          35 :     m_bQueryMultipleBands = true;
    1040          35 :     m_osPixelEncoding.toupper();
    1041             : 
    1042          35 :     bool bHeterogeneousDataTypes = false;
    1043          35 :     if (nBands >= 2)
    1044             :     {
    1045          24 :         GDALDataType eDTFirstBand = GetRasterBand(1)->GetRasterDataType();
    1046          52 :         for (int i = 2; i <= nBands; i++)
    1047             :         {
    1048          28 :             if (GetRasterBand(i)->GetRasterDataType() != eDTFirstBand)
    1049             :             {
    1050           0 :                 bHeterogeneousDataTypes = true;
    1051           0 :                 break;
    1052             :             }
    1053             :         }
    1054             :     }
    1055             : 
    1056          35 :     if (EQUAL(m_osPixelEncoding, "AUTO"))
    1057             :     {
    1058          31 :         if (bHeterogeneousDataTypes)
    1059             :         {
    1060           0 :             m_osPixelEncoding = "NPY";
    1061             :         }
    1062             :         else
    1063             :         {
    1064          31 :             m_osPixelEncoding = "PNG";
    1065          86 :             for (int i = 1; i <= nBands; i++)
    1066             :             {
    1067          55 :                 if (GetRasterBand(i)->GetRasterDataType() != GDT_Byte)
    1068             :                 {
    1069          43 :                     m_osPixelEncoding = "GEO_TIFF";
    1070             :                 }
    1071             :             }
    1072             :         }
    1073             :     }
    1074             : 
    1075          66 :     if (EQUAL(m_osPixelEncoding, "PNG") || EQUAL(m_osPixelEncoding, "JPEG") ||
    1076          31 :         EQUAL(m_osPixelEncoding, "AUTO_JPEG_PNG"))
    1077             :     {
    1078           4 :         if (nBands != 1 && nBands != 3)
    1079             :         {
    1080           0 :             m_bQueryMultipleBands = false;
    1081             :         }
    1082          16 :         for (int i = 1; i <= nBands; i++)
    1083             :         {
    1084          12 :             if (GetRasterBand(i)->GetRasterDataType() != GDT_Byte)
    1085             :             {
    1086           0 :                 CPLError(
    1087             :                     CE_Failure, CPLE_NotSupported,
    1088             :                     "This dataset has non-Byte bands, which is incompatible "
    1089             :                     "with PIXEL_ENCODING=%s",
    1090             :                     m_osPixelEncoding.c_str());
    1091           0 :                 return false;
    1092             :             }
    1093             :         }
    1094             :     }
    1095             : 
    1096          35 :     if (nBands > SERVER_SIMUTANEOUS_BAND_LIMIT)
    1097             :     {
    1098           0 :         m_bQueryMultipleBands = false;
    1099             :     }
    1100             : 
    1101          35 :     if (m_bQueryMultipleBands && m_osPixelEncoding != "NPY" &&
    1102             :         bHeterogeneousDataTypes)
    1103             :     {
    1104           0 :         CPLDebug("EEDAI",
    1105             :                  "%s PIXEL_ENCODING does not support heterogeneous data types. "
    1106             :                  "Falling back to querying band per band",
    1107             :                  m_osPixelEncoding.c_str());
    1108           0 :         m_bQueryMultipleBands = false;
    1109             :     }
    1110             : 
    1111          35 :     return true;
    1112             : }
    1113             : 
    1114             : /************************************************************************/
    1115             : /*                          GetSpatialRef()                             */
    1116             : /************************************************************************/
    1117             : 
    1118           3 : const OGRSpatialReference *GDALEEDAIDataset::GetSpatialRef() const
    1119             : {
    1120           3 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
    1121             : }
    1122             : 
    1123             : /************************************************************************/
    1124             : /*                          GetGeoTransform()                           */
    1125             : /************************************************************************/
    1126             : 
    1127           8 : CPLErr GDALEEDAIDataset::GetGeoTransform(GDALGeoTransform &gt) const
    1128             : {
    1129           8 :     gt = m_gt;
    1130           8 :     return CE_None;
    1131             : }
    1132             : 
    1133             : /************************************************************************/
    1134             : /*                               Open()                                 */
    1135             : /************************************************************************/
    1136             : 
    1137           9 : bool GDALEEDAIDataset::Open(GDALOpenInfo *poOpenInfo)
    1138             : {
    1139             :     m_osBaseURL = CPLGetConfigOption(
    1140           9 :         "EEDA_URL", "https://earthengine-highvolume.googleapis.com/v1alpha/");
    1141             : 
    1142           9 :     m_osAsset = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "ASSET", "");
    1143             :     CPLString osBandList(
    1144          18 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "BANDS", ""));
    1145           9 :     if (m_osAsset.empty())
    1146             :     {
    1147             :         char **papszTokens =
    1148           9 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":", 0);
    1149           9 :         if (CSLCount(papszTokens) < 2)
    1150             :         {
    1151           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1152             :                      "No asset specified in connection string or "
    1153             :                      "ASSET open option");
    1154           0 :             CSLDestroy(papszTokens);
    1155           0 :             return false;
    1156             :         }
    1157           9 :         if (CSLCount(papszTokens) == 3)
    1158             :         {
    1159           2 :             osBandList = papszTokens[2];
    1160             :         }
    1161             : 
    1162           9 :         m_osAsset = papszTokens[1];
    1163           9 :         CSLDestroy(papszTokens);
    1164             :     }
    1165           9 :     m_osAssetName = ConvertPathToName(m_osAsset);
    1166             : 
    1167           9 :     m_osPixelEncoding = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
    1168           9 :                                              "PIXEL_ENCODING", "AUTO");
    1169           9 :     m_nBlockSize =
    1170           9 :         atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "BLOCK_SIZE",
    1171             :                                   CPLSPrintf("%d", DEFAULT_BLOCK_SIZE)));
    1172           9 :     if (m_nBlockSize < 128 &&
    1173           0 :         !CPLTestBool(CPLGetConfigOption("EEDA_FORCE_BLOCK_SIZE", "FALSE")))
    1174             :     {
    1175           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid BLOCK_SIZE");
    1176           0 :         return false;
    1177             :     }
    1178             : 
    1179          18 :     std::set<CPLString> oSetUserBandNames;
    1180             :     {
    1181           9 :         char **papszTokens = CSLTokenizeString2(osBandList, ",", 0);
    1182          12 :         for (int i = 0; papszTokens && papszTokens[i]; i++)
    1183           3 :             oSetUserBandNames.insert(papszTokens[i]);
    1184           9 :         CSLDestroy(papszTokens);
    1185             :     }
    1186             : 
    1187             :     // Issue request to get image metadata
    1188           9 :     char **papszOptions = GetBaseHTTPOptions();
    1189           9 :     if (papszOptions == nullptr)
    1190           1 :         return false;
    1191             :     CPLHTTPResult *psResult =
    1192           8 :         EEDAHTTPFetch((m_osBaseURL + m_osAssetName).c_str(), papszOptions);
    1193           8 :     CSLDestroy(papszOptions);
    1194           8 :     if (psResult == nullptr)
    1195           0 :         return false;
    1196           8 :     if (psResult->pszErrBuf != nullptr)
    1197             :     {
    1198           0 :         if (psResult->pabyData)
    1199             :         {
    1200           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s: %s", psResult->pszErrBuf,
    1201           0 :                      reinterpret_cast<const char *>(psResult->pabyData));
    1202             :         }
    1203             :         else
    1204             :         {
    1205           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf);
    1206             :         }
    1207           0 :         CPLHTTPDestroyResult(psResult);
    1208           0 :         return false;
    1209             :     }
    1210             : 
    1211           8 :     if (psResult->pabyData == nullptr)
    1212             :     {
    1213           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1214             :                  "Empty content returned by server");
    1215           0 :         CPLHTTPDestroyResult(psResult);
    1216           0 :         return false;
    1217             :     }
    1218             : 
    1219           8 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
    1220             : #ifdef DEBUG_VERBOSE
    1221             :     CPLDebug("EEDAI", "%s", pszText);
    1222             : #endif
    1223             : 
    1224           8 :     json_object *poObj = nullptr;
    1225           8 :     if (!OGRJSonParse(pszText, &poObj, true))
    1226             :     {
    1227           0 :         CPLHTTPDestroyResult(psResult);
    1228           0 :         return false;
    1229             :     }
    1230             : 
    1231           8 :     CPLHTTPDestroyResult(psResult);
    1232             : 
    1233           8 :     if (json_object_get_type(poObj) != json_type_object)
    1234             :     {
    1235           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1236             :                  "Return is not a JSON dictionary");
    1237           0 :         json_object_put(poObj);
    1238           0 :         return false;
    1239             :     }
    1240             : 
    1241           8 :     json_object *poType = CPL_json_object_object_get(poObj, "type");
    1242           8 :     const char *pszType = json_object_get_string(poType);
    1243           8 :     if (pszType == nullptr || !EQUAL(pszType, "IMAGE"))
    1244             :     {
    1245           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Asset is not an image, but %s",
    1246             :                  pszType ? pszType : "(null)");
    1247           0 :         json_object_put(poObj);
    1248           0 :         return false;
    1249             :     }
    1250             : 
    1251           8 :     json_object *poBands = CPL_json_object_object_get(poObj, "bands");
    1252           8 :     if (poBands == nullptr || json_object_get_type(poBands) != json_type_array)
    1253             :     {
    1254           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No band found");
    1255           0 :         json_object_put(poObj);
    1256           0 :         return false;
    1257             :     }
    1258             : 
    1259          16 :     std::map<CPLString, CPLString> oMapCodeToWKT;
    1260             :     std::vector<EEDAIBandDesc> aoBandDesc =
    1261          16 :         BuildBandDescArray(poBands, oMapCodeToWKT);
    1262          16 :     std::map<CPLString, int> aoMapBandNames;
    1263             : 
    1264           8 :     if (aoBandDesc.empty())
    1265             :     {
    1266           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No band found");
    1267           0 :         json_object_put(poObj);
    1268           0 :         return false;
    1269             :     }
    1270             : 
    1271             :     // Indices are aoBandDesc indices
    1272          16 :     std::map<int, std::vector<int>> oMapSimilarBands;
    1273             : 
    1274           8 :     size_t iIdxFirstBand = 0;
    1275          30 :     for (size_t i = 0; i < aoBandDesc.size(); i++)
    1276             :     {
    1277             :         // Instantiate bands if they are compatible between them, and
    1278             :         // if they are requested by the user (when user explicitly requested
    1279             :         // them)
    1280          28 :         if ((oSetUserBandNames.empty() ||
    1281           6 :              oSetUserBandNames.find(aoBandDesc[i].osName) !=
    1282          44 :                  oSetUserBandNames.end()) &&
    1283          19 :             (nBands == 0 || aoBandDesc[i].IsSimilar(aoBandDesc[iIdxFirstBand])))
    1284             :         {
    1285          15 :             if (nBands == 0)
    1286             :             {
    1287           8 :                 iIdxFirstBand = i;
    1288           8 :                 nRasterXSize = aoBandDesc[i].nWidth;
    1289           8 :                 nRasterYSize = aoBandDesc[i].nHeight;
    1290           8 :                 m_gt = aoBandDesc[i].gt;
    1291           8 :                 m_oSRS.importFromWkt(aoBandDesc[i].osWKT);
    1292           8 :                 int iOvr = 0;
    1293          35 :                 while ((nRasterXSize >> iOvr) > 256 ||
    1294           8 :                        (nRasterYSize >> iOvr) > 256)
    1295             :                 {
    1296          27 :                     iOvr++;
    1297          27 :                     m_apoOverviewDS.push_back(new GDALEEDAIDataset(this, iOvr));
    1298             :                 }
    1299             :             }
    1300             : 
    1301             :             GDALRasterBand *poBand =
    1302          15 :                 new GDALEEDAIRasterBand(this, aoBandDesc[i].eDT);
    1303          15 :             const int iBand = nBands + 1;
    1304          15 :             SetBand(iBand, poBand);
    1305          15 :             poBand->SetDescription(aoBandDesc[i].osName);
    1306             : 
    1307             :             // as images in USDA/NAIP/DOQQ catalog
    1308          15 :             if (EQUAL(aoBandDesc[i].osName, "R"))
    1309           0 :                 poBand->SetColorInterpretation(GCI_RedBand);
    1310          15 :             else if (EQUAL(aoBandDesc[i].osName, "G"))
    1311           0 :                 poBand->SetColorInterpretation(GCI_GreenBand);
    1312          15 :             else if (EQUAL(aoBandDesc[i].osName, "B"))
    1313           0 :                 poBand->SetColorInterpretation(GCI_BlueBand);
    1314             : 
    1315          63 :             for (size_t iOvr = 0; iOvr < m_apoOverviewDS.size(); iOvr++)
    1316             :             {
    1317             :                 GDALRasterBand *poOvrBand = new GDALEEDAIRasterBand(
    1318          48 :                     m_apoOverviewDS[iOvr], aoBandDesc[i].eDT);
    1319          48 :                 m_apoOverviewDS[iOvr]->SetBand(iBand, poOvrBand);
    1320          48 :                 poOvrBand->SetDescription(aoBandDesc[i].osName);
    1321             :             }
    1322             : 
    1323          15 :             aoMapBandNames[aoBandDesc[i].osName] = iBand;
    1324             :         }
    1325             :         else
    1326             :         {
    1327           7 :             if (oSetUserBandNames.find(aoBandDesc[i].osName) !=
    1328          14 :                 oSetUserBandNames.end())
    1329             :             {
    1330           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1331             :                          "Band %s is not compatible of other bands",
    1332           0 :                          aoBandDesc[i].osName.c_str());
    1333             :             }
    1334           7 :             aoMapBandNames[aoBandDesc[i].osName] = -1;
    1335             :         }
    1336             : 
    1337             :         // Group similar bands to be able to build subdataset list
    1338             :         std::map<int, std::vector<int>>::iterator oIter =
    1339          22 :             oMapSimilarBands.begin();
    1340          28 :         for (; oIter != oMapSimilarBands.end(); ++oIter)
    1341             :         {
    1342          14 :             if (aoBandDesc[i].IsSimilar(aoBandDesc[oIter->first]))
    1343             :             {
    1344           8 :                 oIter->second.push_back(static_cast<int>(i));
    1345           8 :                 break;
    1346             :             }
    1347             :         }
    1348          22 :         if (oIter == oMapSimilarBands.end())
    1349             :         {
    1350          14 :             oMapSimilarBands[static_cast<int>(i)].push_back(
    1351          14 :                 static_cast<int>(i));
    1352             :         }
    1353             :     }
    1354             : 
    1355           8 :     if (!ComputeQueryStrategy())
    1356             :     {
    1357           0 :         json_object_put(poObj);
    1358           0 :         return false;
    1359             :     }
    1360          35 :     for (size_t i = 0; i < m_apoOverviewDS.size(); i++)
    1361             :     {
    1362          27 :         m_apoOverviewDS[i]->ComputeQueryStrategy();
    1363             :     }
    1364             : 
    1365           8 :     if (nBands > 1)
    1366             :     {
    1367           6 :         SetMetadataItem("INTERLEAVE", m_bQueryMultipleBands ? "PIXEL" : "BAND",
    1368           6 :                         "IMAGE_STRUCTURE");
    1369             :     }
    1370             : 
    1371             :     // Build subdataset list
    1372           8 :     if (oSetUserBandNames.empty() && oMapSimilarBands.size() > 1)
    1373             :     {
    1374           8 :         CPLStringList aoSubDSList;
    1375             :         std::map<int, std::vector<int>>::iterator oIter =
    1376           4 :             oMapSimilarBands.begin();
    1377          12 :         for (; oIter != oMapSimilarBands.end(); ++oIter)
    1378             :         {
    1379          16 :             CPLString osSubDSSuffix;
    1380          20 :             for (size_t i = 0; i < oIter->second.size(); ++i)
    1381             :             {
    1382          12 :                 if (!osSubDSSuffix.empty())
    1383           4 :                     osSubDSSuffix += ",";
    1384          12 :                 osSubDSSuffix += aoBandDesc[oIter->second[i]].osName;
    1385             :             }
    1386             :             aoSubDSList.AddNameValue(
    1387          16 :                 CPLSPrintf("SUBDATASET_%d_NAME", aoSubDSList.size() / 2 + 1),
    1388             :                 CPLSPrintf("EEDAI:%s:%s", m_osAsset.c_str(),
    1389           8 :                            osSubDSSuffix.c_str()));
    1390             :             aoSubDSList.AddNameValue(
    1391          16 :                 CPLSPrintf("SUBDATASET_%d_DESC", aoSubDSList.size() / 2 + 1),
    1392             :                 CPLSPrintf("Band%s %s of %s",
    1393           8 :                            oIter->second.size() > 1 ? "s" : "",
    1394          16 :                            osSubDSSuffix.c_str(), m_osAsset.c_str()));
    1395             :         }
    1396           4 :         SetMetadata(aoSubDSList.List(), "SUBDATASETS");
    1397             :     }
    1398             : 
    1399             :     // Attach metadata to dataset or bands
    1400           8 :     json_object *poProperties = CPL_json_object_object_get(poObj, "properties");
    1401           8 :     if (poProperties && json_object_get_type(poProperties) == json_type_object)
    1402             :     {
    1403           6 :         SetMetadataFromProperties(poProperties, aoMapBandNames);
    1404             :     }
    1405           8 :     json_object_put(poObj);
    1406             : 
    1407           8 :     SetDescription(poOpenInfo->pszFilename);
    1408             : 
    1409           8 :     return true;
    1410             : }
    1411             : 
    1412             : /************************************************************************/
    1413             : /*                       SetMetadataFromProperties()                    */
    1414             : /************************************************************************/
    1415             : 
    1416           6 : void GDALEEDAIDataset::SetMetadataFromProperties(
    1417             :     json_object *poProperties, const std::map<CPLString, int> &aoMapBandNames)
    1418             : {
    1419             :     json_object_iter it;
    1420           6 :     it.key = nullptr;
    1421           6 :     it.val = nullptr;
    1422           6 :     it.entry = nullptr;
    1423          24 :     json_object_object_foreachC(poProperties, it)
    1424             :     {
    1425          18 :         if (it.val)
    1426             :         {
    1427          36 :             CPLString osKey(it.key);
    1428          18 :             int nBandForMD = 0;
    1429             :             std::map<CPLString, int>::const_iterator oIter =
    1430          18 :                 aoMapBandNames.begin();
    1431          54 :             for (; oIter != aoMapBandNames.end(); ++oIter)
    1432             :             {
    1433          48 :                 CPLString osBandName(oIter->first);
    1434          48 :                 CPLString osNeedle("_" + osBandName);
    1435          48 :                 size_t nPos = osKey.find(osNeedle);
    1436          54 :                 if (nPos != std::string::npos &&
    1437           6 :                     nPos + osNeedle.size() == osKey.size())
    1438             :                 {
    1439           6 :                     nBandForMD = oIter->second;
    1440           6 :                     osKey.resize(nPos);
    1441           6 :                     break;
    1442             :                 }
    1443             : 
    1444             :                 // Landsat bands are named Bxxx, must their metadata
    1445             :                 // are _BAND_xxxx ...
    1446          84 :                 if (osBandName.size() > 1 && osBandName[0] == 'B' &&
    1447          42 :                     atoi(osBandName.c_str() + 1) > 0)
    1448             :                 {
    1449          42 :                     osNeedle = "_BAND_" + osBandName.substr(1);
    1450          42 :                     nPos = osKey.find(osNeedle);
    1451          48 :                     if (nPos != std::string::npos &&
    1452           6 :                         nPos + osNeedle.size() == osKey.size())
    1453             :                     {
    1454           6 :                         nBandForMD = oIter->second;
    1455           6 :                         osKey.resize(nPos);
    1456           6 :                         break;
    1457             :                     }
    1458             :                 }
    1459             :             }
    1460             : 
    1461          18 :             if (nBandForMD > 0)
    1462             :             {
    1463           6 :                 GetRasterBand(nBandForMD)
    1464           6 :                     ->SetMetadataItem(osKey, json_object_get_string(it.val));
    1465             :             }
    1466          12 :             else if (nBandForMD == 0)
    1467             :             {
    1468           6 :                 SetMetadataItem(osKey, json_object_get_string(it.val));
    1469             :             }
    1470             :         }
    1471             :     }
    1472           6 : }
    1473             : 
    1474             : /************************************************************************/
    1475             : /*                          GDALEEDAIIdentify()                         */
    1476             : /************************************************************************/
    1477             : 
    1478       56798 : static int GDALEEDAIIdentify(GDALOpenInfo *poOpenInfo)
    1479             : {
    1480       56798 :     return STARTS_WITH_CI(poOpenInfo->pszFilename, "EEDAI:");
    1481             : }
    1482             : 
    1483             : /************************************************************************/
    1484             : /*                            GDALEEDAIOpen()                           */
    1485             : /************************************************************************/
    1486             : 
    1487           9 : static GDALDataset *GDALEEDAIOpen(GDALOpenInfo *poOpenInfo)
    1488             : {
    1489           9 :     if (!GDALEEDAIIdentify(poOpenInfo))
    1490           0 :         return nullptr;
    1491             : 
    1492           9 :     GDALEEDAIDataset *poDS = new GDALEEDAIDataset();
    1493           9 :     if (!poDS->Open(poOpenInfo))
    1494             :     {
    1495           1 :         delete poDS;
    1496           1 :         return nullptr;
    1497             :     }
    1498           8 :     return poDS;
    1499             : }
    1500             : 
    1501             : /************************************************************************/
    1502             : /*                         GDALRegister_EEDAI()                         */
    1503             : /************************************************************************/
    1504             : 
    1505        1928 : void GDALRegister_EEDAI()
    1506             : 
    1507             : {
    1508        1928 :     if (GDALGetDriverByName("EEDAI") != nullptr)
    1509         282 :         return;
    1510             : 
    1511        1646 :     GDALDriver *poDriver = new GDALDriver();
    1512             : 
    1513        1646 :     poDriver->SetDescription("EEDAI");
    1514        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1515        1646 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Earth Engine Data API Image");
    1516        1646 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/eedai.html");
    1517        1646 :     poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "EEDAI:");
    1518        1646 :     poDriver->SetMetadataItem(
    1519             :         GDAL_DMD_OPENOPTIONLIST,
    1520             :         "<OpenOptionList>"
    1521             :         "  <Option name='ASSET' type='string' description='Asset name'/>"
    1522             :         "  <Option name='BANDS' type='string' "
    1523             :         "description='Comma separated list of band names'/>"
    1524             :         "  <Option name='PIXEL_ENCODING' type='string-select' "
    1525             :         "description='Format in which pixls are queried'>"
    1526             :         "       <Value>AUTO</Value>"
    1527             :         "       <Value>PNG</Value>"
    1528             :         "       <Value>JPEG</Value>"
    1529             :         "       <Value>GEO_TIFF</Value>"
    1530             :         "       <Value>AUTO_JPEG_PNG</Value>"
    1531             :         "       <Value>NPY</Value>"
    1532             :         "   </Option>"
    1533             :         "  <Option name='BLOCK_SIZE' type='integer' "
    1534             :         "description='Size of a block' default='256'/>"
    1535             :         "  <Option name='VSI_PATH_FOR_AUTH' type='string' "
    1536             :         "description='/vsigs/... path onto which a "
    1537             :         "GOOGLE_APPLICATION_CREDENTIALS path specific "
    1538             :         "option is set'/>"
    1539        1646 :         "</OpenOptionList>");
    1540        1646 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
    1541             : 
    1542        1646 :     poDriver->pfnOpen = GDALEEDAIOpen;
    1543        1646 :     poDriver->pfnIdentify = GDALEEDAIIdentify;
    1544             : 
    1545        1646 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1546             : }

Generated by: LCOV version 1.14