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