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

Generated by: LCOV version 1.14