LCOV - code coverage report
Current view: top level - frmts/eeda - eedaidataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 491 651 75.4 %
Date: 2024-11-21 22:18:42 Functions: 24 26 92.3 %

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

Generated by: LCOV version 1.14