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