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