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