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