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