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