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