Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DAAS driver
4 : * Purpose: DAAS driver
5 : * Author: Even Rouault, <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2018-2019, Airbus DS Intelligence
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_http.h"
14 : #include "gdal_frmts.h"
15 : #include "gdal_alg.h"
16 : #include "gdal_priv.h"
17 : #include "ogr_spatialref.h"
18 : #include "gdal_mdreader.h"
19 : #include "../mem/memdataset.h"
20 :
21 : #include "cpl_json.h"
22 :
23 : #include <algorithm>
24 : #include <array>
25 : #include <memory>
26 :
27 : constexpr int knMIN_BLOCKSIZE = 64;
28 : constexpr int knDEFAULT_BLOCKSIZE = 512;
29 : constexpr int knMAX_BLOCKSIZE = 8192;
30 :
31 : constexpr GUInt32 RETRY_PER_BAND = 1;
32 : constexpr GUInt32 RETRY_SPATIAL_SPLIT = 2;
33 :
34 : // Let's limit to 100 MB uncompressed per request
35 : constexpr int knDEFAULT_SERVER_BYTE_LIMIT = 100 * 1024 * 1024;
36 :
37 : constexpr int MAIN_MASK_BAND_NUMBER = 0;
38 :
39 : /************************************************************************/
40 : /* GDALDAASBandDesc */
41 : /************************************************************************/
42 :
43 : class GDALDAASBandDesc
44 : {
45 : public:
46 : int nIndex = 0;
47 : GDALDataType eDT =
48 : GDT_Unknown; // as declared in the GetMetadata response bands[]
49 : CPLString osName;
50 : CPLString osDescription;
51 : CPLString osColorInterp;
52 : bool bIsMask = false;
53 : };
54 :
55 : /************************************************************************/
56 : /* GDALDAASDataset */
57 : /************************************************************************/
58 :
59 : class GDALDAASRasterBand;
60 :
61 : class GDALDAASDataset final : public GDALDataset
62 : {
63 : public:
64 : enum class Format
65 : {
66 : RAW,
67 : PNG,
68 : JPEG,
69 : JPEG2000,
70 : };
71 :
72 : private:
73 : friend class GDALDAASRasterBand;
74 :
75 : CPLString m_osGetMetadataURL;
76 :
77 : CPLString m_osAuthURL;
78 : CPLString m_osAccessToken;
79 : time_t m_nExpirationTime = 0;
80 : CPLString m_osXForwardUser;
81 :
82 : GDALDAASDataset *m_poParentDS = nullptr;
83 : // int m_iOvrLevel = 0;
84 :
85 : OGRSpatialReference m_oSRS{};
86 : CPLString m_osSRSType;
87 : CPLString m_osSRSValue;
88 : bool m_bGotGeoTransform = false;
89 : std::array<double, 6> m_adfGeoTransform{{0.0, 1.0, 0.0, 0.0, 0.0, 1.0}};
90 : bool m_bRequestInGeoreferencedCoordinates = false;
91 : GDALDataType m_eDT = GDT_Unknown;
92 : int m_nActualBitDepth = 0;
93 : bool m_bHasNoData = false;
94 : double m_dfNoDataValue = 0.0;
95 : CPLString m_osGetBufferURL;
96 : int m_nBlockSize = knDEFAULT_BLOCKSIZE;
97 : Format m_eFormat = Format::RAW;
98 : GIntBig m_nServerByteLimit = knDEFAULT_SERVER_BYTE_LIMIT;
99 : GDALRIOResampleAlg m_eCurrentResampleAlg = GRIORA_NearestNeighbour;
100 :
101 : int m_nMainMaskBandIndex = 0;
102 : CPLString m_osMainMaskName;
103 : GDALDAASRasterBand *m_poMaskBand = nullptr;
104 : std::vector<GDALDAASBandDesc> m_aoBandDesc;
105 :
106 : int m_nXOffAdvise = 0;
107 : int m_nYOffAdvise = 0;
108 : int m_nXSizeAdvise = 0;
109 : int m_nYSizeAdvise = 0;
110 :
111 : int m_nXOffFetched = 0;
112 : int m_nYOffFetched = 0;
113 : int m_nXSizeFetched = 0;
114 : int m_nYSizeFetched = 0;
115 :
116 : std::vector<std::unique_ptr<GDALDAASDataset>> m_apoOverviewDS;
117 :
118 : char **m_papszOpenOptions = nullptr;
119 :
120 : // Methods
121 : bool Open(GDALOpenInfo *poOpenInfo);
122 : bool GetAuthorization();
123 : bool GetImageMetadata();
124 : char **GetHTTPOptions();
125 : void ReadSRS(const CPLJSONObject &oProperties);
126 : void ReadRPCs(const CPLJSONObject &oProperties);
127 : bool SetupServerSideReprojection(const char *pszTargetSRS);
128 : void InstantiateBands();
129 :
130 : public:
131 : GDALDAASDataset();
132 : GDALDAASDataset(GDALDAASDataset *poParentDS, int iOvrLevel);
133 : ~GDALDAASDataset();
134 :
135 : static int Identify(GDALOpenInfo *poOpenInfo);
136 : static GDALDataset *OpenStatic(GDALOpenInfo *poOpenInfo);
137 :
138 : CPLErr GetGeoTransform(double *padfTransform) override;
139 : const OGRSpatialReference *GetSpatialRef() const override;
140 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
141 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
142 : GDALDataType eBufType, int nBandCount,
143 : BANDMAP_TYPE panBands, GSpacing nPixelSpace,
144 : GSpacing nLineSpace, GSpacing nBandSpace,
145 : GDALRasterIOExtraArg *psExtraArg) override;
146 : CPLErr AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
147 : int /* nBufXSize */, int /* nBufYSize */,
148 : GDALDataType /* eBufType */, int /*nBands*/,
149 : int * /*panBands*/, char ** /* papszOptions */) override;
150 : CPLErr FlushCache(bool bAtClosing) override;
151 : };
152 :
153 : /************************************************************************/
154 : /* GDALDAASRasterBand */
155 : /************************************************************************/
156 :
157 : class GDALDAASRasterBand final : public GDALRasterBand
158 : {
159 : friend class GDALDAASDataset;
160 :
161 : int m_nSrcIndex = 0;
162 : GDALColorInterp m_eColorInterp = GCI_Undefined;
163 :
164 : CPLErr GetBlocks(int nBlockXOff, int nBlockYOff, int nXBlocks, int nYBlocks,
165 : const std::vector<int> &anRequestedBands, void *pBuffer);
166 :
167 : GUInt32 PrefetchBlocks(int nXOff, int nYOff, int nXSize, int nYSize,
168 : const std::vector<int> &anRequestedBands);
169 :
170 : public:
171 : GDALDAASRasterBand(GDALDAASDataset *poDS, int nBand,
172 : const GDALDAASBandDesc &oBandDesc);
173 :
174 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
175 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
176 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
177 : GDALDataType eBufType, GSpacing nPixelSpace,
178 : GSpacing nLineSpace,
179 : GDALRasterIOExtraArg *psExtraArg) override;
180 : CPLErr AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
181 : int /* nBufXSize */, int /* nBufYSize */,
182 : GDALDataType /* eBufType */,
183 : char ** /* papszOptions */) override;
184 : double GetNoDataValue(int *pbHasNoData) override;
185 : GDALColorInterp GetColorInterpretation() override;
186 : GDALRasterBand *GetMaskBand() override;
187 : int GetMaskFlags() override;
188 : int GetOverviewCount() override;
189 : GDALRasterBand *GetOverview(int) override;
190 : };
191 :
192 : /************************************************************************/
193 : /* GDALDAASDataset() */
194 : /************************************************************************/
195 :
196 45 : GDALDAASDataset::GDALDAASDataset()
197 : : m_osAuthURL(CPLGetConfigOption(
198 : "GDAL_DAAS_AUTH_URL", "https://authenticate.geoapi-airbusds.com/auth/"
199 45 : "realms/IDP/protocol/openid-connect/token"))
200 : {
201 45 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
202 45 : }
203 :
204 : /************************************************************************/
205 : /* GDALDAASDataset() */
206 : /************************************************************************/
207 :
208 6 : GDALDAASDataset::GDALDAASDataset(GDALDAASDataset *poParentDS, int iOvrLevel)
209 6 : : m_osGetMetadataURL(poParentDS->m_osGetMetadataURL),
210 6 : m_osAuthURL(poParentDS->m_osAuthURL),
211 : m_osAccessToken(CPLString()), // only used by parent
212 : m_nExpirationTime(0), // only used by parent
213 : m_osXForwardUser(CPLString()), // only used by parent
214 : m_poParentDS(poParentDS),
215 : // m_iOvrLevel(iOvrLevel),
216 12 : m_oSRS(poParentDS->m_oSRS), m_osSRSType(poParentDS->m_osSRSType),
217 6 : m_osSRSValue(poParentDS->m_osSRSValue),
218 6 : m_bGotGeoTransform(poParentDS->m_bGotGeoTransform),
219 : m_bRequestInGeoreferencedCoordinates(
220 6 : poParentDS->m_bRequestInGeoreferencedCoordinates),
221 6 : m_eDT(poParentDS->m_eDT),
222 6 : m_nActualBitDepth(poParentDS->m_nActualBitDepth),
223 6 : m_bHasNoData(poParentDS->m_bHasNoData),
224 6 : m_dfNoDataValue(poParentDS->m_dfNoDataValue),
225 6 : m_osGetBufferURL(poParentDS->m_osGetBufferURL),
226 6 : m_eFormat(poParentDS->m_eFormat),
227 6 : m_nServerByteLimit(poParentDS->m_nServerByteLimit),
228 6 : m_nMainMaskBandIndex(poParentDS->m_nMainMaskBandIndex),
229 6 : m_osMainMaskName(poParentDS->m_osMainMaskName), m_poMaskBand(nullptr),
230 6 : m_aoBandDesc(poParentDS->m_aoBandDesc)
231 : {
232 6 : nRasterXSize = m_poParentDS->nRasterXSize >> iOvrLevel;
233 6 : nRasterYSize = m_poParentDS->nRasterYSize >> iOvrLevel;
234 6 : m_adfGeoTransform[0] = m_poParentDS->m_adfGeoTransform[0];
235 6 : m_adfGeoTransform[1] = m_poParentDS->m_adfGeoTransform[1] *
236 6 : m_poParentDS->nRasterXSize / nRasterXSize;
237 6 : m_adfGeoTransform[2] = m_poParentDS->m_adfGeoTransform[2];
238 6 : m_adfGeoTransform[3] = m_poParentDS->m_adfGeoTransform[3];
239 6 : m_adfGeoTransform[4] = m_poParentDS->m_adfGeoTransform[4];
240 6 : m_adfGeoTransform[5] = m_poParentDS->m_adfGeoTransform[5] *
241 6 : m_poParentDS->nRasterYSize / nRasterYSize;
242 :
243 6 : InstantiateBands();
244 :
245 6 : SetMetadata(m_poParentDS->GetMetadata());
246 6 : SetMetadata(m_poParentDS->GetMetadata("RPC"), "RPC");
247 6 : }
248 :
249 : /************************************************************************/
250 : /* ~GDALDAASDataset() */
251 : /************************************************************************/
252 :
253 102 : GDALDAASDataset::~GDALDAASDataset()
254 : {
255 51 : if (m_poParentDS == nullptr)
256 : {
257 45 : char **papszOptions = nullptr;
258 45 : papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT",
259 : CPLSPrintf("%p", this));
260 45 : CPLHTTPDestroyResult(CPLHTTPFetch("", papszOptions));
261 45 : CSLDestroy(papszOptions);
262 : }
263 :
264 51 : delete m_poMaskBand;
265 51 : CSLDestroy(m_papszOpenOptions);
266 102 : }
267 :
268 : /************************************************************************/
269 : /* InstantiateBands() */
270 : /************************************************************************/
271 :
272 25 : void GDALDAASDataset::InstantiateBands()
273 : {
274 64 : for (int i = 0; i < static_cast<int>(m_aoBandDesc.size()); i++)
275 : {
276 : GDALRasterBand *poBand =
277 39 : new GDALDAASRasterBand(this, i + 1, m_aoBandDesc[i]);
278 39 : SetBand(i + 1, poBand);
279 : }
280 :
281 25 : if (!m_osMainMaskName.empty())
282 : {
283 1 : GDALDAASBandDesc oDesc;
284 1 : oDesc.nIndex = m_nMainMaskBandIndex;
285 1 : oDesc.osName = m_osMainMaskName;
286 1 : m_poMaskBand = new GDALDAASRasterBand(this, 0, oDesc);
287 : }
288 :
289 25 : if (nBands > 1)
290 : {
291 : // Hint for users of the driver
292 7 : GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
293 : }
294 25 : }
295 :
296 : /************************************************************************/
297 : /* Identify() */
298 : /************************************************************************/
299 :
300 50665 : int GDALDAASDataset::Identify(GDALOpenInfo *poOpenInfo)
301 : {
302 50665 : return STARTS_WITH_CI(poOpenInfo->pszFilename, "DAAS:");
303 : }
304 :
305 : /************************************************************************/
306 : /* GetGeoTransform() */
307 : /************************************************************************/
308 :
309 1 : CPLErr GDALDAASDataset::GetGeoTransform(double *padfTransform)
310 : {
311 1 : std::copy_n(m_adfGeoTransform.begin(), m_adfGeoTransform.size(),
312 : padfTransform);
313 1 : return (m_bGotGeoTransform) ? CE_None : CE_Failure;
314 : }
315 :
316 : /************************************************************************/
317 : /* GetSpatialRef() */
318 : /************************************************************************/
319 :
320 1 : const OGRSpatialReference *GDALDAASDataset::GetSpatialRef() const
321 : {
322 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
323 : }
324 :
325 : /********************-****************************************************/
326 : /* URLEscape() */
327 : /************************************************************************/
328 :
329 14 : static CPLString URLEscape(const CPLString &osStr)
330 : {
331 14 : char *pszEscaped = CPLEscapeString(osStr.c_str(), -1, CPLES_URL);
332 14 : CPLString osRet(pszEscaped);
333 14 : CPLFree(pszEscaped);
334 14 : return osRet;
335 : }
336 :
337 : /************************************************************************/
338 : /* GetHTTPOptions() */
339 : /************************************************************************/
340 :
341 72 : char **GDALDAASDataset::GetHTTPOptions()
342 : {
343 72 : if (m_poParentDS)
344 1 : return m_poParentDS->GetHTTPOptions();
345 :
346 71 : char **papszOptions = nullptr;
347 71 : CPLString osHeaders;
348 71 : if (!m_osAccessToken.empty())
349 : {
350 : // Renew token if needed
351 4 : if (m_nExpirationTime != 0 && time(nullptr) >= m_nExpirationTime)
352 : {
353 1 : GetAuthorization();
354 : }
355 4 : osHeaders += "Authorization: Bearer " + m_osAccessToken;
356 : }
357 : else
358 : {
359 : const char *pszAuthorization =
360 67 : CPLGetConfigOption("GDAL_DAAS_AUTHORIZATION", nullptr);
361 67 : if (pszAuthorization)
362 0 : osHeaders += pszAuthorization;
363 : }
364 71 : if (!m_osXForwardUser.empty())
365 : {
366 1 : if (!osHeaders.empty())
367 1 : osHeaders += "\r\n";
368 1 : osHeaders += "X-Forwarded-User: " + m_osXForwardUser;
369 : }
370 71 : if (!osHeaders.empty())
371 : {
372 : papszOptions =
373 4 : CSLSetNameValue(papszOptions, "HEADERS", osHeaders.c_str());
374 : }
375 : papszOptions =
376 71 : CSLSetNameValue(papszOptions, "PERSISTENT", CPLSPrintf("%p", this));
377 : // 30 minutes
378 71 : papszOptions = CSLSetNameValue(papszOptions, "TIMEOUT", "1800");
379 71 : return papszOptions;
380 : }
381 :
382 : /************************************************************************/
383 : /* DAASBackoffFactor() */
384 : /************************************************************************/
385 :
386 : /* Add a small amount of random jitter to avoid cyclic server stampedes */
387 8 : static double DAASBackoffFactor(double base)
388 : {
389 : // We don't need cryptographic quality randomness...
390 : // coverity[dont_call]
391 8 : return base + rand() * 0.5 / RAND_MAX;
392 : }
393 :
394 : /************************************************************************/
395 : /* DAAS_CPLHTTPFetch() */
396 : /************************************************************************/
397 :
398 78 : static CPLHTTPResult *DAAS_CPLHTTPFetch(const char *pszURL, char **papszOptions)
399 : {
400 : CPLHTTPResult *psResult;
401 78 : const int RETRY_COUNT = 4;
402 : // coverity[tainted_data]
403 : double dfRetryDelay =
404 78 : CPLAtof(CPLGetConfigOption("GDAL_DAAS_INITIAL_RETRY_DELAY", "1.0"));
405 86 : for (int i = 0; i <= RETRY_COUNT; i++)
406 : {
407 86 : psResult = CPLHTTPFetch(pszURL, papszOptions);
408 86 : if (psResult == nullptr)
409 0 : break;
410 :
411 86 : if (psResult->nDataLen != 0 && psResult->nStatus == 0 &&
412 60 : psResult->pszErrBuf == nullptr)
413 : {
414 : /* got a valid response */
415 57 : CPLErrorReset();
416 57 : break;
417 : }
418 : else
419 : {
420 29 : const char *pszErrorText =
421 29 : psResult->pszErrBuf ? psResult->pszErrBuf : "(null)";
422 :
423 : /* Get HTTP status code */
424 29 : int nHTTPStatus = -1;
425 29 : if (psResult->pszErrBuf != nullptr &&
426 28 : EQUALN(psResult->pszErrBuf,
427 : "HTTP error code : ", strlen("HTTP error code : ")))
428 : {
429 : nHTTPStatus =
430 28 : atoi(psResult->pszErrBuf + strlen("HTTP error code : "));
431 28 : if (psResult->pabyData)
432 3 : pszErrorText = (const char *)psResult->pabyData;
433 : }
434 :
435 29 : if ((nHTTPStatus == 500 ||
436 9 : (nHTTPStatus >= 502 && nHTTPStatus <= 504)) &&
437 : i < RETRY_COUNT)
438 : {
439 8 : CPLError(CE_Warning, CPLE_FileIO,
440 : "Error when downloading %s,"
441 : "HTTP status=%d, retrying in %.2fs : %s",
442 : pszURL, nHTTPStatus, dfRetryDelay, pszErrorText);
443 8 : CPLHTTPDestroyResult(psResult);
444 8 : psResult = nullptr;
445 :
446 8 : CPLSleep(dfRetryDelay);
447 8 : dfRetryDelay *= DAASBackoffFactor(4);
448 : }
449 : else
450 : {
451 : break;
452 : }
453 : }
454 : }
455 :
456 78 : return psResult;
457 : }
458 :
459 : /************************************************************************/
460 : /* GetAuthorization() */
461 : /************************************************************************/
462 :
463 10 : bool GDALDAASDataset::GetAuthorization()
464 : {
465 : const CPLString osClientId =
466 10 : CSLFetchNameValueDef(m_papszOpenOptions, "CLIENT_ID",
467 20 : CPLGetConfigOption("GDAL_DAAS_CLIENT_ID", ""));
468 : const CPLString osAPIKey =
469 10 : CSLFetchNameValueDef(m_papszOpenOptions, "API_KEY",
470 20 : CPLGetConfigOption("GDAL_DAAS_API_KEY", ""));
471 : const CPLString osAuthorization =
472 10 : CSLFetchNameValueDef(m_papszOpenOptions, "ACCESS_TOKEN",
473 20 : CPLGetConfigOption("GDAL_DAAS_ACCESS_TOKEN", ""));
474 : m_osXForwardUser = CSLFetchNameValueDef(
475 10 : m_papszOpenOptions, "X_FORWARDED_USER",
476 10 : CPLGetConfigOption("GDAL_DAAS_X_FORWARDED_USER", ""));
477 :
478 10 : if (!osAuthorization.empty())
479 : {
480 1 : if (!osClientId.empty() && !osAPIKey.empty())
481 : {
482 0 : CPLError(
483 : CE_Warning, CPLE_AppDefined,
484 : "GDAL_DAAS_CLIENT_ID + GDAL_DAAS_API_KEY and "
485 : "GDAL_DAAS_ACCESS_TOKEN defined. Only the later taken into "
486 : "account");
487 : }
488 1 : m_osAccessToken = osAuthorization;
489 1 : return true;
490 : }
491 :
492 9 : if (osClientId.empty() && osAPIKey.empty())
493 : {
494 0 : CPLDebug("DAAS",
495 : "Neither GDAL_DAAS_CLIENT_ID, GDAL_DAAS_API_KEY "
496 : "nor GDAL_DAAS_ACCESS_TOKEN is defined. Trying without "
497 : "authorization");
498 0 : return true;
499 : }
500 :
501 9 : if (osClientId.empty())
502 : {
503 1 : CPLError(CE_Failure, CPLE_AppDefined,
504 : "GDAL_DAAS_API_KEY defined, but GDAL_DAAS_CLIENT_ID missing.");
505 1 : return false;
506 : }
507 :
508 8 : if (osAPIKey.empty())
509 : {
510 1 : CPLError(CE_Failure, CPLE_AppDefined,
511 : "GDAL_DAAS_CLIENT_ID defined, but GDAL_DAAS_API_KEY missing.");
512 1 : return false;
513 : }
514 :
515 14 : CPLString osPostContent;
516 7 : osPostContent += "client_id=" + URLEscape(osClientId);
517 7 : osPostContent += "&apikey=" + URLEscape(osAPIKey);
518 7 : osPostContent += "&grant_type=api_key";
519 :
520 7 : char **papszOptions = nullptr;
521 : papszOptions =
522 7 : CSLSetNameValue(papszOptions, "POSTFIELDS", osPostContent.c_str());
523 14 : CPLString osHeaders("Content-Type: application/x-www-form-urlencoded");
524 7 : papszOptions = CSLSetNameValue(papszOptions, "HEADERS", osHeaders.c_str());
525 : // FIXME for server side: make sure certificates are valid
526 7 : papszOptions = CSLSetNameValue(papszOptions, "UNSAFESSL", "YES");
527 7 : CPLHTTPResult *psResult = DAAS_CPLHTTPFetch(m_osAuthURL, papszOptions);
528 7 : CSLDestroy(papszOptions);
529 :
530 7 : if (psResult == nullptr)
531 : {
532 0 : return false;
533 : }
534 :
535 7 : if (psResult->pszErrBuf != nullptr)
536 : {
537 1 : CPLError(CE_Failure, CPLE_AppDefined, "Get request %s failed: %s",
538 : m_osAuthURL.c_str(),
539 2 : psResult->pabyData ? CPLSPrintf("%s: %s", psResult->pszErrBuf,
540 : reinterpret_cast<const char *>(
541 1 : psResult->pabyData))
542 : : psResult->pszErrBuf);
543 1 : CPLHTTPDestroyResult(psResult);
544 1 : return false;
545 : }
546 :
547 6 : if (psResult->pabyData == nullptr)
548 : {
549 0 : CPLError(CE_Failure, CPLE_AppDefined,
550 : "Authorization request failed: "
551 : "Empty content returned by server");
552 0 : CPLHTTPDestroyResult(psResult);
553 0 : return false;
554 : }
555 :
556 : CPLString osAuthorizationResponse(
557 12 : reinterpret_cast<char *>(psResult->pabyData));
558 6 : CPLHTTPDestroyResult(psResult);
559 :
560 12 : CPLJSONDocument oDoc;
561 6 : if (!oDoc.LoadMemory(osAuthorizationResponse))
562 : {
563 1 : CPLError(CE_Failure, CPLE_AppDefined,
564 : "Cannont parse GetAuthorization response");
565 1 : return false;
566 : }
567 :
568 5 : m_osAccessToken = oDoc.GetRoot().GetString("access_token");
569 5 : if (m_osAccessToken.empty())
570 : {
571 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot retrieve access_token");
572 1 : return false;
573 : }
574 :
575 4 : int nExpiresIn = oDoc.GetRoot().GetInteger("expires_in");
576 4 : if (nExpiresIn > 0)
577 : {
578 2 : m_nExpirationTime = time(nullptr) + nExpiresIn - 60;
579 : }
580 :
581 4 : return true;
582 : }
583 :
584 : /************************************************************************/
585 : /* GetObject() */
586 : /************************************************************************/
587 :
588 359 : static CPLJSONObject GetObject(const CPLJSONObject &oContainer,
589 : const char *pszPath,
590 : CPLJSONObject::Type eExpectedType,
591 : const char *pszExpectedType, bool bVerboseError,
592 : bool &bError)
593 : {
594 718 : CPLJSONObject oObj = oContainer.GetObj(pszPath);
595 359 : if (!oObj.IsValid())
596 : {
597 156 : if (bVerboseError)
598 : {
599 3 : CPLError(CE_Failure, CPLE_AppDefined, "%s missing", pszPath);
600 : }
601 156 : bError = true;
602 156 : oObj.Deinit();
603 156 : return oObj;
604 : }
605 203 : if (oObj.GetType() != eExpectedType)
606 : {
607 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s not %s", pszPath,
608 : pszExpectedType);
609 1 : bError = true;
610 1 : oObj.Deinit();
611 1 : return oObj;
612 : }
613 202 : return oObj;
614 : }
615 :
616 : /************************************************************************/
617 : /* GetInteger() */
618 : /************************************************************************/
619 :
620 84 : static int GetInteger(const CPLJSONObject &oContainer, const char *pszPath,
621 : bool bVerboseError, bool &bError)
622 : {
623 : CPLJSONObject oObj =
624 : GetObject(oContainer, pszPath, CPLJSONObject::Type::Integer,
625 168 : "an integer", bVerboseError, bError);
626 84 : if (!oObj.IsValid())
627 : {
628 29 : return 0;
629 : }
630 55 : return oObj.ToInteger();
631 : }
632 :
633 : /************************************************************************/
634 : /* GetDouble() */
635 : /************************************************************************/
636 :
637 92 : static double GetDouble(const CPLJSONObject &oContainer, const char *pszPath,
638 : bool bVerboseError, bool &bError)
639 : {
640 276 : CPLJSONObject oObj = oContainer.GetObj(pszPath);
641 92 : if (!oObj.IsValid())
642 : {
643 66 : if (bVerboseError)
644 : {
645 10 : CPLError(CE_Failure, CPLE_AppDefined, "%s missing", pszPath);
646 : }
647 66 : bError = true;
648 66 : return 0.0;
649 : }
650 28 : if (oObj.GetType() != CPLJSONObject::Type::Integer &&
651 2 : oObj.GetType() != CPLJSONObject::Type::Double)
652 : {
653 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s not a double", pszPath);
654 0 : bError = true;
655 0 : return 0.0;
656 : }
657 26 : return oObj.ToDouble();
658 : }
659 :
660 : /************************************************************************/
661 : /* GetString() */
662 : /************************************************************************/
663 :
664 275 : static CPLString GetString(const CPLJSONObject &oContainer, const char *pszPath,
665 : bool bVerboseError, bool &bError)
666 : {
667 : CPLJSONObject oObj =
668 : GetObject(oContainer, pszPath, CPLJSONObject::Type::String, "a string",
669 550 : bVerboseError, bError);
670 275 : if (!oObj.IsValid())
671 : {
672 128 : return CPLString();
673 : }
674 294 : return oObj.ToString();
675 : }
676 :
677 : /************************************************************************/
678 : /* GetGDALDataTypeFromDAASPixelType() */
679 : /************************************************************************/
680 :
681 : static GDALDataType
682 46 : GetGDALDataTypeFromDAASPixelType(const CPLString &osPixelType)
683 : {
684 : const struct
685 : {
686 : const char *pszName;
687 : GDALDataType eDT;
688 46 : } asDataTypes[] = {
689 : {"Byte", GDT_Byte}, {"UInt16", GDT_UInt16},
690 : {"Int16", GDT_Int16}, {"UInt32", GDT_UInt32},
691 : {"Int32", GDT_Int32}, {"Float32", GDT_Float32},
692 : {"Float64", GDT_Float64},
693 : };
694 :
695 71 : for (size_t i = 0; i < CPL_ARRAYSIZE(asDataTypes); ++i)
696 : {
697 69 : if (osPixelType == asDataTypes[i].pszName)
698 : {
699 44 : return asDataTypes[i].eDT;
700 : }
701 : }
702 2 : return GDT_Unknown;
703 : }
704 :
705 : /************************************************************************/
706 : /* GetImageMetadata() */
707 : /************************************************************************/
708 :
709 39 : bool GDALDAASDataset::GetImageMetadata()
710 : {
711 39 : char **papszOptions = GetHTTPOptions();
712 : CPLHTTPResult *psResult =
713 39 : DAAS_CPLHTTPFetch(m_osGetMetadataURL, papszOptions);
714 39 : CSLDestroy(papszOptions);
715 39 : if (psResult == nullptr)
716 0 : return false;
717 :
718 39 : if (psResult->pszErrBuf != nullptr)
719 : {
720 8 : CPLError(CE_Failure, CPLE_AppDefined, "Get request %s failed: %s",
721 : m_osGetMetadataURL.c_str(),
722 9 : psResult->pabyData ? CPLSPrintf("%s: %s", psResult->pszErrBuf,
723 : reinterpret_cast<const char *>(
724 1 : psResult->pabyData))
725 : : psResult->pszErrBuf);
726 8 : CPLHTTPDestroyResult(psResult);
727 8 : return false;
728 : }
729 :
730 31 : if (psResult->pabyData == nullptr)
731 : {
732 1 : CPLError(CE_Failure, CPLE_AppDefined,
733 : "Get request %s failed: "
734 : "Empty content returned by server",
735 : m_osGetMetadataURL.c_str());
736 1 : CPLHTTPDestroyResult(psResult);
737 1 : return false;
738 : }
739 :
740 60 : CPLString osResponse(reinterpret_cast<char *>(psResult->pabyData));
741 30 : CPLHTTPDestroyResult(psResult);
742 :
743 60 : CPLJSONDocument oDoc;
744 30 : CPLDebug("DAAS", "%s", osResponse.c_str());
745 30 : if (!oDoc.LoadMemory(osResponse))
746 : {
747 1 : CPLError(CE_Failure, CPLE_AppDefined,
748 : "Cannont parse GetImageMetadata response");
749 1 : return false;
750 : }
751 :
752 29 : CPLJSONObject oProperties = oDoc.GetRoot().GetObj(
753 87 : "response/payload/payload/imageMetadata/properties");
754 29 : if (!oProperties.IsValid())
755 : {
756 11 : oProperties = oDoc.GetRoot().GetObj("properties");
757 11 : if (!oProperties.IsValid())
758 : {
759 1 : CPLError(CE_Failure, CPLE_AppDefined,
760 : "Cannont find response/payload/payload/imageMetadata/"
761 : "properties nor properties in GetImageMetadata response");
762 1 : return false;
763 : }
764 : }
765 :
766 28 : bool bError = false;
767 28 : nRasterXSize = GetInteger(oProperties, "width", true, bError);
768 28 : nRasterYSize = GetInteger(oProperties, "height", true, bError);
769 28 : if (!bError && !GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
770 : {
771 1 : bError = true;
772 : }
773 :
774 28 : bool bIgnoredError = false;
775 :
776 28 : m_nActualBitDepth =
777 28 : GetInteger(oProperties, "actualBitDepth", false, bIgnoredError);
778 :
779 28 : bool bNoDataError = false;
780 28 : m_dfNoDataValue =
781 28 : GetDouble(oProperties, "noDataValue", false, bNoDataError);
782 28 : m_bHasNoData = !bNoDataError;
783 :
784 84 : CPLJSONObject oGetBufferObj = oProperties.GetObj("_links/getBuffer");
785 28 : if (!oGetBufferObj.IsValid())
786 : {
787 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s missing", "_links/getBuffer");
788 1 : bError = true;
789 : }
790 56 : CPLJSONObject oGetBufferDict;
791 28 : oGetBufferDict.Deinit();
792 28 : if (oGetBufferObj.GetType() == CPLJSONObject::Type::Array)
793 : {
794 0 : auto array = oGetBufferObj.ToArray();
795 0 : if (array.Size() > 0)
796 : {
797 0 : oGetBufferDict = array[0];
798 : }
799 : }
800 28 : else if (oGetBufferObj.GetType() == CPLJSONObject::Type::Object)
801 : {
802 27 : oGetBufferDict = oGetBufferObj;
803 : }
804 28 : CPL_IGNORE_RET_VAL(oGetBufferObj);
805 28 : if (!oGetBufferDict.IsValid())
806 : {
807 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s missing",
808 : "_links/getBuffer/href");
809 1 : bError = true;
810 : }
811 : else
812 : {
813 27 : m_osGetBufferURL = GetString(oGetBufferDict, "href", true, bError);
814 : }
815 :
816 : #ifndef REMOVE_THAT_LEGACY_CODE
817 28 : if (!STARTS_WITH_CI(m_osGetMetadataURL, "https://192.168.") &&
818 56 : !STARTS_WITH_CI(m_osGetMetadataURL, "http://192.168.") &&
819 28 : STARTS_WITH_CI(m_osGetBufferURL, "http://192.168."))
820 : {
821 0 : size_t nPosDaas = m_osGetMetadataURL.find("/daas/");
822 0 : size_t nPosImages = m_osGetMetadataURL.find("/images/");
823 0 : if (nPosDaas != std::string::npos && nPosImages != std::string::npos)
824 : {
825 : m_osGetBufferURL =
826 0 : m_osGetMetadataURL.substr(0, nPosDaas) + "/daas/images/" +
827 0 : m_osGetMetadataURL.substr(nPosImages + strlen("/images/")) +
828 0 : "/buffer";
829 : }
830 : }
831 : #endif
832 :
833 84 : CPLJSONArray oGTArray = oProperties.GetArray("geotransform");
834 28 : if (oGTArray.IsValid() && oGTArray.Size() == 6)
835 : {
836 3 : m_bGotGeoTransform = true;
837 21 : for (int i = 0; i < 6; i++)
838 : {
839 18 : m_adfGeoTransform[i] = oGTArray[i].ToDouble();
840 : }
841 : }
842 :
843 84 : CPLJSONArray oBandArray = oProperties.GetArray("bands");
844 28 : if (!oBandArray.IsValid() || oBandArray.Size() == 0)
845 : {
846 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing or empty bands array");
847 1 : bError = true;
848 : }
849 : else
850 : {
851 69 : for (int i = 0; i < oBandArray.Size(); ++i)
852 : {
853 42 : CPLJSONObject oBandObj = oBandArray[i];
854 42 : if (oBandObj.GetType() == CPLJSONObject::Type::Object)
855 : {
856 42 : GDALDAASBandDesc oDesc;
857 42 : oDesc.nIndex = i + 1;
858 42 : oDesc.osName = GetString(oBandObj, "name", true, bError);
859 : oDesc.osDescription =
860 42 : GetString(oBandObj, "description", false, bIgnoredError);
861 84 : oDesc.osColorInterp = GetString(oBandObj, "colorInterpretation",
862 42 : false, bIgnoredError);
863 42 : oDesc.bIsMask = oBandObj.GetBool("isMask");
864 :
865 : const CPLString osPixelType(
866 42 : GetString(oBandObj, "pixelType", true, bError));
867 42 : oDesc.eDT = GetGDALDataTypeFromDAASPixelType(osPixelType);
868 42 : if (oDesc.eDT == GDT_Unknown)
869 : {
870 2 : CPLError(CE_Failure, CPLE_NotSupported,
871 : "Unsupported value pixelType = '%s'",
872 : osPixelType.c_str());
873 2 : bError = true;
874 : }
875 42 : if (i == 0)
876 : {
877 27 : m_eDT = oDesc.eDT;
878 : }
879 :
880 42 : if (!CPLFetchBool(m_papszOpenOptions, "MASKS", true) &&
881 0 : oDesc.bIsMask)
882 : {
883 0 : continue;
884 : }
885 43 : if (oDesc.osColorInterp == "MAIN_MASK" &&
886 1 : m_osMainMaskName.empty())
887 : {
888 1 : m_nMainMaskBandIndex = i + 1;
889 1 : m_osMainMaskName = oDesc.osName;
890 : }
891 : else
892 : {
893 41 : m_aoBandDesc.push_back(oDesc);
894 : }
895 : }
896 : else
897 : {
898 0 : CPLError(CE_Failure, CPLE_AppDefined,
899 : "Invalid bands[] element");
900 0 : bError = true;
901 : }
902 : }
903 : }
904 :
905 28 : ReadSRS(oProperties);
906 :
907 28 : ReadRPCs(oProperties);
908 :
909 : // Collect other metadata
910 183 : for (const auto &oObj : oProperties.GetChildren())
911 : {
912 310 : const CPLString &osName(oObj.GetName());
913 155 : const auto &oType(oObj.GetType());
914 465 : if (osName != "aoiFactor" && osName != "crsCode" &&
915 465 : osName != "nbBands" && osName != "nbBits" && osName != "nBits" &&
916 434 : osName != "actualBitDepth" && osName != "width" &&
917 322 : osName != "height" && osName != "noDataValue" && osName != "step" &&
918 194 : osName != "pixelType" && oObj.IsValid() &&
919 97 : oType != CPLJSONObject::Type::Null &&
920 377 : oType != CPLJSONObject::Type::Array &&
921 67 : oType != CPLJSONObject::Type::Object)
922 : {
923 8 : SetMetadataItem(osName.c_str(), oObj.ToString().c_str());
924 : }
925 : }
926 :
927 : // Metadata for IMAGERY domain
928 : CPLString osAcquisitionDate(
929 56 : GetString(oProperties, "acquisitionDate", false, bIgnoredError));
930 28 : if (!osAcquisitionDate.empty())
931 : {
932 2 : int iYear = 0;
933 2 : int iMonth = 0;
934 2 : int iDay = 0;
935 2 : int iHours = 0;
936 2 : int iMin = 0;
937 2 : int iSec = 0;
938 : const int r =
939 2 : sscanf(osAcquisitionDate.c_str(), "%d-%d-%dT%d:%d:%d.%*dZ", &iYear,
940 : &iMonth, &iDay, &iHours, &iMin, &iSec);
941 2 : if (r == 6)
942 : {
943 2 : SetMetadataItem(MD_NAME_ACQDATETIME,
944 : CPLSPrintf("%04d-%02d-%02d %02d:%02d:%02d", iYear,
945 : iMonth, iDay, iHours, iMin, iSec),
946 2 : MD_DOMAIN_IMAGERY);
947 : }
948 : }
949 :
950 28 : bIgnoredError = false;
951 : double dfCloudCover =
952 28 : GetDouble(oProperties, "cloudCover", false, bIgnoredError);
953 28 : if (!bIgnoredError)
954 : {
955 2 : SetMetadataItem(MD_NAME_CLOUDCOVER, CPLSPrintf("%.2f", dfCloudCover),
956 2 : MD_DOMAIN_IMAGERY);
957 : }
958 :
959 : CPLString osSatellite(
960 28 : GetString(oProperties, "satellite", false, bIgnoredError));
961 28 : if (!osSatellite.empty())
962 : {
963 2 : SetMetadataItem(MD_NAME_SATELLITE, osSatellite.c_str(),
964 2 : MD_DOMAIN_IMAGERY);
965 : }
966 :
967 28 : return !bError;
968 : }
969 :
970 : /************************************************************************/
971 : /* ReadSRS() */
972 : /************************************************************************/
973 :
974 28 : void GDALDAASDataset::ReadSRS(const CPLJSONObject &oProperties)
975 : {
976 84 : CPLJSONArray oSRSArray = oProperties.GetArray("srsExpression/names");
977 28 : if (oSRSArray.IsValid())
978 : {
979 12 : for (int i = 0; i < oSRSArray.Size(); ++i)
980 : {
981 20 : CPLJSONObject oSRSObj = oSRSArray[i];
982 10 : if (oSRSObj.GetType() == CPLJSONObject::Type::Object)
983 : {
984 10 : bool bError = false;
985 : const std::string osType(
986 20 : GetString(oSRSObj, "type", true, bError));
987 : const std::string osValue(
988 20 : GetString(oSRSObj, "value", true, bError));
989 : // Use urn in priority
990 10 : if (osType == "urn" && !osValue.empty())
991 : {
992 2 : m_osSRSType = osType;
993 2 : m_osSRSValue = osValue;
994 : }
995 : // Use proj4 if urn not already set
996 12 : else if (osType == "proj4" && !osValue.empty() &&
997 4 : m_osSRSType != "urn")
998 : {
999 2 : m_osSRSType = osType;
1000 2 : m_osSRSValue = osValue;
1001 : }
1002 : // If no SRS set, take the first one
1003 8 : else if (m_osSRSValue.empty() && !osType.empty() &&
1004 2 : !osValue.empty())
1005 : {
1006 2 : m_osSRSType = osType;
1007 2 : m_osSRSValue = osValue;
1008 : }
1009 : }
1010 : }
1011 : }
1012 : else
1013 : {
1014 78 : auto osCrsCode = oProperties.GetString("crsCode");
1015 26 : if (!osCrsCode.empty())
1016 : {
1017 0 : m_osSRSType = "urn";
1018 0 : m_osSRSValue = osCrsCode;
1019 : }
1020 : }
1021 :
1022 28 : if (m_osSRSType == "urn" || m_osSRSType == "proj4")
1023 : {
1024 2 : m_oSRS.SetFromUserInput(
1025 : m_osSRSValue,
1026 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1027 : }
1028 28 : }
1029 :
1030 : /************************************************************************/
1031 : /* ReadRPCs() */
1032 : /************************************************************************/
1033 :
1034 28 : void GDALDAASDataset::ReadRPCs(const CPLJSONObject &oProperties)
1035 : {
1036 84 : CPLJSONObject oRPC = oProperties.GetObj("rpc");
1037 28 : if (oRPC.IsValid())
1038 : {
1039 3 : bool bRPCError = false;
1040 6 : CPLStringList aoRPC;
1041 :
1042 : const struct
1043 : {
1044 : const char *pszJsonName;
1045 : const char *pszGDALName;
1046 3 : } asRPCSingleValues[] = {
1047 : {"errBias", RPC_ERR_BIAS}, {"errRand", RPC_ERR_RAND},
1048 : {"sampOff", RPC_SAMP_OFF}, {"lineOff", RPC_LINE_OFF},
1049 : {"latOff", RPC_LAT_OFF}, {"longOff", RPC_LONG_OFF},
1050 : {"heightOff", RPC_HEIGHT_OFF}, {"lineScale", RPC_LINE_SCALE},
1051 : {"sampScale", RPC_SAMP_SCALE}, {"latScale", RPC_LAT_SCALE},
1052 : {"longScale", RPC_LONG_SCALE}, {"heightScale", RPC_HEIGHT_SCALE},
1053 : };
1054 :
1055 39 : for (size_t i = 0; i < CPL_ARRAYSIZE(asRPCSingleValues); ++i)
1056 : {
1057 36 : bool bRPCErrorTmp = false;
1058 36 : const bool bVerboseError =
1059 69 : !(strcmp(asRPCSingleValues[i].pszGDALName, RPC_ERR_BIAS) == 0 ||
1060 33 : strcmp(asRPCSingleValues[i].pszGDALName, RPC_ERR_RAND) == 0);
1061 36 : double dfRPCVal = GetDouble(oRPC, asRPCSingleValues[i].pszJsonName,
1062 : bVerboseError, bRPCErrorTmp);
1063 36 : if (bRPCErrorTmp)
1064 : {
1065 14 : if (bVerboseError)
1066 : {
1067 10 : bRPCError = true;
1068 : }
1069 14 : continue;
1070 : }
1071 22 : aoRPC.SetNameValue(asRPCSingleValues[i].pszGDALName,
1072 22 : CPLSPrintf("%.17g", dfRPCVal));
1073 : }
1074 :
1075 : const struct
1076 : {
1077 : const char *pszJsonName;
1078 : const char *pszGDALName;
1079 3 : } asRPCArrayValues[] = {
1080 : {"lineNumCoeff", RPC_LINE_NUM_COEFF},
1081 : {"lineDenCoeff", RPC_LINE_DEN_COEFF},
1082 : {"sampNumCoeff", RPC_SAMP_NUM_COEFF},
1083 : {"sampDenCoeff", RPC_SAMP_DEN_COEFF},
1084 : };
1085 :
1086 15 : for (size_t i = 0; i < CPL_ARRAYSIZE(asRPCArrayValues); ++i)
1087 : {
1088 : CPLJSONArray oRPCArray =
1089 36 : oRPC.GetArray(asRPCArrayValues[i].pszJsonName);
1090 12 : if (oRPCArray.IsValid() && oRPCArray.Size() == 20)
1091 : {
1092 16 : CPLString osVal;
1093 168 : for (int j = 0; j < 20; j++)
1094 : {
1095 160 : if (j > 0)
1096 152 : osVal += " ";
1097 160 : osVal += CPLSPrintf("%.17g", oRPCArray[j].ToDouble());
1098 : }
1099 8 : aoRPC.SetNameValue(asRPCArrayValues[i].pszGDALName,
1100 8 : osVal.c_str());
1101 : }
1102 : else
1103 : {
1104 4 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
1105 4 : asRPCArrayValues[i].pszJsonName);
1106 : }
1107 : }
1108 3 : if (!bRPCError)
1109 : {
1110 2 : SetMetadata(aoRPC.List(), "RPC");
1111 : }
1112 : }
1113 28 : }
1114 :
1115 : /************************************************************************/
1116 : /* SetupServerSideReprojection() */
1117 : /************************************************************************/
1118 :
1119 0 : bool GDALDAASDataset::SetupServerSideReprojection(const char *pszTargetSRS)
1120 : {
1121 0 : if (m_oSRS.IsEmpty() || !m_bGotGeoTransform)
1122 : {
1123 0 : CPLError(CE_Failure, CPLE_AppDefined,
1124 : "TARGET_SRS is specified, but projection and/or "
1125 : "geotransform are missing in image metadata");
1126 0 : return false;
1127 : }
1128 :
1129 0 : OGRSpatialReference oSRS;
1130 0 : if (oSRS.SetFromUserInput(
1131 : pszTargetSRS,
1132 0 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
1133 : OGRERR_NONE)
1134 : {
1135 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid TARGET_SRS value");
1136 0 : return false;
1137 : }
1138 :
1139 : // Check that we can find the EPSG code as we will need to
1140 : // provide as a urn to getBuffer
1141 0 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
1142 0 : const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
1143 0 : if (pszAuthorityName == nullptr || !EQUAL(pszAuthorityName, "EPSG") ||
1144 : pszAuthorityCode == nullptr)
1145 : {
1146 0 : CPLError(CE_Failure, CPLE_AppDefined,
1147 : "TARGET_SRS cannot be identified to a EPSG code");
1148 0 : return false;
1149 : }
1150 :
1151 0 : CPLString osTargetEPSGCode = CPLString("epsg:") + pszAuthorityCode;
1152 :
1153 0 : char *pszWKT = nullptr;
1154 0 : oSRS.exportToWkt(&pszWKT);
1155 0 : char **papszTO = CSLSetNameValue(nullptr, "DST_SRS", pszWKT);
1156 0 : CPLFree(pszWKT);
1157 :
1158 : void *hTransformArg =
1159 0 : GDALCreateGenImgProjTransformer2(this, nullptr, papszTO);
1160 0 : if (hTransformArg == nullptr)
1161 : {
1162 0 : CSLDestroy(papszTO);
1163 0 : return false;
1164 : }
1165 :
1166 0 : GDALTransformerInfo *psInfo = (GDALTransformerInfo *)hTransformArg;
1167 : double adfGeoTransform[6];
1168 : double adfExtent[4];
1169 : int nXSize, nYSize;
1170 :
1171 0 : if (GDALSuggestedWarpOutput2(this, psInfo->pfnTransform, hTransformArg,
1172 : adfGeoTransform, &nXSize, &nYSize, adfExtent,
1173 0 : 0) != CE_None)
1174 : {
1175 0 : CPLError(CE_Failure, CPLE_AppDefined,
1176 : "Cannot find extent in specified TARGET_SRS");
1177 0 : CSLDestroy(papszTO);
1178 0 : GDALDestroyGenImgProjTransformer(hTransformArg);
1179 0 : return false;
1180 : }
1181 :
1182 0 : GDALDestroyGenImgProjTransformer(hTransformArg);
1183 :
1184 0 : std::copy_n(adfGeoTransform, 6, m_adfGeoTransform.begin());
1185 0 : m_bRequestInGeoreferencedCoordinates = true;
1186 0 : m_osSRSType = "epsg";
1187 0 : m_osSRSValue = std::move(osTargetEPSGCode);
1188 0 : m_oSRS = std::move(oSRS);
1189 0 : nRasterXSize = nXSize;
1190 0 : nRasterYSize = nYSize;
1191 0 : return true;
1192 : }
1193 :
1194 : /************************************************************************/
1195 : /* Open() */
1196 : /************************************************************************/
1197 :
1198 45 : bool GDALDAASDataset::Open(GDALOpenInfo *poOpenInfo)
1199 : {
1200 45 : m_papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
1201 : m_osGetMetadataURL =
1202 45 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "GET_METADATA_URL",
1203 45 : poOpenInfo->pszFilename + strlen("DAAS:"));
1204 45 : if (m_osGetMetadataURL.empty())
1205 : {
1206 1 : CPLError(CE_Failure, CPLE_AppDefined, "GET_METADATA_URL is missing");
1207 1 : return false;
1208 : }
1209 44 : m_nBlockSize =
1210 44 : std::max(knMIN_BLOCKSIZE,
1211 : std::min(knMAX_BLOCKSIZE,
1212 44 : atoi(CSLFetchNameValueDef(
1213 44 : poOpenInfo->papszOpenOptions, "BLOCK_SIZE",
1214 44 : CPLSPrintf("%d", m_nBlockSize)))));
1215 44 : m_nServerByteLimit =
1216 44 : atoi(CPLGetConfigOption("GDAL_DAAS_SERVER_BYTE_LIMIT",
1217 : CPLSPrintf("%d", knDEFAULT_SERVER_BYTE_LIMIT)));
1218 :
1219 53 : if (CPLTestBool(CPLGetConfigOption("GDAL_DAAS_PERFORM_AUTH", "YES")) &&
1220 9 : !GetAuthorization())
1221 5 : return false;
1222 39 : if (!GetImageMetadata())
1223 19 : return false;
1224 :
1225 20 : const char *pszFormat = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
1226 : "PIXEL_ENCODING", "AUTO");
1227 20 : if (EQUAL(pszFormat, "AUTO"))
1228 : {
1229 9 : if ((m_aoBandDesc.size() == 1 || m_aoBandDesc.size() == 3 ||
1230 14 : m_aoBandDesc.size() == 4) &&
1231 7 : m_eDT == GDT_Byte)
1232 : {
1233 7 : m_eFormat = Format::PNG;
1234 : }
1235 : else
1236 : {
1237 0 : m_eFormat = Format::RAW;
1238 : }
1239 : }
1240 13 : else if (EQUAL(pszFormat, "RAW"))
1241 : {
1242 6 : m_eFormat = Format::RAW;
1243 : }
1244 7 : else if (EQUAL(pszFormat, "PNG"))
1245 : {
1246 3 : if ((m_aoBandDesc.size() == 1 || m_aoBandDesc.size() == 3 ||
1247 4 : m_aoBandDesc.size() == 4) &&
1248 2 : m_eDT == GDT_Byte)
1249 : {
1250 1 : m_eFormat = Format::PNG;
1251 : }
1252 : else
1253 : {
1254 1 : CPLError(CE_Warning, CPLE_AppDefined,
1255 : "PNG only supported for 1, 3 or 4-band Byte dataset. "
1256 : "Falling back to RAW");
1257 1 : m_eFormat = Format::RAW;
1258 : }
1259 : }
1260 5 : else if (EQUAL(pszFormat, "JPEG"))
1261 : {
1262 4 : if ((m_aoBandDesc.size() == 1 || m_aoBandDesc.size() == 3) &&
1263 2 : m_eDT == GDT_Byte)
1264 : {
1265 1 : m_eFormat = Format::JPEG;
1266 : }
1267 : else
1268 : {
1269 1 : CPLError(CE_Warning, CPLE_AppDefined,
1270 : "JPEG only supported for 1 or 3-band Byte dataset. "
1271 : "Falling back to RAW");
1272 1 : m_eFormat = Format::RAW;
1273 : }
1274 : }
1275 3 : else if (EQUAL(pszFormat, "JPEG2000"))
1276 : {
1277 2 : if (m_eDT != GDT_Float32 && m_eDT != GDT_Float64)
1278 : {
1279 1 : m_eFormat = Format::JPEG2000;
1280 : }
1281 : else
1282 : {
1283 1 : CPLError(CE_Warning, CPLE_AppDefined,
1284 : "JPEG2000 only supported for integer datatype dataset. "
1285 : "Falling back to RAW");
1286 1 : m_eFormat = Format::RAW;
1287 : }
1288 : }
1289 : else
1290 : {
1291 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported PIXEL_ENCODING=%s",
1292 : pszFormat);
1293 1 : return false;
1294 : }
1295 :
1296 : const char *pszTargetSRS =
1297 19 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TARGET_SRS");
1298 19 : if (pszTargetSRS)
1299 : {
1300 0 : if (!SetupServerSideReprojection(pszTargetSRS))
1301 : {
1302 0 : return false;
1303 : }
1304 : }
1305 :
1306 19 : InstantiateBands();
1307 :
1308 : // Instantiate overviews
1309 19 : int iOvr = 0;
1310 25 : while ((nRasterXSize >> iOvr) > 256 || (nRasterYSize >> iOvr) > 256)
1311 : {
1312 6 : iOvr++;
1313 6 : if ((nRasterXSize >> iOvr) == 0 || (nRasterYSize >> iOvr) == 0)
1314 : {
1315 : break;
1316 : }
1317 6 : m_apoOverviewDS.push_back(
1318 12 : std::make_unique<GDALDAASDataset>(this, iOvr));
1319 : }
1320 :
1321 19 : return true;
1322 : }
1323 :
1324 45 : GDALDataset *GDALDAASDataset::OpenStatic(GDALOpenInfo *poOpenInfo)
1325 : {
1326 45 : if (!Identify(poOpenInfo))
1327 0 : return nullptr;
1328 :
1329 90 : auto poDS = std::make_unique<GDALDAASDataset>();
1330 45 : if (!poDS->Open(poOpenInfo))
1331 26 : return nullptr;
1332 19 : return poDS.release();
1333 : }
1334 :
1335 : /************************************************************************/
1336 : /* GDALDAASRasterBand() */
1337 : /************************************************************************/
1338 :
1339 40 : GDALDAASRasterBand::GDALDAASRasterBand(GDALDAASDataset *poDSIn, int nBandIn,
1340 40 : const GDALDAASBandDesc &oBandDesc)
1341 : {
1342 40 : poDS = poDSIn;
1343 40 : nBand = nBandIn;
1344 40 : eDataType = poDSIn->m_eDT;
1345 40 : nRasterXSize = poDSIn->GetRasterXSize();
1346 40 : nRasterYSize = poDSIn->GetRasterYSize();
1347 40 : nBlockXSize = poDSIn->m_nBlockSize;
1348 40 : nBlockYSize = poDSIn->m_nBlockSize;
1349 40 : m_nSrcIndex = oBandDesc.nIndex;
1350 :
1351 40 : SetDescription(oBandDesc.osName);
1352 40 : if (!oBandDesc.osDescription.empty())
1353 : {
1354 4 : SetMetadataItem("DESCRIPTION", oBandDesc.osDescription);
1355 : }
1356 :
1357 : const struct
1358 : {
1359 : const char *pszName;
1360 : GDALColorInterp eColorInterp;
1361 40 : } asColorInterpretations[] = {
1362 : {"RED", GCI_RedBand}, {"GREEN", GCI_GreenBand},
1363 : {"BLUE", GCI_BlueBand}, {"GRAY", GCI_GrayIndex},
1364 : {"ALPHA", GCI_AlphaBand}, {"UNDEFINED", GCI_Undefined},
1365 : };
1366 :
1367 268 : for (size_t i = 0; i < CPL_ARRAYSIZE(asColorInterpretations); ++i)
1368 : {
1369 232 : if (EQUAL(oBandDesc.osColorInterp, asColorInterpretations[i].pszName))
1370 : {
1371 4 : m_eColorInterp = asColorInterpretations[i].eColorInterp;
1372 4 : break;
1373 : }
1374 : }
1375 40 : if (!oBandDesc.osColorInterp.empty() &&
1376 44 : !EQUAL(oBandDesc.osColorInterp, "UNDEFINED") &&
1377 4 : m_eColorInterp != GCI_Undefined)
1378 : {
1379 4 : SetMetadataItem("COLOR_INTERPRETATION", oBandDesc.osColorInterp);
1380 : }
1381 :
1382 40 : if (poDSIn->m_nActualBitDepth != 0 && poDSIn->m_nActualBitDepth != 8 &&
1383 2 : poDSIn->m_nActualBitDepth != 16 && poDSIn->m_nActualBitDepth != 32 &&
1384 2 : poDSIn->m_nActualBitDepth != 64)
1385 : {
1386 2 : SetMetadataItem("NBITS", CPLSPrintf("%d", poDSIn->m_nActualBitDepth),
1387 : "IMAGE_STRUCTURE");
1388 : }
1389 40 : }
1390 :
1391 : /************************************************************************/
1392 : /* GetNoDataValue() */
1393 : /************************************************************************/
1394 :
1395 2 : double GDALDAASRasterBand::GetNoDataValue(int *pbHasNoData)
1396 : {
1397 2 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1398 2 : if (poGDS->m_bHasNoData)
1399 : {
1400 1 : if (pbHasNoData)
1401 1 : *pbHasNoData = true;
1402 1 : return poGDS->m_dfNoDataValue;
1403 : }
1404 1 : if (pbHasNoData)
1405 1 : *pbHasNoData = false;
1406 1 : return 0.0;
1407 : }
1408 :
1409 : /************************************************************************/
1410 : /* GetColorInterpretation() */
1411 : /************************************************************************/
1412 :
1413 1 : GDALColorInterp GDALDAASRasterBand::GetColorInterpretation()
1414 : {
1415 1 : return m_eColorInterp;
1416 : }
1417 :
1418 : /************************************************************************/
1419 : /* GetMaskBand() */
1420 : /************************************************************************/
1421 :
1422 3 : GDALRasterBand *GDALDAASRasterBand::GetMaskBand()
1423 : {
1424 3 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1425 3 : if (poGDS->m_poMaskBand)
1426 3 : return poGDS->m_poMaskBand;
1427 0 : return GDALRasterBand::GetMaskBand();
1428 : }
1429 :
1430 : /************************************************************************/
1431 : /* GetMaskFlags() */
1432 : /************************************************************************/
1433 :
1434 1 : int GDALDAASRasterBand::GetMaskFlags()
1435 : {
1436 1 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1437 1 : if (poGDS->m_poMaskBand)
1438 1 : return GMF_PER_DATASET;
1439 0 : return GDALRasterBand::GetMaskFlags();
1440 : }
1441 :
1442 : /************************************************************************/
1443 : /* CanSpatiallySplit() */
1444 : /************************************************************************/
1445 :
1446 32 : static bool CanSpatiallySplit(GUInt32 nRetryFlags, int nXOff, int nYOff,
1447 : int nXSize, int nYSize, int nBufXSize,
1448 : int nBufYSize, int nBlockXSize, int nBlockYSize,
1449 : GSpacing nPixelSpace, GSpacing nLineSpace,
1450 : int &nXOff1, int &nYOff1, int &nXSize1,
1451 : int &nYSize1, int &nXOff2, int &nYOff2,
1452 : int &nXSize2, int &nYSize2, GSpacing &nDataShift2)
1453 : {
1454 32 : if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
1455 1 : nYSize == nBufYSize && nYSize > nBlockYSize)
1456 : {
1457 : int nHalf =
1458 0 : std::max(nBlockYSize, ((nYSize / 2) / nBlockYSize) * nBlockYSize);
1459 0 : nXOff1 = nXOff;
1460 0 : nYOff1 = nYOff;
1461 0 : nXSize1 = nXSize;
1462 0 : nYSize1 = nHalf;
1463 0 : nXOff2 = nXOff;
1464 0 : nYOff2 = nYOff + nHalf;
1465 0 : nXSize2 = nXSize;
1466 0 : nYSize2 = nYSize - nHalf;
1467 0 : nDataShift2 = nHalf * nLineSpace;
1468 0 : return true;
1469 : }
1470 32 : else if ((nRetryFlags & RETRY_SPATIAL_SPLIT) && nXSize == nBufXSize &&
1471 1 : nYSize == nBufYSize && nXSize > nBlockXSize)
1472 : {
1473 : int nHalf =
1474 1 : std::max(nBlockXSize, ((nXSize / 2) / nBlockXSize) * nBlockXSize);
1475 1 : nXOff1 = nXOff;
1476 1 : nYOff1 = nYOff;
1477 1 : nXSize1 = nHalf;
1478 1 : nYSize1 = nYSize;
1479 1 : nXOff2 = nXOff + nHalf;
1480 1 : nYOff2 = nYOff;
1481 1 : nXSize2 = nXSize - nHalf;
1482 1 : nYSize2 = nYSize;
1483 1 : nDataShift2 = nHalf * nPixelSpace;
1484 1 : return true;
1485 : }
1486 31 : return false;
1487 : }
1488 :
1489 : /************************************************************************/
1490 : /* IRasterIO() */
1491 : /************************************************************************/
1492 :
1493 4 : CPLErr GDALDAASDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1494 : int nXSize, int nYSize, void *pData,
1495 : int nBufXSize, int nBufYSize,
1496 : GDALDataType eBufType, int nBandCount,
1497 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1498 : GSpacing nLineSpace, GSpacing nBandSpace,
1499 : GDALRasterIOExtraArg *psExtraArg)
1500 : {
1501 4 : m_eCurrentResampleAlg = psExtraArg->eResampleAlg;
1502 :
1503 : /* ==================================================================== */
1504 : /* Do we have overviews that would be appropriate to satisfy */
1505 : /* this request? */
1506 : /* ==================================================================== */
1507 4 : if ((nBufXSize < nXSize || nBufYSize < nYSize) &&
1508 8 : GetRasterBand(1)->GetOverviewCount() > 0 && eRWFlag == GF_Read)
1509 : {
1510 : GDALRasterIOExtraArg sExtraArg;
1511 0 : GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
1512 :
1513 0 : const int nOverview = GDALBandGetBestOverviewLevel2(
1514 : GetRasterBand(1), nXOff, nYOff, nXSize, nYSize, nBufXSize,
1515 : nBufYSize, &sExtraArg);
1516 0 : if (nOverview >= 0)
1517 : {
1518 : GDALRasterBand *poOverviewBand =
1519 0 : GetRasterBand(1)->GetOverview(nOverview);
1520 0 : if (poOverviewBand == nullptr ||
1521 0 : poOverviewBand->GetDataset() == nullptr)
1522 : {
1523 0 : return CE_Failure;
1524 : }
1525 :
1526 0 : return poOverviewBand->GetDataset()->RasterIO(
1527 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
1528 : nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
1529 0 : nLineSpace, nBandSpace, &sExtraArg);
1530 : }
1531 : }
1532 :
1533 : GDALDAASRasterBand *poBand =
1534 4 : cpl::down_cast<GDALDAASRasterBand *>(GetRasterBand(1));
1535 :
1536 8 : std::vector<int> anRequestedBands;
1537 4 : if (m_poMaskBand)
1538 1 : anRequestedBands.push_back(0);
1539 8 : for (int i = 1; i <= GetRasterCount(); i++)
1540 4 : anRequestedBands.push_back(i);
1541 : GUInt32 nRetryFlags =
1542 4 : poBand->PrefetchBlocks(nXOff, nYOff, nXSize, nYSize, anRequestedBands);
1543 : int nBlockXSize, nBlockYSize;
1544 4 : poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
1545 4 : int nXOff1 = 0;
1546 4 : int nYOff1 = 0;
1547 4 : int nXSize1 = 0;
1548 4 : int nYSize1 = 0;
1549 4 : int nXOff2 = 0;
1550 4 : int nYOff2 = 0;
1551 4 : int nXSize2 = 0;
1552 4 : int nYSize2 = 0;
1553 4 : GSpacing nDataShift2 = 0;
1554 4 : if (CanSpatiallySplit(nRetryFlags, nXOff, nYOff, nXSize, nYSize, nBufXSize,
1555 : nBufYSize, nBlockXSize, nBlockYSize, nPixelSpace,
1556 : nLineSpace, nXOff1, nYOff1, nXSize1, nYSize1, nXOff2,
1557 : nYOff2, nXSize2, nYSize2, nDataShift2))
1558 : {
1559 : GDALRasterIOExtraArg sExtraArg;
1560 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1561 :
1562 : CPLErr eErr =
1563 0 : IRasterIO(eRWFlag, nXOff1, nYOff1, nXSize1, nYSize1, pData, nXSize1,
1564 : nYSize1, eBufType, nBandCount, panBandMap, nPixelSpace,
1565 : nLineSpace, nBandSpace, &sExtraArg);
1566 0 : if (eErr == CE_None)
1567 : {
1568 0 : eErr = IRasterIO(eRWFlag, nXOff2, nYOff2, nXSize2, nYSize2,
1569 0 : static_cast<GByte *>(pData) + nDataShift2, nXSize2,
1570 : nYSize2, eBufType, nBandCount, panBandMap,
1571 : nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
1572 : }
1573 0 : return eErr;
1574 : }
1575 4 : else if ((nRetryFlags & RETRY_PER_BAND) && nBands > 1)
1576 : {
1577 0 : for (int iBand = 1; iBand <= nBands; iBand++)
1578 : {
1579 0 : poBand = cpl::down_cast<GDALDAASRasterBand *>(GetRasterBand(iBand));
1580 0 : CPL_IGNORE_RET_VAL(poBand->PrefetchBlocks(
1581 0 : nXOff, nYOff, nXSize, nYSize, std::vector<int>{iBand}));
1582 : }
1583 : }
1584 :
1585 4 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1586 : nBufXSize, nBufYSize, eBufType, nBandCount,
1587 : panBandMap, nPixelSpace, nLineSpace,
1588 4 : nBandSpace, psExtraArg);
1589 : }
1590 :
1591 : /************************************************************************/
1592 : /* AdviseRead() */
1593 : /************************************************************************/
1594 :
1595 1 : CPLErr GDALDAASDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
1596 : int nBufXSize, int nBufYSize,
1597 : GDALDataType /* eBufType */, int /*nBands*/,
1598 : int * /*panBands*/,
1599 : char ** /* papszOptions */)
1600 : {
1601 1 : if (nXSize == nBufXSize && nYSize == nBufYSize)
1602 : {
1603 1 : m_nXOffAdvise = nXOff;
1604 1 : m_nYOffAdvise = nYOff;
1605 1 : m_nXSizeAdvise = nXSize;
1606 1 : m_nYSizeAdvise = nYSize;
1607 : }
1608 1 : return CE_None;
1609 : }
1610 :
1611 : /************************************************************************/
1612 : /* FlushCache() */
1613 : /************************************************************************/
1614 :
1615 10 : CPLErr GDALDAASDataset::FlushCache(bool bAtClosing)
1616 : {
1617 10 : CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
1618 10 : m_nXOffFetched = 0;
1619 10 : m_nYOffFetched = 0;
1620 10 : m_nXSizeFetched = 0;
1621 10 : m_nYSizeFetched = 0;
1622 10 : return eErr;
1623 : }
1624 :
1625 : /************************************************************************/
1626 : /* GetOverviewCount() */
1627 : /************************************************************************/
1628 :
1629 4 : int GDALDAASRasterBand::GetOverviewCount()
1630 : {
1631 4 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1632 4 : return static_cast<int>(poGDS->m_apoOverviewDS.size());
1633 : }
1634 :
1635 : /************************************************************************/
1636 : /* GetOverview() */
1637 : /************************************************************************/
1638 :
1639 5 : GDALRasterBand *GDALDAASRasterBand::GetOverview(int iIndex)
1640 : {
1641 5 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1642 5 : if (iIndex >= 0 && iIndex < static_cast<int>(poGDS->m_apoOverviewDS.size()))
1643 : {
1644 3 : return poGDS->m_apoOverviewDS[iIndex]->GetRasterBand(nBand);
1645 : }
1646 2 : return nullptr;
1647 : }
1648 :
1649 : /************************************************************************/
1650 : /* IReadBlock() */
1651 : /************************************************************************/
1652 :
1653 11 : CPLErr GDALDAASRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1654 : void *pImage)
1655 : {
1656 22 : return GetBlocks(nBlockXOff, nBlockYOff, 1, 1, std::vector<int>{nBand},
1657 22 : pImage);
1658 : }
1659 :
1660 : /************************************************************************/
1661 : /* IRasterIO() */
1662 : /************************************************************************/
1663 :
1664 29 : CPLErr GDALDAASRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1665 : int nXSize, int nYSize, void *pData,
1666 : int nBufXSize, int nBufYSize,
1667 : GDALDataType eBufType,
1668 : GSpacing nPixelSpace, GSpacing nLineSpace,
1669 : GDALRasterIOExtraArg *psExtraArg)
1670 : {
1671 29 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1672 :
1673 29 : poGDS->m_eCurrentResampleAlg = psExtraArg->eResampleAlg;
1674 :
1675 : /* ==================================================================== */
1676 : /* Do we have overviews that would be appropriate to satisfy */
1677 : /* this request? */
1678 : /* ==================================================================== */
1679 29 : if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0 &&
1680 : eRWFlag == GF_Read)
1681 : {
1682 : GDALRasterIOExtraArg sExtraArg;
1683 1 : GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
1684 :
1685 : const int nOverview =
1686 1 : GDALBandGetBestOverviewLevel2(this, nXOff, nYOff, nXSize, nYSize,
1687 : nBufXSize, nBufYSize, &sExtraArg);
1688 1 : if (nOverview >= 0)
1689 : {
1690 1 : GDALRasterBand *poOverviewBand = GetOverview(nOverview);
1691 1 : if (poOverviewBand == nullptr)
1692 1 : return CE_Failure;
1693 :
1694 1 : return poOverviewBand->RasterIO(
1695 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
1696 1 : nBufYSize, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
1697 : }
1698 : }
1699 :
1700 56 : std::vector<int> anRequestedBands;
1701 28 : if (poGDS->m_poMaskBand)
1702 3 : anRequestedBands.push_back(0);
1703 88 : for (int i = 1; i <= poGDS->GetRasterCount(); i++)
1704 60 : anRequestedBands.push_back(i);
1705 : GUInt32 nRetryFlags =
1706 28 : PrefetchBlocks(nXOff, nYOff, nXSize, nYSize, anRequestedBands);
1707 28 : int nXOff1 = 0;
1708 28 : int nYOff1 = 0;
1709 28 : int nXSize1 = 0;
1710 28 : int nYSize1 = 0;
1711 28 : int nXOff2 = 0;
1712 28 : int nYOff2 = 0;
1713 28 : int nXSize2 = 0;
1714 28 : int nYSize2 = 0;
1715 28 : GSpacing nDataShift2 = 0;
1716 28 : if (CanSpatiallySplit(nRetryFlags, nXOff, nYOff, nXSize, nYSize, nBufXSize,
1717 : nBufYSize, nBlockXSize, nBlockYSize, nPixelSpace,
1718 : nLineSpace, nXOff1, nYOff1, nXSize1, nYSize1, nXOff2,
1719 : nYOff2, nXSize2, nYSize2, nDataShift2))
1720 : {
1721 : GDALRasterIOExtraArg sExtraArg;
1722 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1723 :
1724 : CPLErr eErr =
1725 1 : IRasterIO(eRWFlag, nXOff1, nYOff1, nXSize1, nYSize1, pData, nXSize1,
1726 : nYSize1, eBufType, nPixelSpace, nLineSpace, &sExtraArg);
1727 1 : if (eErr == CE_None)
1728 : {
1729 1 : eErr = IRasterIO(eRWFlag, nXOff2, nYOff2, nXSize2, nYSize2,
1730 1 : static_cast<GByte *>(pData) + nDataShift2, nXSize2,
1731 : nYSize2, eBufType, nPixelSpace, nLineSpace,
1732 : &sExtraArg);
1733 : }
1734 1 : return eErr;
1735 : }
1736 27 : else if ((nRetryFlags & RETRY_PER_BAND) && poGDS->nBands > 1)
1737 : {
1738 0 : CPL_IGNORE_RET_VAL(PrefetchBlocks(nXOff, nYOff, nXSize, nYSize,
1739 0 : std::vector<int>{nBand}));
1740 : }
1741 :
1742 27 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1743 : pData, nBufXSize, nBufYSize, eBufType,
1744 27 : nPixelSpace, nLineSpace, psExtraArg);
1745 : }
1746 :
1747 : /************************************************************************/
1748 : /* AdviseRead() */
1749 : /************************************************************************/
1750 :
1751 2 : CPLErr GDALDAASRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize,
1752 : int nYSize, int nBufXSize, int nBufYSize,
1753 : GDALDataType /* eBufType */,
1754 : char ** /* papszOptions */)
1755 : {
1756 2 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1757 2 : if (nXSize == nBufXSize && nYSize == nBufYSize)
1758 : {
1759 2 : poGDS->m_nXOffAdvise = nXOff;
1760 2 : poGDS->m_nYOffAdvise = nYOff;
1761 2 : poGDS->m_nXSizeAdvise = nXSize;
1762 2 : poGDS->m_nYSizeAdvise = nYSize;
1763 : }
1764 2 : return CE_None;
1765 : }
1766 :
1767 : /************************************************************************/
1768 : /* PrefetchBlocks() */
1769 : /************************************************************************/
1770 :
1771 : // Return or'ed flags among 0, RETRY_PER_BAND, RETRY_SPATIAL_SPLIT if the user
1772 : // should try to split the request in smaller chunks
1773 :
1774 : GUInt32
1775 32 : GDALDAASRasterBand::PrefetchBlocks(int nXOff, int nYOff, int nXSize, int nYSize,
1776 : const std::vector<int> &anRequestedBands)
1777 : {
1778 32 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1779 :
1780 32 : if (anRequestedBands.size() > 1)
1781 : {
1782 20 : if (poGDS->m_nXOffFetched == nXOff && poGDS->m_nYOffFetched == nYOff &&
1783 20 : poGDS->m_nXSizeFetched == nXSize &&
1784 5 : poGDS->m_nYSizeFetched == nYSize)
1785 : {
1786 5 : return 0;
1787 : }
1788 15 : poGDS->m_nXOffFetched = nXOff;
1789 15 : poGDS->m_nYOffFetched = nYOff;
1790 15 : poGDS->m_nXSizeFetched = nXSize;
1791 15 : poGDS->m_nYSizeFetched = nYSize;
1792 : }
1793 :
1794 27 : int nBlockXOff = nXOff / nBlockXSize;
1795 27 : int nBlockYOff = nYOff / nBlockYSize;
1796 27 : int nXBlocks = (nXOff + nXSize - 1) / nBlockXSize - nBlockXOff + 1;
1797 27 : int nYBlocks = (nYOff + nYSize - 1) / nBlockYSize - nBlockYOff + 1;
1798 :
1799 27 : int nTotalDataTypeSize = 0;
1800 27 : const int nQueriedBands = static_cast<int>(anRequestedBands.size());
1801 82 : for (int i = 0; i < nQueriedBands; i++)
1802 : {
1803 55 : const int iBand = anRequestedBands[i];
1804 55 : if (iBand > 0 && iBand <= poGDS->GetRasterCount())
1805 : {
1806 53 : nTotalDataTypeSize += GDALGetDataTypeSizeBytes(
1807 : poGDS->GetRasterBand(iBand)->GetRasterDataType());
1808 : }
1809 : else
1810 : {
1811 2 : nTotalDataTypeSize += GDALGetDataTypeSizeBytes(
1812 2 : poGDS->m_poMaskBand->GetRasterDataType());
1813 : }
1814 : }
1815 :
1816 : // If AdviseRead() was called before, and the current requested area is
1817 : // in it, check if we can prefetch the whole advised area
1818 27 : const GIntBig nCacheMax = GDALGetCacheMax64() / 2;
1819 27 : if (poGDS->m_nXSizeAdvise > 0 && nXOff >= poGDS->m_nXOffAdvise &&
1820 10 : nYOff >= poGDS->m_nYOffAdvise &&
1821 10 : nXOff + nXSize <= poGDS->m_nXOffAdvise + poGDS->m_nXSizeAdvise &&
1822 10 : nYOff + nYSize <= poGDS->m_nYOffAdvise + poGDS->m_nYSizeAdvise)
1823 : {
1824 10 : int nBlockXOffAdvise = poGDS->m_nXOffAdvise / nBlockXSize;
1825 10 : int nBlockYOffAdvise = poGDS->m_nYOffAdvise / nBlockYSize;
1826 10 : int nXBlocksAdvise =
1827 10 : (poGDS->m_nXOffAdvise + poGDS->m_nXSizeAdvise - 1) / nBlockXSize -
1828 : nBlockXOffAdvise + 1;
1829 10 : int nYBlocksAdvise =
1830 10 : (poGDS->m_nYOffAdvise + poGDS->m_nYSizeAdvise - 1) / nBlockYSize -
1831 : nBlockYOffAdvise + 1;
1832 10 : const GIntBig nUncompressedSize = static_cast<GIntBig>(nXBlocksAdvise) *
1833 10 : nYBlocksAdvise * nBlockXSize *
1834 10 : nBlockYSize * nTotalDataTypeSize;
1835 10 : if (nUncompressedSize <= nCacheMax &&
1836 10 : nUncompressedSize <= poGDS->m_nServerByteLimit)
1837 : {
1838 7 : CPLDebug("DAAS", "Using advise read");
1839 7 : nBlockXOff = nBlockXOffAdvise;
1840 7 : nBlockYOff = nBlockYOffAdvise;
1841 7 : nXBlocks = nXBlocksAdvise;
1842 7 : nYBlocks = nYBlocksAdvise;
1843 7 : if (anRequestedBands.size() > 1)
1844 : {
1845 0 : poGDS->m_nXOffAdvise = 0;
1846 0 : poGDS->m_nYOffAdvise = 0;
1847 0 : poGDS->m_nXSizeAdvise = 0;
1848 0 : poGDS->m_nYSizeAdvise = 0;
1849 : }
1850 : }
1851 : }
1852 :
1853 : // Check the number of already cached blocks, and remove fully
1854 : // cached lines at the top of the area of interest from the queried
1855 : // blocks
1856 27 : int nBlocksCached = 0;
1857 27 : int nBlocksCachedForThisBand = 0;
1858 27 : bool bAllLineCached = true;
1859 54 : for (int iYBlock = 0; iYBlock < nYBlocks;)
1860 : {
1861 62 : for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
1862 : {
1863 98 : for (int i = 0; i < nQueriedBands; i++)
1864 : {
1865 63 : const int iBand = anRequestedBands[i];
1866 63 : GDALRasterBlock *poBlock = nullptr;
1867 : GDALDAASRasterBand *poIterBand;
1868 63 : if (iBand > 0 && iBand <= poGDS->GetRasterCount())
1869 : poIterBand = reinterpret_cast<GDALDAASRasterBand *>(
1870 61 : poGDS->GetRasterBand(iBand));
1871 : else
1872 2 : poIterBand = poGDS->m_poMaskBand;
1873 :
1874 126 : poBlock = poIterBand->TryGetLockedBlockRef(
1875 63 : nBlockXOff + iXBlock, nBlockYOff + iYBlock);
1876 63 : if (poBlock != nullptr)
1877 : {
1878 11 : nBlocksCached++;
1879 11 : if (iBand == nBand)
1880 10 : nBlocksCachedForThisBand++;
1881 11 : poBlock->DropLock();
1882 11 : continue;
1883 : }
1884 : else
1885 : {
1886 52 : bAllLineCached = false;
1887 : }
1888 : }
1889 : }
1890 :
1891 27 : if (bAllLineCached)
1892 : {
1893 5 : nBlocksCached -= nXBlocks * nQueriedBands;
1894 5 : nBlocksCachedForThisBand -= nXBlocks;
1895 5 : nBlockYOff++;
1896 5 : nYBlocks--;
1897 : }
1898 : else
1899 : {
1900 22 : iYBlock++;
1901 : }
1902 : }
1903 :
1904 27 : if (nXBlocks > 0 && nYBlocks > 0)
1905 : {
1906 22 : bool bMustReturn = false;
1907 22 : GUInt32 nRetryFlags = 0;
1908 :
1909 : // Get the blocks if the number of already cached blocks is lesser
1910 : // than 25% of the to be queried blocks
1911 22 : if (nBlocksCached > (nQueriedBands * nXBlocks * nYBlocks) / 4)
1912 : {
1913 1 : if (nBlocksCachedForThisBand <= (nXBlocks * nYBlocks) / 4)
1914 : {
1915 1 : nRetryFlags |= RETRY_PER_BAND;
1916 : }
1917 : else
1918 : {
1919 0 : bMustReturn = true;
1920 : }
1921 : }
1922 :
1923 : // Make sure that we have enough cache (with a margin of 50%)
1924 : // and the number of queried pixels isn't too big w.r.t server
1925 : // limit
1926 22 : const GIntBig nUncompressedSize = static_cast<GIntBig>(nXBlocks) *
1927 22 : nYBlocks * nBlockXSize * nBlockYSize *
1928 22 : nTotalDataTypeSize;
1929 22 : if (nUncompressedSize > nCacheMax ||
1930 22 : nUncompressedSize > poGDS->m_nServerByteLimit)
1931 : {
1932 3 : if (anRequestedBands.size() > 1 && poGDS->GetRasterCount() > 1)
1933 : {
1934 0 : const int nThisDTSize = GDALGetDataTypeSizeBytes(eDataType);
1935 0 : const GIntBig nUncompressedSizeThisBand =
1936 0 : static_cast<GIntBig>(nXBlocks) * nYBlocks * nBlockXSize *
1937 0 : nBlockYSize * nThisDTSize;
1938 0 : if (nUncompressedSizeThisBand <= poGDS->m_nServerByteLimit &&
1939 : nUncompressedSizeThisBand <= nCacheMax)
1940 : {
1941 0 : nRetryFlags |= RETRY_PER_BAND;
1942 : }
1943 : }
1944 3 : if (nXBlocks > 1 || nYBlocks > 1)
1945 : {
1946 1 : nRetryFlags |= RETRY_SPATIAL_SPLIT;
1947 : }
1948 3 : return nRetryFlags;
1949 : }
1950 19 : if (bMustReturn)
1951 0 : return nRetryFlags;
1952 :
1953 19 : GetBlocks(nBlockXOff, nBlockYOff, nXBlocks, nYBlocks, anRequestedBands,
1954 : nullptr);
1955 : }
1956 :
1957 24 : return 0;
1958 : }
1959 :
1960 : /************************************************************************/
1961 : /* GetBlocks() */
1962 : /************************************************************************/
1963 :
1964 34 : CPLErr GDALDAASRasterBand::GetBlocks(int nBlockXOff, int nBlockYOff,
1965 : int nXBlocks, int nYBlocks,
1966 : const std::vector<int> &anRequestedBands,
1967 : void *pDstBuffer)
1968 : {
1969 34 : GDALDAASDataset *poGDS = reinterpret_cast<GDALDAASDataset *>(poDS);
1970 :
1971 34 : CPLAssert(!anRequestedBands.empty());
1972 34 : if (pDstBuffer)
1973 : {
1974 11 : CPLAssert(nXBlocks == 1 && nYBlocks == 1 &&
1975 : anRequestedBands.size() == 1);
1976 : }
1977 :
1978 : // Detect if there is a mix of non-mask and mask bands
1979 34 : if (anRequestedBands.size() > 1)
1980 : {
1981 15 : std::vector<int> anNonMasks;
1982 15 : std::vector<int> anMasks;
1983 58 : for (auto &iBand : anRequestedBands)
1984 : {
1985 84 : if (iBand == MAIN_MASK_BAND_NUMBER ||
1986 41 : poGDS->m_aoBandDesc[iBand - 1].bIsMask)
1987 2 : anMasks.push_back(iBand);
1988 : else
1989 41 : anNonMasks.push_back(iBand);
1990 : }
1991 15 : if (!anNonMasks.empty() && !anMasks.empty())
1992 : {
1993 2 : return GetBlocks(nBlockXOff, nBlockYOff, nXBlocks, nYBlocks,
1994 2 : anNonMasks, nullptr) == CE_None &&
1995 2 : GetBlocks(nBlockXOff, nBlockYOff, nXBlocks, nYBlocks,
1996 : anMasks, nullptr) == CE_None
1997 4 : ? CE_None
1998 2 : : CE_Failure;
1999 : }
2000 : }
2001 :
2002 32 : char **papszOptions = poGDS->GetHTTPOptions();
2003 :
2004 64 : CPLString osHeaders = CSLFetchNameValueDef(papszOptions, "HEADERS", "");
2005 32 : if (!osHeaders.empty())
2006 0 : osHeaders += "\r\n";
2007 32 : osHeaders += "Content-Type: application/json";
2008 32 : osHeaders += "\r\n";
2009 64 : CPLString osDataContentType("application/octet-stream");
2010 32 : GDALDAASDataset::Format eRequestFormat(GDALDAASDataset::Format::RAW);
2011 41 : if (poGDS->m_eFormat == GDALDAASDataset::Format::PNG &&
2012 9 : (anRequestedBands.size() == 1 || anRequestedBands.size() == 3 ||
2013 0 : anRequestedBands.size() == 4))
2014 : {
2015 6 : eRequestFormat = poGDS->m_eFormat;
2016 6 : osDataContentType = "image/png";
2017 : }
2018 28 : else if (poGDS->m_eFormat == GDALDAASDataset::Format::JPEG &&
2019 2 : (anRequestedBands.size() == 1 || anRequestedBands.size() == 3))
2020 : {
2021 1 : eRequestFormat = poGDS->m_eFormat;
2022 1 : osDataContentType = "image/jpeg";
2023 : }
2024 25 : else if (poGDS->m_eFormat == GDALDAASDataset::Format::JPEG2000)
2025 : {
2026 1 : eRequestFormat = poGDS->m_eFormat;
2027 1 : osDataContentType = "image/jp2";
2028 : }
2029 32 : osHeaders += "Accept: " + osDataContentType;
2030 32 : papszOptions = CSLSetNameValue(papszOptions, "HEADERS", osHeaders);
2031 :
2032 : // Build request JSon document
2033 64 : CPLJSONDocument oDoc;
2034 64 : CPLJSONObject oBBox;
2035 :
2036 32 : if (poGDS->m_bRequestInGeoreferencedCoordinates)
2037 : {
2038 0 : CPLJSONObject oSRS;
2039 0 : oSRS.Add("type", poGDS->m_osSRSType);
2040 0 : oSRS.Add("value", poGDS->m_osSRSValue);
2041 0 : oBBox.Add("srs", oSRS);
2042 : }
2043 : else
2044 : {
2045 32 : CPLJSONObject oSRS;
2046 32 : oSRS.Add("type", "image");
2047 32 : oBBox.Add("srs", oSRS);
2048 : }
2049 :
2050 32 : const int nMainXSize = poGDS->m_poParentDS
2051 32 : ? poGDS->m_poParentDS->GetRasterXSize()
2052 32 : : nRasterXSize;
2053 32 : const int nMainYSize = poGDS->m_poParentDS
2054 32 : ? poGDS->m_poParentDS->GetRasterYSize()
2055 32 : : nRasterYSize;
2056 32 : const int nULX = nBlockXOff * nBlockXSize;
2057 32 : const int nULY = nBlockYOff * nBlockYSize;
2058 : const int nLRX =
2059 32 : std::min(nRasterXSize, (nBlockXOff + nXBlocks) * nBlockXSize);
2060 : const int nLRY =
2061 32 : std::min(nRasterYSize, (nBlockYOff + nYBlocks) * nBlockYSize);
2062 :
2063 64 : CPLJSONObject oUL;
2064 64 : CPLJSONObject oLR;
2065 32 : if (poGDS->m_bRequestInGeoreferencedCoordinates)
2066 : {
2067 : double dfULX, dfULY;
2068 0 : GDALApplyGeoTransform(poGDS->m_adfGeoTransform.data(), nULX, nULY,
2069 : &dfULX, &dfULY);
2070 0 : oUL.Add("x", dfULX);
2071 0 : oUL.Add("y", dfULY);
2072 :
2073 : double dfLRX, dfLRY;
2074 0 : GDALApplyGeoTransform(poGDS->m_adfGeoTransform.data(), nLRX, nLRY,
2075 : &dfLRX, &dfLRY);
2076 0 : oLR.Add("x", dfLRX);
2077 0 : oLR.Add("y", dfLRY);
2078 : }
2079 : else
2080 : {
2081 32 : oUL.Add("x",
2082 32 : static_cast<int>((static_cast<GIntBig>(nULX) * nMainXSize) /
2083 32 : nRasterXSize));
2084 32 : oUL.Add("y",
2085 32 : static_cast<int>((static_cast<GIntBig>(nULY) * nMainYSize) /
2086 32 : nRasterYSize));
2087 :
2088 33 : oLR.Add("x", (nLRX == nRasterXSize)
2089 : ? nMainXSize
2090 : : static_cast<int>(
2091 1 : (static_cast<GIntBig>(nLRX) * nMainXSize) /
2092 1 : nRasterXSize));
2093 32 : oLR.Add("y", (nLRY == nRasterYSize)
2094 : ? nMainYSize
2095 : : static_cast<int>(
2096 0 : (static_cast<GIntBig>(nLRY) * nMainYSize) /
2097 0 : nRasterYSize));
2098 : }
2099 32 : oBBox.Add("ul", oUL);
2100 32 : oBBox.Add("lr", oLR);
2101 32 : oDoc.GetRoot().Add("bbox", oBBox);
2102 :
2103 64 : CPLJSONObject oTargetModel;
2104 :
2105 64 : CPLJSONObject oStepTargetModel;
2106 32 : if (poGDS->m_bRequestInGeoreferencedCoordinates)
2107 : {
2108 0 : oStepTargetModel.Add("x", poGDS->m_adfGeoTransform[1]);
2109 0 : oStepTargetModel.Add("y", fabs(poGDS->m_adfGeoTransform[5]));
2110 : }
2111 : else
2112 : {
2113 32 : oStepTargetModel.Add("x", 0);
2114 32 : oStepTargetModel.Add("y", 0);
2115 : }
2116 32 : oTargetModel.Add("step", oStepTargetModel);
2117 :
2118 64 : CPLJSONObject oSize;
2119 32 : int nRequestWidth = nLRX - nULX;
2120 32 : int nRequestHeight = nLRY - nULY;
2121 32 : oSize.Add("columns", nRequestWidth);
2122 32 : oSize.Add("lines", nRequestHeight);
2123 32 : oTargetModel.Add("size", oSize);
2124 :
2125 32 : if (poGDS->m_eCurrentResampleAlg == GRIORA_NearestNeighbour)
2126 : {
2127 32 : oTargetModel.Add("sampling-algo", "NEAREST");
2128 : }
2129 0 : else if (poGDS->m_eCurrentResampleAlg == GRIORA_Bilinear)
2130 : {
2131 0 : oTargetModel.Add("sampling-algo", "BILINEAR");
2132 : }
2133 0 : else if (poGDS->m_eCurrentResampleAlg == GRIORA_Cubic)
2134 : {
2135 0 : oTargetModel.Add("sampling-algo", "BICUBIC");
2136 : }
2137 0 : else if (poGDS->m_eCurrentResampleAlg == GRIORA_Average)
2138 : {
2139 0 : oTargetModel.Add("sampling-algo", "AVERAGE");
2140 : }
2141 : else
2142 : {
2143 : // Defaults to BILINEAR for other GDAL methods not supported by
2144 : // server
2145 0 : oTargetModel.Add("sampling-algo", "BILINEAR");
2146 : }
2147 :
2148 32 : oTargetModel.Add("strictOutputSize", true);
2149 :
2150 32 : if (!poGDS->m_bRequestInGeoreferencedCoordinates)
2151 : {
2152 32 : CPLJSONObject oSRS;
2153 32 : oSRS.Add("type", "image");
2154 32 : oTargetModel.Add("srs", oSRS);
2155 : }
2156 :
2157 32 : oDoc.GetRoot().Add("target-model", oTargetModel);
2158 :
2159 64 : CPLJSONArray oBands;
2160 32 : bool bOK = true;
2161 90 : for (const int iBand : anRequestedBands)
2162 : {
2163 : auto desc = (iBand == MAIN_MASK_BAND_NUMBER)
2164 58 : ? poGDS->m_poMaskBand->GetDescription()
2165 56 : : poGDS->GetRasterBand(iBand)->GetDescription();
2166 58 : if (EQUAL(desc, ""))
2167 0 : bOK = false;
2168 : else
2169 58 : oBands.Add(desc);
2170 : }
2171 32 : if (bOK)
2172 : {
2173 32 : oDoc.GetRoot().Add("bands", oBands);
2174 : }
2175 :
2176 32 : papszOptions = CSLSetNameValue(
2177 : papszOptions, "POSTFIELDS",
2178 64 : oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty).c_str());
2179 :
2180 : CPLString osURL(CPLGetConfigOption("GDAL_DAAS_GET_BUFFER_URL",
2181 64 : poGDS->m_osGetBufferURL.c_str()));
2182 32 : CPLHTTPResult *psResult = DAAS_CPLHTTPFetch(osURL, papszOptions);
2183 32 : CSLDestroy(papszOptions);
2184 32 : if (psResult == nullptr)
2185 0 : return CE_Failure;
2186 :
2187 32 : if (psResult->pszErrBuf != nullptr)
2188 : {
2189 11 : CPLError(CE_Failure, CPLE_AppDefined, "Get request %s failed: %s",
2190 : osURL.c_str(),
2191 12 : psResult->pabyData ? CPLSPrintf("%s: %s", psResult->pszErrBuf,
2192 : reinterpret_cast<const char *>(
2193 1 : psResult->pabyData))
2194 : : psResult->pszErrBuf);
2195 11 : CPLHTTPDestroyResult(psResult);
2196 11 : return CE_Failure;
2197 : }
2198 :
2199 21 : if (psResult->nDataLen == 0)
2200 : {
2201 : // Presumably HTTP 204 empty
2202 0 : CPLHTTPDestroyResult(psResult);
2203 :
2204 0 : for (int iYBlock = 0; iYBlock < nYBlocks; iYBlock++)
2205 : {
2206 0 : for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
2207 : {
2208 0 : for (const int iBand : anRequestedBands)
2209 : {
2210 0 : GByte *pabyDstBuffer = nullptr;
2211 : GDALDAASRasterBand *poIterBand;
2212 0 : if (iBand == MAIN_MASK_BAND_NUMBER)
2213 : {
2214 0 : poIterBand = poGDS->m_poMaskBand;
2215 : }
2216 : else
2217 : {
2218 : poIterBand = reinterpret_cast<GDALDAASRasterBand *>(
2219 0 : poGDS->GetRasterBand(iBand));
2220 : }
2221 :
2222 0 : GDALRasterBlock *poBlock = nullptr;
2223 0 : if (pDstBuffer != nullptr)
2224 : {
2225 0 : pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
2226 : }
2227 : else
2228 : {
2229 : // Check if the same block in other bands is already in
2230 : // the GDAL block cache
2231 0 : poBlock = poIterBand->TryGetLockedBlockRef(
2232 0 : nBlockXOff + iXBlock, nBlockYOff + iYBlock);
2233 0 : if (poBlock != nullptr)
2234 : {
2235 : // Yes, no need to do further work
2236 0 : poBlock->DropLock();
2237 0 : continue;
2238 : }
2239 : // Instantiate the block
2240 0 : poBlock = poIterBand->GetLockedBlockRef(
2241 0 : nBlockXOff + iXBlock, nBlockYOff + iYBlock, TRUE);
2242 0 : if (poBlock == nullptr)
2243 : {
2244 0 : continue;
2245 : }
2246 : pabyDstBuffer =
2247 0 : static_cast<GByte *>(poBlock->GetDataRef());
2248 : }
2249 :
2250 0 : const int nDTSize = GDALGetDataTypeSizeBytes(
2251 : poIterBand->GetRasterDataType());
2252 0 : double dfNoDataValue = poIterBand->GetNoDataValue(nullptr);
2253 0 : GDALCopyWords(&dfNoDataValue, GDT_Float64, 0, pabyDstBuffer,
2254 : poIterBand->GetRasterDataType(), nDTSize,
2255 0 : nBlockXSize * nBlockYSize);
2256 0 : if (poBlock)
2257 0 : poBlock->DropLock();
2258 : }
2259 : }
2260 : }
2261 :
2262 0 : return CE_None;
2263 : }
2264 :
2265 : #ifdef DEBUG_VERBOSE
2266 : CPLDebug("DAAS", "Response = '%s'",
2267 : reinterpret_cast<const char *>(psResult->pabyData));
2268 : #endif
2269 21 : if (!CPLHTTPParseMultipartMime(psResult))
2270 : {
2271 1 : CPLError(CE_Failure, CPLE_AppDefined,
2272 : "Get request %s failed: "
2273 : "Invalid content returned by server",
2274 : osURL.c_str());
2275 1 : CPLHTTPDestroyResult(psResult);
2276 1 : return CE_Failure;
2277 : }
2278 20 : int iMetadataPart = -1;
2279 20 : int iDataPart = -1;
2280 : // Identify metadata and data parts
2281 58 : for (int i = 0; i < psResult->nMimePartCount; i++)
2282 : {
2283 76 : const char *pszContentType = CSLFetchNameValue(
2284 38 : psResult->pasMimePart[i].papszHeaders, "Content-Type");
2285 76 : const char *pszContentDisposition = CSLFetchNameValue(
2286 38 : psResult->pasMimePart[i].papszHeaders, "Content-Disposition");
2287 38 : if (pszContentType)
2288 : {
2289 38 : if (EQUAL(pszContentType, "application/json"))
2290 : {
2291 19 : iMetadataPart = i;
2292 : }
2293 19 : else if (EQUAL(pszContentType, osDataContentType))
2294 : {
2295 19 : iDataPart = i;
2296 : }
2297 : }
2298 38 : if (pszContentDisposition)
2299 : {
2300 38 : if (EQUAL(pszContentDisposition, "form-data; name=\"Data\";"))
2301 : {
2302 19 : iDataPart = i;
2303 : }
2304 : }
2305 : }
2306 20 : if (iDataPart < 0)
2307 : {
2308 1 : CPLError(CE_Failure, CPLE_AppDefined,
2309 : "Cannot find part with Content-Type: %s in GetBuffer response",
2310 : osDataContentType.c_str());
2311 1 : CPLHTTPDestroyResult(psResult);
2312 1 : return CE_Failure;
2313 : }
2314 19 : if (iMetadataPart < 0)
2315 : {
2316 1 : CPLError(CE_Failure, CPLE_AppDefined,
2317 : "Cannot find part with Content-Type: %s in GetBuffer response",
2318 : "application/json");
2319 1 : CPLHTTPDestroyResult(psResult);
2320 1 : return CE_Failure;
2321 : }
2322 :
2323 36 : CPLString osJson;
2324 : osJson.assign(reinterpret_cast<const char *>(
2325 18 : psResult->pasMimePart[iMetadataPart].pabyData),
2326 18 : psResult->pasMimePart[iMetadataPart].nDataLen);
2327 18 : CPLDebug("DAAS", "GetBuffer metadata response: %s", osJson.c_str());
2328 18 : if (!oDoc.LoadMemory(osJson))
2329 : {
2330 0 : CPLHTTPDestroyResult(psResult);
2331 0 : return CE_Failure;
2332 : }
2333 36 : auto oDocRoot = oDoc.GetRoot();
2334 18 : int nGotHeight = oDocRoot.GetInteger("properties/height");
2335 18 : int nGotWidth = oDocRoot.GetInteger("properties/width");
2336 18 : if (nGotHeight != nRequestHeight || nGotWidth != nRequestWidth)
2337 : {
2338 1 : CPLError(CE_Failure, CPLE_AppDefined,
2339 : "Got buffer of size %dx%d, whereas %dx%d was expected",
2340 : nGotWidth, nGotHeight, nRequestWidth, nRequestHeight);
2341 1 : CPLHTTPDestroyResult(psResult);
2342 1 : return CE_Failure;
2343 : }
2344 :
2345 : // Get the actual data type of the buffer response
2346 : GDALDataType eBufferDataType =
2347 17 : anRequestedBands[0] == MAIN_MASK_BAND_NUMBER
2348 32 : ? GDT_Byte
2349 15 : : poGDS->m_aoBandDesc[anRequestedBands[0] - 1].eDT;
2350 51 : auto oBandArray = oDocRoot.GetArray("properties/bands");
2351 17 : if (oBandArray.IsValid() && oBandArray.Size() >= 1)
2352 : {
2353 : bool bIgnored;
2354 4 : auto oBandProperties = oBandArray[0];
2355 : auto osPixelType =
2356 4 : GetString(oBandProperties, "pixelType", false, bIgnored);
2357 4 : if (!osPixelType.empty())
2358 : {
2359 4 : eBufferDataType = GetGDALDataTypeFromDAASPixelType(osPixelType);
2360 4 : if (eBufferDataType == GDT_Unknown)
2361 : {
2362 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid pixelType: %s",
2363 : osPixelType.c_str());
2364 0 : CPLHTTPDestroyResult(psResult);
2365 0 : return CE_Failure;
2366 : }
2367 : }
2368 : }
2369 :
2370 17 : const int nBufferDTSize = GDALGetDataTypeSizeBytes(eBufferDataType);
2371 17 : std::shared_ptr<GDALDataset> poTileDS;
2372 17 : if (eRequestFormat == GDALDAASDataset::Format::RAW)
2373 : {
2374 22 : int nExpectedBytes = nGotHeight * nGotWidth * nBufferDTSize *
2375 11 : static_cast<int>(anRequestedBands.size());
2376 11 : if (psResult->pasMimePart[iDataPart].nDataLen != nExpectedBytes)
2377 : {
2378 1 : CPLError(CE_Failure, CPLE_AppDefined,
2379 : "Got buffer of %d bytes, whereas %d were expected",
2380 1 : psResult->pasMimePart[iDataPart].nDataLen, nExpectedBytes);
2381 1 : CPLHTTPDestroyResult(psResult);
2382 1 : return CE_Failure;
2383 : }
2384 :
2385 10 : GByte *pabySrcData = psResult->pasMimePart[iDataPart].pabyData;
2386 : #ifdef CPL_MSB
2387 : GDALSwapWords(pabySrcData, nBufferDTSize,
2388 : nGotHeight * nGotWidth *
2389 : static_cast<int>(anRequestedBands.size()),
2390 : nBufferDTSize);
2391 : #endif
2392 :
2393 10 : auto poMEMDS = MEMDataset::Create("", nRequestWidth, nRequestHeight, 0,
2394 : eBufferDataType, nullptr);
2395 10 : poTileDS.reset(poMEMDS);
2396 22 : for (int i = 0; i < static_cast<int>(anRequestedBands.size()); i++)
2397 : {
2398 24 : auto hBand = MEMCreateRasterBandEx(
2399 : poMEMDS, i + 1,
2400 12 : pabySrcData + i * nGotHeight * nGotWidth * nBufferDTSize,
2401 : eBufferDataType, 0, 0, false);
2402 12 : poMEMDS->AddMEMBand(hBand);
2403 : }
2404 : }
2405 : else
2406 : {
2407 6 : const CPLString osTmpMemFile = VSIMemGenerateHiddenFilename("daas");
2408 6 : VSIFCloseL(VSIFileFromMemBuffer(
2409 6 : osTmpMemFile, psResult->pasMimePart[iDataPart].pabyData,
2410 6 : psResult->pasMimePart[iDataPart].nDataLen, false));
2411 6 : poTileDS.reset(GDALDataset::Open(osTmpMemFile,
2412 : GDAL_OF_RASTER | GDAL_OF_INTERNAL,
2413 : nullptr, nullptr, nullptr));
2414 6 : if (!poTileDS)
2415 : {
2416 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot decode image");
2417 1 : VSIUnlink(osTmpMemFile);
2418 1 : CPLHTTPDestroyResult(psResult);
2419 1 : return CE_Failure;
2420 : }
2421 : }
2422 :
2423 15 : CPLErr eErr = CE_None;
2424 15 : poTileDS->MarkSuppressOnClose();
2425 :
2426 : bool bExpectedImageCharacteristics =
2427 29 : (poTileDS->GetRasterXSize() == nRequestWidth &&
2428 14 : poTileDS->GetRasterYSize() == nRequestHeight);
2429 15 : if (bExpectedImageCharacteristics)
2430 : {
2431 28 : if (poTileDS->GetRasterCount() ==
2432 14 : static_cast<int>(anRequestedBands.size()))
2433 : {
2434 : // ok
2435 : }
2436 1 : else if (eRequestFormat == GDALDAASDataset::Format::PNG &&
2437 2 : anRequestedBands.size() == 1 &&
2438 1 : poTileDS->GetRasterCount() == 4)
2439 : {
2440 : // ok
2441 : }
2442 : else
2443 : {
2444 0 : bExpectedImageCharacteristics = false;
2445 : }
2446 : }
2447 :
2448 15 : if (!bExpectedImageCharacteristics)
2449 : {
2450 1 : CPLError(CE_Failure, CPLE_AppDefined,
2451 : "Got tile of size %dx%dx%d, whereas %dx%dx%d was expected",
2452 : poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
2453 : poTileDS->GetRasterCount(), nRequestWidth, nRequestHeight,
2454 1 : static_cast<int>(anRequestedBands.size()));
2455 1 : CPLHTTPDestroyResult(psResult);
2456 1 : return CE_Failure;
2457 : }
2458 :
2459 28 : for (int iYBlock = 0; eErr == CE_None && iYBlock < nYBlocks; iYBlock++)
2460 : {
2461 : int nBlockActualYSize = std::min(
2462 14 : nBlockYSize, nRasterYSize - (iYBlock + nBlockYOff) * nBlockYSize);
2463 30 : for (int iXBlock = 0; eErr == CE_None && iXBlock < nXBlocks; iXBlock++)
2464 : {
2465 : int nBlockActualXSize =
2466 16 : std::min(nBlockXSize,
2467 16 : nRasterXSize - (iXBlock + nBlockXOff) * nBlockXSize);
2468 :
2469 40 : for (int i = 0; i < static_cast<int>(anRequestedBands.size()); i++)
2470 : {
2471 24 : const int iBand = anRequestedBands[i];
2472 24 : GByte *pabyDstBuffer = nullptr;
2473 : GDALDAASRasterBand *poIterBand;
2474 24 : if (iBand == MAIN_MASK_BAND_NUMBER)
2475 : {
2476 2 : poIterBand = poGDS->m_poMaskBand;
2477 : }
2478 : else
2479 : {
2480 : poIterBand = reinterpret_cast<GDALDAASRasterBand *>(
2481 22 : poGDS->GetRasterBand(iBand));
2482 : }
2483 :
2484 24 : GDALRasterBlock *poBlock = nullptr;
2485 24 : if (pDstBuffer != nullptr)
2486 2 : pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
2487 : else
2488 : {
2489 : // Check if the same block in other bands is already in
2490 : // the GDAL block cache
2491 44 : poBlock = poIterBand->TryGetLockedBlockRef(
2492 22 : nBlockXOff + iXBlock, nBlockYOff + iYBlock);
2493 22 : if (poBlock != nullptr)
2494 : {
2495 : // Yes, no need to do further work
2496 1 : poBlock->DropLock();
2497 1 : continue;
2498 : }
2499 : // Instantiate the block
2500 42 : poBlock = poIterBand->GetLockedBlockRef(
2501 21 : nBlockXOff + iXBlock, nBlockYOff + iYBlock, TRUE);
2502 21 : if (poBlock == nullptr)
2503 : {
2504 0 : continue;
2505 : }
2506 21 : pabyDstBuffer = static_cast<GByte *>(poBlock->GetDataRef());
2507 : }
2508 :
2509 23 : GDALRasterBand *poTileBand = poTileDS->GetRasterBand(i + 1);
2510 23 : const auto eIterBandDT = poIterBand->GetRasterDataType();
2511 23 : const int nDTSize = GDALGetDataTypeSizeBytes(eIterBandDT);
2512 46 : eErr = poTileBand->RasterIO(
2513 23 : GF_Read, iXBlock * nBlockXSize, iYBlock * nBlockYSize,
2514 : nBlockActualXSize, nBlockActualYSize, pabyDstBuffer,
2515 : nBlockActualXSize, nBlockActualYSize, eIterBandDT, nDTSize,
2516 23 : static_cast<GSpacing>(nDTSize) * nBlockXSize, nullptr);
2517 :
2518 23 : if (poBlock)
2519 21 : poBlock->DropLock();
2520 23 : if (eErr != CE_None)
2521 0 : break;
2522 : }
2523 : }
2524 : }
2525 :
2526 14 : CPLHTTPDestroyResult(psResult);
2527 14 : return eErr;
2528 : }
2529 :
2530 : /************************************************************************/
2531 : /* GDALRegister_DAAS() */
2532 : /************************************************************************/
2533 :
2534 1595 : void GDALRegister_DAAS()
2535 :
2536 : {
2537 1595 : if (GDALGetDriverByName("DAAS") != nullptr)
2538 302 : return;
2539 :
2540 1293 : GDALDriver *poDriver = new GDALDriver();
2541 :
2542 1293 : poDriver->SetDescription("DAAS");
2543 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
2544 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Airbus DS Intelligence "
2545 1293 : "Data As A Service driver");
2546 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/daas.html");
2547 :
2548 1293 : poDriver->SetMetadataItem(
2549 : GDAL_DMD_OPENOPTIONLIST,
2550 : "<OpenOptionList>"
2551 : " <Option name='GET_METADATA_URL' type='string' "
2552 : "description='URL to GetImageMetadata' "
2553 : "required='true'/>"
2554 : " <Option name='API_KEY' alt_config_option='GDAL_DAAS_API_KEY' "
2555 : "type='string' "
2556 : "description='API key'/>"
2557 : " <Option name='CLIENT_ID' alt_config_option='GDAL_DAAS_CLIENT_ID' "
2558 : "type='string' description='Client id'/>"
2559 : " <Option name='ACCESS_TOKEN' "
2560 : "alt_config_option='GDAL_DAAS_ACCESS_TOKEN' "
2561 : "type='string' description='Authorization access token'/>"
2562 : " <Option name='X_FORWARDED_USER' "
2563 : "alt_config_option='GDAL_DAAS_X_FORWARDED_USER' type='string' "
2564 : "description='User from which the request originates from'/>"
2565 : " <Option name='BLOCK_SIZE' type='integer' "
2566 : "description='Size of a block' default='512'/>"
2567 : " <Option name='PIXEL_ENCODING' type='string-select' "
2568 : "description='Format in which pixels are queried'>"
2569 : " <Value>AUTO</Value>"
2570 : " <Value>RAW</Value>"
2571 : " <Value>PNG</Value>"
2572 : " <Value>JPEG</Value>"
2573 : " <Value>JPEG2000</Value>"
2574 : " </Option>"
2575 : " <Option name='TARGET_SRS' type='string' description="
2576 : "'SRS name for server-side reprojection.'/>"
2577 : " <Option name='MASKS' type='boolean' "
2578 : "description='Whether to expose mask bands' default='YES'/>"
2579 1293 : "</OpenOptionList>");
2580 :
2581 1293 : poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "DAAS:");
2582 :
2583 1293 : poDriver->pfnIdentify = GDALDAASDataset::Identify;
2584 1293 : poDriver->pfnOpen = GDALDAASDataset::OpenStatic;
2585 :
2586 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
2587 : }
|