LCOV - code coverage report
Current view: top level - frmts/eeda - eedaidataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 483 643 75.1 %
Date: 2025-09-10 17:48:50 Functions: 24 26 92.3 %

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

Generated by: LCOV version 1.14