Coverage Report

Created: 2026-03-30 09:00

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