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