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