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