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

Generated by: LCOV version 1.14