LCOV - code coverage report
Current view: top level - frmts/eeda - eedaidataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 483 646 74.8 %
Date: 2024-05-04 12:52:34 Functions: 24 26 92.3 %

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

Generated by: LCOV version 1.14