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