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