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