• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15797738239

21 Jun 2025 04:51PM UTC coverage: 71.076% (+0.001%) from 71.075%
15797738239

Pull #12622

github

web-flow
Merge 19559fd15 into 645a00347
Pull Request #12622: VSI Win32: implement OpenDir()

574181 of 807840 relevant lines covered (71.08%)

249831.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.44
/frmts/rs2/rs2dataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Polarimetric Workstation
4
 * Purpose:  Radarsat 2 - XML Products (product.xml) driver
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10
 * Copyright (c) 2020, Defence Research and Development Canada (DRDC) Ottawa Research Centre
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14

15
#include "cpl_minixml.h"
16
#include "gdal_frmts.h"
17
#include "gdal_pam.h"
18
#include "ogr_spatialref.h"
19

20
typedef enum eCalibration_t
21
{
22
    Sigma0 = 0,
23
    Gamma,
24
    Beta0,
25
    Uncalib,
26
    None
27
} eCalibration;
28

29
/*** Function to test for valid LUT files ***/
30
static bool IsValidXMLFile(const char *pszPath, const char *pszLut)
15✔
31
{
32
    /* Return true for valid xml file, false otherwise */
33
    char *pszLutFile =
34
        VSIStrdup(CPLFormFilenameSafe(pszPath, pszLut, nullptr).c_str());
15✔
35

36
    CPLXMLTreeCloser psLut(CPLParseXMLFile(pszLutFile));
15✔
37

38
    CPLFree(pszLutFile);
15✔
39

40
    return psLut.get() != nullptr;
30✔
41
}
42

43
// Check that the referenced dataset for each band has the
44
// correct data type and returns whether a 2 band I+Q dataset should
45
// be mapped onto a single complex band.
46
// Returns BANDERROR for error, STRAIGHT for 1:1 mapping, TWOBANDCOMPLEX for 2
47
// bands -> 1 complex band
48
typedef enum
49
{
50
    BANDERROR,
51
    STRAIGHT,
52
    TWOBANDCOMPLEX
53
} BandMapping;
54

55
static BandMapping GetBandFileMapping(GDALDataType eDataType,
10✔
56
                                      GDALDataset *poBandFile)
57
{
58

59
    GDALRasterBand *poBand1 = poBandFile->GetRasterBand(1);
10✔
60
    GDALDataType eBandDataType1 = poBand1->GetRasterDataType();
10✔
61

62
    // if there is one band and it has the same datatype, the band file gets
63
    // passed straight through
64
    if (poBandFile->GetRasterCount() == 1 && eDataType == eBandDataType1)
10✔
65
        return STRAIGHT;
10✔
66

67
    // if the band file has 2 bands, they should represent I+Q
68
    // and be a compatible data type
69
    if (poBandFile->GetRasterCount() == 2 && GDALDataTypeIsComplex(eDataType))
×
70
    {
71
        GDALRasterBand *band2 = poBandFile->GetRasterBand(2);
×
72

73
        if (eBandDataType1 != band2->GetRasterDataType())
×
74
            return BANDERROR;  // both bands must be same datatype
×
75

76
        // check compatible types - there are 4 complex types in GDAL
77
        if ((eDataType == GDT_CInt16 && eBandDataType1 == GDT_Int16) ||
×
78
            (eDataType == GDT_CInt32 && eBandDataType1 == GDT_Int32) ||
×
79
            (eDataType == GDT_CFloat32 && eBandDataType1 == GDT_Float32) ||
×
80
            (eDataType == GDT_CFloat64 && eBandDataType1 == GDT_Float64))
×
81
            return TWOBANDCOMPLEX;
×
82
    }
83
    return BANDERROR;  // don't accept any other combinations
×
84
}
85

86
/************************************************************************/
87
/* ==================================================================== */
88
/*                               RS2Dataset                             */
89
/* ==================================================================== */
90
/************************************************************************/
91

92
class RS2Dataset final : public GDALPamDataset
93
{
94
    CPLXMLNode *psProduct;
95

96
    int nGCPCount;
97
    GDAL_GCP *pasGCPList;
98
    OGRSpatialReference m_oSRS{};
99
    OGRSpatialReference m_oGCPSRS{};
100
    char **papszSubDatasets;
101
    double adfGeoTransform[6];
102
    bool bHaveGeoTransform;
103

104
    char **papszExtraFiles;
105

106
    CPL_DISALLOW_COPY_ASSIGN(RS2Dataset)
107

108
  protected:
109
    virtual int CloseDependentDatasets() override;
110

111
  public:
112
    RS2Dataset();
113
    virtual ~RS2Dataset();
114

115
    virtual int GetGCPCount() override;
116
    const OGRSpatialReference *GetGCPSpatialRef() const override;
117
    virtual const GDAL_GCP *GetGCPs() override;
118

119
    const OGRSpatialReference *GetSpatialRef() const override;
120
    virtual CPLErr GetGeoTransform(double *) override;
121

122
    virtual char **GetMetadataDomainList() override;
123
    virtual char **GetMetadata(const char *pszDomain = "") override;
124
    virtual char **GetFileList(void) override;
125

126
    static GDALDataset *Open(GDALOpenInfo *);
127
    static int Identify(GDALOpenInfo *);
128

129
    CPLXMLNode *GetProduct()
130
    {
131
        return psProduct;
132
    }
133
};
134

135
/************************************************************************/
136
/* ==================================================================== */
137
/*                    RS2RasterBand                           */
138
/* ==================================================================== */
139
/************************************************************************/
140

141
class RS2RasterBand final : public GDALPamRasterBand
142
{
143
    GDALDataset *poBandFile = nullptr;
144

145
    // 2 bands representing I+Q -> one complex band
146
    // otherwise poBandFile is passed straight through
147
    bool bIsTwoBandComplex = false;
148

149
    CPL_DISALLOW_COPY_ASSIGN(RS2RasterBand)
150

151
  public:
152
    RS2RasterBand(RS2Dataset *poDSIn, GDALDataType eDataTypeIn,
153
                  const char *pszPole, GDALDataset *poBandFile,
154
                  bool bTwoBandComplex = false);
155
    virtual ~RS2RasterBand();
156

157
    virtual CPLErr IReadBlock(int, int, void *) override;
158

159
    static GDALDataset *Open(GDALOpenInfo *);
160
};
161

162
/************************************************************************/
163
/*                            RS2RasterBand                             */
164
/************************************************************************/
165

166
RS2RasterBand::RS2RasterBand(RS2Dataset *poDSIn, GDALDataType eDataTypeIn,
8✔
167
                             const char *pszPole, GDALDataset *poBandFileIn,
168
                             bool bTwoBandComplex)
8✔
169
    : poBandFile(poBandFileIn)
8✔
170
{
171
    poDS = poDSIn;
8✔
172

173
    GDALRasterBand *poSrcBand = poBandFile->GetRasterBand(1);
8✔
174

175
    poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
8✔
176

177
    eDataType = eDataTypeIn;
8✔
178

179
    bIsTwoBandComplex = bTwoBandComplex;
8✔
180

181
    if (*pszPole != '\0')
8✔
182
        SetMetadataItem("POLARIMETRIC_INTERP", pszPole);
8✔
183
}
8✔
184

185
/************************************************************************/
186
/*                            RSRasterBand()                            */
187
/************************************************************************/
188

189
RS2RasterBand::~RS2RasterBand()
16✔
190

191
{
192
    if (poBandFile != nullptr)
8✔
193
        GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
8✔
194
}
16✔
195

196
/************************************************************************/
197
/*                             IReadBlock()                             */
198
/************************************************************************/
199

200
CPLErr RS2RasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
40✔
201

202
{
203
    /* -------------------------------------------------------------------- */
204
    /*      If the last strip is partial, we need to avoid                  */
205
    /*      over-requesting.  We also need to initialize the extra part     */
206
    /*      of the block to zero.                                           */
207
    /* -------------------------------------------------------------------- */
208
    int nRequestYSize;
209
    if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
40✔
210
    {
211
        nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
×
212
        memset(pImage, 0,
×
213
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
×
214
                   nBlockXSize * nBlockYSize);
×
215
    }
216
    else
217
    {
218
        nRequestYSize = nBlockYSize;
40✔
219
    }
220

221
    /*-------------------------------------------------------------------- */
222
    /*      If the input imagery is tiled, also need to avoid over-        */
223
    /*      requesting in the X-direction.                                 */
224
    /* ------------------------------------------------------------------- */
225
    int nRequestXSize;
226
    if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize)
40✔
227
    {
228
        nRequestXSize = nRasterXSize - nBlockXOff * nBlockXSize;
×
229
        memset(pImage, 0,
×
230
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
×
231
                   nBlockXSize * nBlockYSize);
×
232
    }
233
    else
234
    {
235
        nRequestXSize = nBlockXSize;
40✔
236
    }
237
    if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 2)
40✔
238
        return poBandFile->RasterIO(
×
239
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
240
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
241
            GDT_Int16, 2, nullptr, 4, nBlockXSize * 4, 2, nullptr);
×
242

243
    /* -------------------------------------------------------------------- */
244
    /*      File has one sample marked as sample format void, a 32bits.     */
245
    /* -------------------------------------------------------------------- */
246
    else if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 1)
40✔
247
    {
248
        CPLErr eErr = poBandFile->RasterIO(
×
249
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
250
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
251
            GDT_UInt32, 1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
×
252

253
#ifdef CPL_LSB
254
        /* First, undo the 32bit swap. */
255
        GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
×
256

257
        /* Then apply 16 bit swap. */
258
        GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
×
259
#endif
260

261
        return eErr;
×
262
    }
263

264
    /* -------------------------------------------------------------------- */
265
    /*      The 16bit case is straight forward.  The underlying file        */
266
    /*      looks like a 16bit unsigned data too.                           */
267
    /* -------------------------------------------------------------------- */
268
    else if (eDataType == GDT_UInt16)
40✔
269
        return poBandFile->RasterIO(
×
270
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
271
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
272
            GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, nullptr);
×
273
    else if (eDataType == GDT_Byte)
40✔
274
        /* Ticket #2104: Support for ScanSAR products */
275
        return poBandFile->RasterIO(
80✔
276
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
40✔
277
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
278
            GDT_Byte, 1, nullptr, 1, nBlockXSize, 0, nullptr);
40✔
279

280
    CPLAssert(false);
×
281
    return CE_Failure;
282
}
283

284
/************************************************************************/
285
/* ==================================================================== */
286
/*                         RS2CalibRasterBand                           */
287
/* ==================================================================== */
288
/************************************************************************/
289
/* Returns data that has been calibrated to sigma nought, gamma         */
290
/* or beta nought.                                                      */
291
/************************************************************************/
292

293
class RS2CalibRasterBand final : public GDALPamRasterBand
294
{
295
  private:
296
    // eCalibration m_eCalib;
297
    GDALDataset *m_poBandDataset;
298
    GDALDataType m_eType; /* data type of data being ingested */
299
    float *m_nfTable;
300
    int m_nTableSize;
301
    float m_nfOffset;
302
    char *m_pszLUTFile;
303

304
    CPL_DISALLOW_COPY_ASSIGN(RS2CalibRasterBand)
305

306
    void ReadLUT();
307

308
  public:
309
    RS2CalibRasterBand(RS2Dataset *poDataset, const char *pszPolarization,
310
                       GDALDataType eType, GDALDataset *poBandDataset,
311
                       eCalibration eCalib, const char *pszLUT);
312
    ~RS2CalibRasterBand();
313

314
    CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
315
};
316

317
/************************************************************************/
318
/*                            ReadLUT()                                 */
319
/************************************************************************/
320
/* Read the provided LUT in to m_ndTable                                */
321
/************************************************************************/
322
void RS2CalibRasterBand::ReadLUT()
2✔
323
{
324
    CPLXMLNode *psLUT = CPLParseXMLFile(m_pszLUTFile);
2✔
325

326
    this->m_nfOffset = static_cast<float>(
2✔
327
        CPLAtof(CPLGetXMLValue(psLUT, "=lut.offset", "0.0")));
2✔
328

329
    char **papszLUTList = CSLTokenizeString2(
2✔
330
        CPLGetXMLValue(psLUT, "=lut.gains", ""), " ", CSLT_HONOURSTRINGS);
331

332
    m_nTableSize = CSLCount(papszLUTList);
2✔
333

334
    m_nfTable =
2✔
335
        reinterpret_cast<float *>(CPLMalloc(sizeof(float) * m_nTableSize));
2✔
336

337
    for (int i = 0; i < m_nTableSize; i++)
514✔
338
    {
339
        m_nfTable[i] = static_cast<float>(CPLAtof(papszLUTList[i]));
512✔
340
    }
341

342
    CPLDestroyXMLNode(psLUT);
2✔
343

344
    CSLDestroy(papszLUTList);
2✔
345
}
2✔
346

347
/************************************************************************/
348
/*                        RS2CalibRasterBand()                          */
349
/************************************************************************/
350

351
RS2CalibRasterBand::RS2CalibRasterBand(
2✔
352
    RS2Dataset *poDataset, const char *pszPolarization, GDALDataType eType,
353
    GDALDataset *poBandDataset, eCalibration /* eCalib */, const char *pszLUT)
2✔
354
    :  // m_eCalib(eCalib),
355
      m_poBandDataset(poBandDataset), m_eType(eType), m_nfTable(nullptr),
356
      m_nTableSize(0), m_nfOffset(0), m_pszLUTFile(VSIStrdup(pszLUT))
2✔
357
{
358
    poDS = poDataset;
2✔
359

360
    if (*pszPolarization != '\0')
2✔
361
    {
362
        SetMetadataItem("POLARIMETRIC_INTERP", pszPolarization);
2✔
363
    }
364

365
    if (eType == GDT_CInt16)
2✔
366
        eDataType = GDT_CFloat32;
×
367
    else
368
        eDataType = GDT_Float32;
2✔
369

370
    GDALRasterBand *poRasterBand = poBandDataset->GetRasterBand(1);
2✔
371
    poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
2✔
372

373
    ReadLUT();
2✔
374
}
2✔
375

376
/************************************************************************/
377
/*                       ~RS2CalibRasterBand()                          */
378
/************************************************************************/
379

380
RS2CalibRasterBand::~RS2CalibRasterBand()
4✔
381
{
382
    CPLFree(m_nfTable);
2✔
383
    CPLFree(m_pszLUTFile);
2✔
384
    GDALClose(m_poBandDataset);
2✔
385
}
4✔
386

387
/************************************************************************/
388
/*                        IReadBlock()                                  */
389
/************************************************************************/
390

391
CPLErr RS2CalibRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
20✔
392
                                      void *pImage)
393
{
394
    /* -------------------------------------------------------------------- */
395
    /*      If the last strip is partial, we need to avoid                  */
396
    /*      over-requesting.  We also need to initialize the extra part     */
397
    /*      of the block to zero.                                           */
398
    /* -------------------------------------------------------------------- */
399
    int nRequestYSize;
400
    if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
20✔
401
    {
402
        nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
×
403
        memset(pImage, 0,
×
404
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
×
405
                   nBlockXSize * nBlockYSize);
×
406
    }
407
    else
408
    {
409
        nRequestYSize = nBlockYSize;
20✔
410
    }
411

412
    CPLErr eErr;
413
    if (m_eType == GDT_CInt16)
20✔
414
    {
415
        /* read in complex values */
416
        GInt16 *pnImageTmp = reinterpret_cast<GInt16 *>(
417
            CPLMalloc(2 * sizeof(int16_t) * nBlockXSize * nBlockYSize));
×
418
        if (m_poBandDataset->GetRasterCount() == 2)
×
419
        {
420
            eErr = m_poBandDataset->RasterIO(
×
421
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
422
                nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize,
423
                nRequestYSize, GDT_Int16, 2, nullptr, 4, nBlockXSize * 4, 2,
×
424
                nullptr);
425
        }
426
        else
427
        {
428
            eErr = m_poBandDataset->RasterIO(
×
429
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
430
                nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize,
431
                nRequestYSize, GDT_UInt32, 1, nullptr, 4, nBlockXSize * 4, 0,
×
432
                nullptr);
433

434
#ifdef CPL_LSB
435
            /* First, undo the 32bit swap. */
436
            GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
×
437

438
            /* Then apply 16 bit swap. */
439
            GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
×
440
#endif
441
        }
442

443
        /* calibrate the complex values */
444
        for (int i = 0; i < nBlockYSize; i++)
×
445
        {
446
            for (int j = 0; j < nBlockXSize; j++)
×
447
            {
448
                /* calculate pixel offset in memory*/
449
                int nPixOff = (2 * (i * nBlockXSize)) + (j * 2);
×
450

451
                reinterpret_cast<float *>(pImage)[nPixOff] =
×
452
                    static_cast<float>(pnImageTmp[nPixOff]) /
×
453
                    (m_nfTable[nBlockXOff + j]);
×
454
                reinterpret_cast<float *>(pImage)[nPixOff + 1] =
×
455
                    static_cast<float>(pnImageTmp[nPixOff + 1]) /
×
456
                    (m_nfTable[nBlockXOff + j]);
×
457
            }
458
        }
459
        CPLFree(pnImageTmp);
×
460
    }
461
    // If the underlying file is NITF CFloat32
462
    else if (eDataType == GDT_CFloat32 &&
20✔
463
             m_poBandDataset->GetRasterCount() == 1)
×
464
    {
465
        eErr = m_poBandDataset->RasterIO(
×
466
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
467
            nBlockXSize, nRequestYSize, pImage, nBlockXSize, nRequestYSize,
468
            GDT_CFloat32, 1, nullptr, 2 * static_cast<int>(sizeof(float)),
469
            nBlockXSize * 2 * static_cast<GSpacing>(sizeof(float)), 0, nullptr);
×
470

471
        /* calibrate the complex values */
472
        for (int i = 0; i < nBlockYSize; i++)
×
473
        {
474
            for (int j = 0; j < nBlockXSize; j++)
×
475
            {
476
                /* calculate pixel offset in memory*/
477
                const int nPixOff = 2 * (i * nBlockXSize + j);
×
478

479
                reinterpret_cast<float *>(pImage)[nPixOff] /=
×
480
                    m_nfTable[nBlockXOff * nBlockXSize + j];
×
481
                reinterpret_cast<float *>(pImage)[nPixOff + 1] /=
×
482
                    m_nfTable[nBlockXOff * nBlockXSize + j];
×
483
            }
484
        }
485
    }
486
    else if (m_eType == GDT_UInt16)
20✔
487
    {
488
        /* read in detected values */
489
        GUInt16 *pnImageTmp = reinterpret_cast<GUInt16 *>(
490
            CPLMalloc(sizeof(uint16_t) * nBlockXSize * nBlockYSize));
×
491
        eErr = m_poBandDataset->RasterIO(
×
492
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
493
            nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize, nRequestYSize,
494
            GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, nullptr);
×
495

496
        /* iterate over detected values */
497
        for (int i = 0; i < nBlockYSize; i++)
×
498
        {
499
            for (int j = 0; j < nBlockXSize; j++)
×
500
            {
501
                int nPixOff = (i * nBlockXSize) + j;
×
502

503
                reinterpret_cast<float *>(pImage)[nPixOff] =
×
504
                    ((static_cast<float>(pnImageTmp[nPixOff]) *
×
505
                      static_cast<float>(pnImageTmp[nPixOff])) +
×
506
                     m_nfOffset) /
×
507
                    m_nfTable[nBlockXOff + j];
×
508
            }
509
        }
510
        CPLFree(pnImageTmp);
×
511
    } /* Ticket #2104: Support for ScanSAR products */
512
    else if (m_eType == GDT_Byte)
20✔
513
    {
514
        GByte *pnImageTmp = reinterpret_cast<GByte *>(
515
            CPLMalloc(static_cast<size_t>(nBlockXSize) * nBlockYSize));
20✔
516
        eErr = m_poBandDataset->RasterIO(
40✔
517
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
20✔
518
            nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize, nRequestYSize,
519
            GDT_Byte, 1, nullptr, 1, 1, 0, nullptr);
520

521
        /* iterate over detected values */
522
        for (int i = 0; i < nBlockYSize; i++)
40✔
523
        {
524
            for (int j = 0; j < nBlockXSize; j++)
420✔
525
            {
526
                int nPixOff = (i * nBlockXSize) + j;
400✔
527

528
                reinterpret_cast<float *>(pImage)[nPixOff] =
400✔
529
                    ((pnImageTmp[nPixOff] * pnImageTmp[nPixOff]) + m_nfOffset) /
400✔
530
                    m_nfTable[nBlockXOff + j];
400✔
531
            }
532
        }
533
        CPLFree(pnImageTmp);
20✔
534
    }
535
    else
536
    {
537
        CPLAssert(false);
×
538
        return CE_Failure;
539
    }
540
    return eErr;
20✔
541
}
542

543
/************************************************************************/
544
/* ==================================================================== */
545
/*                              RS2Dataset                              */
546
/* ==================================================================== */
547
/************************************************************************/
548

549
/************************************************************************/
550
/*                             RS2Dataset()                             */
551
/************************************************************************/
552

553
RS2Dataset::RS2Dataset()
5✔
554
    : psProduct(nullptr), nGCPCount(0), pasGCPList(nullptr),
555
      papszSubDatasets(nullptr), bHaveGeoTransform(FALSE),
556
      papszExtraFiles(nullptr)
5✔
557
{
558
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5✔
559
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5✔
560
    adfGeoTransform[0] = 0.0;
5✔
561
    adfGeoTransform[1] = 1.0;
5✔
562
    adfGeoTransform[2] = 0.0;
5✔
563
    adfGeoTransform[3] = 0.0;
5✔
564
    adfGeoTransform[4] = 0.0;
5✔
565
    adfGeoTransform[5] = 1.0;
5✔
566
}
5✔
567

568
/************************************************************************/
569
/*                            ~RS2Dataset()                             */
570
/************************************************************************/
571

572
RS2Dataset::~RS2Dataset()
10✔
573

574
{
575
    RS2Dataset::FlushCache(true);
5✔
576

577
    CPLDestroyXMLNode(psProduct);
5✔
578

579
    if (nGCPCount > 0)
5✔
580
    {
581
        GDALDeinitGCPs(nGCPCount, pasGCPList);
5✔
582
        CPLFree(pasGCPList);
5✔
583
    }
584

585
    RS2Dataset::CloseDependentDatasets();
5✔
586

587
    CSLDestroy(papszSubDatasets);
5✔
588
    CSLDestroy(papszExtraFiles);
5✔
589
}
10✔
590

591
/************************************************************************/
592
/*                      CloseDependentDatasets()                        */
593
/************************************************************************/
594

595
int RS2Dataset::CloseDependentDatasets()
12✔
596
{
597
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
12✔
598

599
    if (nBands != 0)
12✔
600
        bHasDroppedRef = TRUE;
5✔
601

602
    for (int iBand = 0; iBand < nBands; iBand++)
22✔
603
    {
604
        delete papoBands[iBand];
10✔
605
    }
606
    nBands = 0;
12✔
607

608
    return bHasDroppedRef;
12✔
609
}
610

611
/************************************************************************/
612
/*                            GetFileList()                             */
613
/************************************************************************/
614

615
char **RS2Dataset::GetFileList()
2✔
616

617
{
618
    char **papszFileList = GDALPamDataset::GetFileList();
2✔
619

620
    papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
2✔
621

622
    return papszFileList;
2✔
623
}
624

625
/************************************************************************/
626
/*                             Identify()                               */
627
/************************************************************************/
628

629
int RS2Dataset::Identify(GDALOpenInfo *poOpenInfo)
61,491✔
630
{
631
    /* Check for the case where we're trying to read the calibrated data: */
632
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "RADARSAT_2_CALIB:"))
61,491✔
633
    {
634
        return TRUE;
2✔
635
    }
636

637
    /* Check for directory access when there is a product.xml file in the
638
       directory. */
639
    if (poOpenInfo->bIsDirectory)
61,489✔
640
    {
641
        const CPLString osMDFilename = CPLFormCIFilenameSafe(
×
642
            poOpenInfo->pszFilename, "product.xml", nullptr);
1,226✔
643

644
        GDALOpenInfo oOpenInfo(osMDFilename.c_str(), GA_ReadOnly);
1,226✔
645
        return Identify(&oOpenInfo);
613✔
646
    }
647

648
    /* otherwise, do our normal stuff */
649
    if (strlen(poOpenInfo->pszFilename) < 11 ||
60,876✔
650
        !EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 11,
59,430✔
651
               "product.xml"))
652
        return FALSE;
60,248✔
653

654
    if (poOpenInfo->nHeaderBytes < 100)
628✔
655
        return FALSE;
618✔
656

657
    if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
10✔
658
               "/rs2") == nullptr ||
8✔
659
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
8✔
660
               "<product") == nullptr)
661
        return FALSE;
2✔
662

663
    return TRUE;
8✔
664
}
665

666
/************************************************************************/
667
/*                                Open()                                */
668
/************************************************************************/
669

670
GDALDataset *RS2Dataset::Open(GDALOpenInfo *poOpenInfo)
5✔
671

672
{
673
    /* -------------------------------------------------------------------- */
674
    /*      Is this a RADARSAT-2 Product.xml definition?                   */
675
    /* -------------------------------------------------------------------- */
676
    if (!RS2Dataset::Identify(poOpenInfo))
5✔
677
    {
678
        return nullptr;
×
679
    }
680

681
    /* -------------------------------------------------------------------- */
682
    /*        Get subdataset information, if relevant */
683
    /* -------------------------------------------------------------------- */
684
    const char *pszFilename = poOpenInfo->pszFilename;
5✔
685
    eCalibration eCalib = None;
5✔
686

687
    if (STARTS_WITH_CI(pszFilename, "RADARSAT_2_CALIB:"))
5✔
688
    {
689
        pszFilename += 17;
1✔
690

691
        if (STARTS_WITH_CI(pszFilename, "BETA0"))
1✔
692
            eCalib = Beta0;
1✔
693
        else if (STARTS_WITH_CI(pszFilename, "SIGMA0"))
×
694
            eCalib = Sigma0;
×
695
        else if (STARTS_WITH_CI(pszFilename, "GAMMA"))
×
696
            eCalib = Gamma;
×
697
        else if (STARTS_WITH_CI(pszFilename, "UNCALIB"))
×
698
            eCalib = Uncalib;
×
699
        else
700
            eCalib = None;
×
701

702
        /* advance the pointer to the actual filename */
703
        while (*pszFilename != '\0' && *pszFilename != ':')
6✔
704
            pszFilename++;
5✔
705

706
        if (*pszFilename == ':')
1✔
707
            pszFilename++;
1✔
708

709
        // need to redo the directory check:
710
        // the GDALOpenInfo check would have failed because of the calibration
711
        // string on the filename
712
        VSIStatBufL sStat;
713
        if (VSIStatL(pszFilename, &sStat) == 0)
1✔
714
            poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode);
1✔
715
    }
716

717
    CPLString osMDFilename;
10✔
718
    if (poOpenInfo->bIsDirectory)
5✔
719
    {
720
        osMDFilename =
721
            CPLFormCIFilenameSafe(pszFilename, "product.xml", nullptr);
×
722
    }
723
    else
724
        osMDFilename = pszFilename;
5✔
725

726
    /* -------------------------------------------------------------------- */
727
    /*      Ingest the Product.xml file.                                    */
728
    /* -------------------------------------------------------------------- */
729
    CPLXMLNode *psProduct = CPLParseXMLFile(osMDFilename);
5✔
730
    if (psProduct == nullptr)
5✔
731
        return nullptr;
×
732

733
    /* -------------------------------------------------------------------- */
734
    /*      Confirm the requested access is supported.                      */
735
    /* -------------------------------------------------------------------- */
736
    if (poOpenInfo->eAccess == GA_Update)
5✔
737
    {
738
        CPLDestroyXMLNode(psProduct);
×
739
        ReportUpdateNotSupportedByDriver("RS2");
×
740
        return nullptr;
×
741
    }
742

743
    CPLXMLNode *psImageAttributes =
744
        CPLGetXMLNode(psProduct, "=product.imageAttributes");
5✔
745
    if (psImageAttributes == nullptr)
5✔
746
    {
747
        CPLDestroyXMLNode(psProduct);
×
748
        CPLError(CE_Failure, CPLE_OpenFailed,
×
749
                 "Failed to find <imageAttributes> in document.");
750
        return nullptr;
×
751
    }
752

753
    CPLXMLNode *psImageGenerationParameters =
754
        CPLGetXMLNode(psProduct, "=product.imageGenerationParameters");
5✔
755
    if (psImageGenerationParameters == nullptr)
5✔
756
    {
757
        CPLDestroyXMLNode(psProduct);
×
758
        CPLError(CE_Failure, CPLE_OpenFailed,
×
759
                 "Failed to find <imageGenerationParameters> in document.");
760
        return nullptr;
×
761
    }
762

763
    /* -------------------------------------------------------------------- */
764
    /*      Create the dataset.                                             */
765
    /* -------------------------------------------------------------------- */
766
    RS2Dataset *poDS = new RS2Dataset();
5✔
767

768
    poDS->psProduct = psProduct;
5✔
769

770
    /* -------------------------------------------------------------------- */
771
    /*      Get overall image information.                                  */
772
    /* -------------------------------------------------------------------- */
773
    poDS->nRasterXSize = atoi(CPLGetXMLValue(
5✔
774
        psImageAttributes, "rasterAttributes.numberOfSamplesPerLine", "-1"));
775
    poDS->nRasterYSize = atoi(CPLGetXMLValue(
5✔
776
        psImageAttributes, "rasterAttributes.numberofLines", "-1"));
777
    if (poDS->nRasterXSize <= 1 || poDS->nRasterYSize <= 1)
5✔
778
    {
779
        CPLError(
×
780
            CE_Failure, CPLE_OpenFailed,
781
            "Non-sane raster dimensions provided in product.xml. If this is "
782
            "a valid RADARSAT-2 scene, please contact your data provider for "
783
            "a corrected dataset.");
784
        delete poDS;
×
785
        return nullptr;
×
786
    }
787

788
    /* -------------------------------------------------------------------- */
789
    /*      Check product type, as to determine if there are LUTs for       */
790
    /*      calibration purposes.                                           */
791
    /* -------------------------------------------------------------------- */
792

793
    const char *pszProductType =
794
        CPLGetXMLValue(psImageGenerationParameters,
5✔
795
                       "generalProcessingInformation.productType", "UNK");
796

797
    poDS->SetMetadataItem("PRODUCT_TYPE", pszProductType);
5✔
798

799
    /* the following cases can be assumed to have no LUTs, as per
800
     * RN-RP-51-2713, but also common sense
801
     */
802
    bool bCanCalib = false;
5✔
803
    if (!(STARTS_WITH_CI(pszProductType, "UNK") ||
5✔
804
          STARTS_WITH_CI(pszProductType, "SSG") ||
5✔
805
          STARTS_WITH_CI(pszProductType, "SPG")))
5✔
806
    {
807
        bCanCalib = true;
5✔
808
    }
809

810
    /* -------------------------------------------------------------------- */
811
    /*      Get dataType (so we can recognise complex data), and the        */
812
    /*      bitsPerSample.                                                  */
813
    /* -------------------------------------------------------------------- */
814
    const char *pszDataType =
815
        CPLGetXMLValue(psImageAttributes, "rasterAttributes.dataType", "");
5✔
816
    const int nBitsPerSample = atoi(CPLGetXMLValue(
5✔
817
        psImageAttributes, "rasterAttributes.bitsPerSample", ""));
818

819
    GDALDataType eDataType;
820
    if (nBitsPerSample == 16 && EQUAL(pszDataType, "Complex"))
5✔
821
        eDataType = GDT_CInt16;
×
822
    else if (nBitsPerSample == 32 &&
5✔
823
             EQUAL(pszDataType,
×
824
                   "Complex"))  // NITF datasets can come in this configuration
825
        eDataType = GDT_CFloat32;
×
826
    else if (nBitsPerSample == 16 && STARTS_WITH_CI(pszDataType, "Mag"))
5✔
827
        eDataType = GDT_UInt16;
×
828
    else if (nBitsPerSample == 8 && STARTS_WITH_CI(pszDataType, "Mag"))
5✔
829
        eDataType = GDT_Byte;
5✔
830
    else
831
    {
832
        delete poDS;
×
833
        CPLError(
×
834
            CE_Failure, CPLE_AppDefined,
835
            "dataType=%s, bitsPerSample=%d: not a supported configuration.",
836
            pszDataType, nBitsPerSample);
837
        return nullptr;
×
838
    }
839

840
    /* while we're at it, extract the pixel spacing information */
841
    const char *pszPixelSpacing = CPLGetXMLValue(
5✔
842
        psImageAttributes, "rasterAttributes.sampledPixelSpacing", "UNK");
843
    poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing);
5✔
844

845
    const char *pszLineSpacing = CPLGetXMLValue(
5✔
846
        psImageAttributes, "rasterAttributes.sampledLineSpacing", "UNK");
847
    poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing);
5✔
848

849
    /* -------------------------------------------------------------------- */
850
    /*      Open each of the data files as a complex band.                  */
851
    /* -------------------------------------------------------------------- */
852
    CPLString osBeta0LUT;
10✔
853
    CPLString osGammaLUT;
10✔
854
    CPLString osSigma0LUT;
10✔
855

856
    char *pszPath = CPLStrdup(CPLGetPathSafe(osMDFilename).c_str());
5✔
857
    const int nFLen = static_cast<int>(osMDFilename.size());
5✔
858

859
    CPLXMLNode *psNode = psImageAttributes->psChild;
5✔
860
    for (; psNode != nullptr; psNode = psNode->psNext)
40✔
861
    {
862
        if (psNode->eType != CXT_Element ||
35✔
863
            !(EQUAL(psNode->pszValue, "fullResolutionImageData") ||
35✔
864
              EQUAL(psNode->pszValue, "lookupTable")))
25✔
865
            continue;
10✔
866

867
        if (EQUAL(psNode->pszValue, "lookupTable") && bCanCalib)
25✔
868
        {
869
            /* Determine which incidence angle correction this is */
870
            const char *pszLUTType =
871
                CPLGetXMLValue(psNode, "incidenceAngleCorrection", "");
15✔
872
            const char *pszLUTFile = CPLGetXMLValue(psNode, "", "");
15✔
873
            CPLString osLUTFilePath =
874
                CPLFormFilenameSafe(pszPath, pszLUTFile, nullptr);
30✔
875

876
            if (EQUAL(pszLUTType, ""))
15✔
877
                continue;
×
878
            else if (EQUAL(pszLUTType, "Beta Nought") &&
20✔
879
                     IsValidXMLFile(pszPath, pszLUTFile))
5✔
880
            {
881
                poDS->papszExtraFiles =
5✔
882
                    CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
5✔
883

884
                const size_t nBufLen = nFLen + 27;
5✔
885
                char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
5✔
886
                osBeta0LUT = pszLUTFile;
5✔
887
                poDS->SetMetadataItem("BETA_NOUGHT_LUT", pszLUTFile);
5✔
888

889
                snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:BETA0:%s",
5✔
890
                         osMDFilename.c_str());
891
                poDS->papszSubDatasets = CSLSetNameValue(
5✔
892
                    poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf);
893
                poDS->papszSubDatasets =
5✔
894
                    CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_3_DESC",
5✔
895
                                    "Beta Nought calibrated");
896
                CPLFree(pszBuf);
5✔
897
            }
898
            else if (EQUAL(pszLUTType, "Sigma Nought") &&
15✔
899
                     IsValidXMLFile(pszPath, pszLUTFile))
5✔
900
            {
901
                poDS->papszExtraFiles =
5✔
902
                    CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
5✔
903

904
                const size_t nBufLen = nFLen + 27;
5✔
905
                char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
5✔
906
                osSigma0LUT = pszLUTFile;
5✔
907
                poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", pszLUTFile);
5✔
908

909
                snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:SIGMA0:%s",
5✔
910
                         osMDFilename.c_str());
911
                poDS->papszSubDatasets = CSLSetNameValue(
5✔
912
                    poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf);
913
                poDS->papszSubDatasets =
5✔
914
                    CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_2_DESC",
5✔
915
                                    "Sigma Nought calibrated");
916
                CPLFree(pszBuf);
5✔
917
            }
918
            else if (EQUAL(pszLUTType, "Gamma") &&
10✔
919
                     IsValidXMLFile(pszPath, pszLUTFile))
5✔
920
            {
921
                poDS->papszExtraFiles =
5✔
922
                    CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
5✔
923

924
                const size_t nBufLen = nFLen + 27;
5✔
925
                char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
5✔
926
                osGammaLUT = pszLUTFile;
5✔
927
                poDS->SetMetadataItem("GAMMA_LUT", pszLUTFile);
5✔
928
                snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:GAMMA:%s",
5✔
929
                         osMDFilename.c_str());
930
                poDS->papszSubDatasets = CSLSetNameValue(
5✔
931
                    poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf);
932
                poDS->papszSubDatasets =
5✔
933
                    CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_4_DESC",
5✔
934
                                    "Gamma calibrated");
935
                CPLFree(pszBuf);
5✔
936
            }
937
            continue;
15✔
938
        }
939

940
        /* --------------------------------------------------------------------
941
         */
942
        /*      Fetch filename. */
943
        /* --------------------------------------------------------------------
944
         */
945
        const char *pszBasename = CPLGetXMLValue(psNode, "", "");
10✔
946
        if (*pszBasename == '\0')
10✔
947
            continue;
×
948

949
        /* --------------------------------------------------------------------
950
         */
951
        /*      Form full filename (path of product.xml + basename). */
952
        /* --------------------------------------------------------------------
953
         */
954
        char *pszFullname = CPLStrdup(
10✔
955
            CPLFormFilenameSafe(pszPath, pszBasename, nullptr).c_str());
20✔
956

957
        /* --------------------------------------------------------------------
958
         */
959
        /*      Try and open the file. */
960
        /* --------------------------------------------------------------------
961
         */
962
        GDALDataset *poBandFile =
963
            GDALDataset::FromHandle(GDALOpen(pszFullname, GA_ReadOnly));
10✔
964
        if (poBandFile == nullptr)
10✔
965
        {
966
            CPLFree(pszFullname);
×
967
            continue;
×
968
        }
969
        if (poBandFile->GetRasterCount() == 0)
10✔
970
        {
971
            GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
×
972
            CPLFree(pszFullname);
×
973
            continue;
×
974
        }
975

976
        /* Some CFloat32 NITF files have nBitsPerSample incorrectly reported */
977
        /* as 16, and get misinterpreted as CInt16.  Check the underlying NITF
978
         */
979
        /* and override if this is the case. */
980
        if (poBandFile->GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
10✔
981
            eDataType = GDT_CFloat32;
×
982

983
        BandMapping b = GetBandFileMapping(eDataType, poBandFile);
10✔
984
        const bool twoBandComplex = b == TWOBANDCOMPLEX;
10✔
985

986
        poDS->papszExtraFiles =
10✔
987
            CSLAddString(poDS->papszExtraFiles, pszFullname);
10✔
988

989
        /* --------------------------------------------------------------------
990
         */
991
        /*      Create the band. */
992
        /* --------------------------------------------------------------------
993
         */
994
        if (eCalib == None || eCalib == Uncalib)
10✔
995
        {
996
            RS2RasterBand *poBand = new RS2RasterBand(
997
                poDS, eDataType, CPLGetXMLValue(psNode, "pole", ""), poBandFile,
8✔
998
                twoBandComplex);
8✔
999

1000
            poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
8✔
1001
        }
1002
        else
1003
        {
1004
            const char *pszLUT = nullptr;
2✔
1005
            switch (eCalib)
2✔
1006
            {
1007
                case Sigma0:
×
1008
                    pszLUT = osSigma0LUT;
×
1009
                    break;
×
1010
                case Beta0:
2✔
1011
                    pszLUT = osBeta0LUT;
2✔
1012
                    break;
2✔
1013
                case Gamma:
×
1014
                    pszLUT = osGammaLUT;
×
1015
                    break;
×
1016
                default:
×
1017
                    /* we should bomb gracefully... */
1018
                    pszLUT = osSigma0LUT;
×
1019
            }
1020
            RS2CalibRasterBand *poBand = new RS2CalibRasterBand(
1021
                poDS, CPLGetXMLValue(psNode, "pole", ""), eDataType, poBandFile,
2✔
1022
                eCalib, CPLFormFilenameSafe(pszPath, pszLUT, nullptr).c_str());
2✔
1023
            poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
2✔
1024
        }
1025

1026
        CPLFree(pszFullname);
10✔
1027
    }
1028

1029
    if (poDS->papszSubDatasets != nullptr && eCalib == None)
5✔
1030
    {
1031
        const size_t nBufLen = nFLen + 28;
4✔
1032
        char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
4✔
1033
        snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:UNCALIB:%s",
4✔
1034
                 osMDFilename.c_str());
1035
        poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
4✔
1036
                                                 "SUBDATASET_1_NAME", pszBuf);
1037
        poDS->papszSubDatasets =
4✔
1038
            CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_1_DESC",
4✔
1039
                            "Uncalibrated digital numbers");
1040
        CPLFree(pszBuf);
4✔
1041
    }
1042
    else if (poDS->papszSubDatasets != nullptr)
1✔
1043
    {
1044
        CSLDestroy(poDS->papszSubDatasets);
1✔
1045
        poDS->papszSubDatasets = nullptr;
1✔
1046
    }
1047

1048
    /* -------------------------------------------------------------------- */
1049
    /*      Set the appropriate MATRIX_REPRESENTATION.                      */
1050
    /* -------------------------------------------------------------------- */
1051

1052
    if (poDS->GetRasterCount() == 4 &&
5✔
1053
        (eDataType == GDT_CInt16 || eDataType == GDT_CFloat32))
×
1054
    {
1055
        poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
×
1056
    }
1057

1058
    /* -------------------------------------------------------------------- */
1059
    /*      Collect a few useful metadata items                             */
1060
    /* -------------------------------------------------------------------- */
1061

1062
    CPLXMLNode *psSourceAttrs =
1063
        CPLGetXMLNode(psProduct, "=product.sourceAttributes");
5✔
1064
    const char *pszItem = CPLGetXMLValue(psSourceAttrs, "satellite", "");
5✔
1065
    poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem);
5✔
1066

1067
    pszItem = CPLGetXMLValue(psSourceAttrs, "sensor", "");
5✔
1068
    poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem);
5✔
1069

1070
    if (psSourceAttrs != nullptr)
5✔
1071
    {
1072
        /* Get beam mode mnemonic */
1073
        pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeMnemonic", "UNK");
5✔
1074
        poDS->SetMetadataItem("BEAM_MODE", pszItem);
5✔
1075
        pszItem = CPLGetXMLValue(psSourceAttrs, "rawDataStartTime", "UNK");
5✔
1076
        poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem);
5✔
1077

1078
        pszItem =
1079
            CPLGetXMLValue(psSourceAttrs, "inputDatasetFacilityId", "UNK");
5✔
1080
        poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem);
5✔
1081

1082
        pszItem = CPLGetXMLValue(
5✔
1083
            psSourceAttrs, "orbitAndAttitude.orbitInformation.passDirection",
1084
            "UNK");
1085
        poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem);
5✔
1086
        pszItem = CPLGetXMLValue(
5✔
1087
            psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataSource",
1088
            "UNK");
1089
        poDS->SetMetadataItem("ORBIT_DATA_SOURCE", pszItem);
5✔
1090
        pszItem = CPLGetXMLValue(
5✔
1091
            psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataFile",
1092
            "UNK");
1093
        poDS->SetMetadataItem("ORBIT_DATA_FILE", pszItem);
5✔
1094
    }
1095

1096
    CPLXMLNode *psSarProcessingInformation =
1097
        CPLGetXMLNode(psProduct, "=product.imageGenerationParameters");
5✔
1098

1099
    if (psSarProcessingInformation != nullptr)
5✔
1100
    {
1101
        /* Get incidence angle information */
1102
        pszItem = CPLGetXMLValue(
5✔
1103
            psSarProcessingInformation,
1104
            "sarProcessingInformation.incidenceAngleNearRange", "UNK");
1105
        poDS->SetMetadataItem("NEAR_RANGE_INCIDENCE_ANGLE", pszItem);
5✔
1106

1107
        pszItem = CPLGetXMLValue(
5✔
1108
            psSarProcessingInformation,
1109
            "sarProcessingInformation.incidenceAngleFarRange", "UNK");
1110
        poDS->SetMetadataItem("FAR_RANGE_INCIDENCE_ANGLE", pszItem);
5✔
1111

1112
        pszItem = CPLGetXMLValue(psSarProcessingInformation,
5✔
1113
                                 "sarProcessingInformation.slantRangeNearEdge",
1114
                                 "UNK");
1115
        poDS->SetMetadataItem("SLANT_RANGE_NEAR_EDGE", pszItem);
5✔
1116

1117
        pszItem = CPLGetXMLValue(
5✔
1118
            psSarProcessingInformation,
1119
            "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK");
1120
        poDS->SetMetadataItem("FIRST_LINE_TIME", pszItem);
5✔
1121

1122
        pszItem = CPLGetXMLValue(
5✔
1123
            psSarProcessingInformation,
1124
            "sarProcessingInformation.zeroDopplerTimeLastLine", "UNK");
1125
        poDS->SetMetadataItem("LAST_LINE_TIME", pszItem);
5✔
1126

1127
        pszItem =
1128
            CPLGetXMLValue(psSarProcessingInformation,
5✔
1129
                           "generalProcessingInformation.productType", "UNK");
1130
        poDS->SetMetadataItem("PRODUCT_TYPE", pszItem);
5✔
1131

1132
        pszItem = CPLGetXMLValue(
5✔
1133
            psSarProcessingInformation,
1134
            "generalProcessingInformation.processingFacility", "UNK");
1135
        poDS->SetMetadataItem("PROCESSING_FACILITY", pszItem);
5✔
1136

1137
        pszItem = CPLGetXMLValue(psSarProcessingInformation,
5✔
1138
                                 "generalProcessingInformation.processingTime",
1139
                                 "UNK");
1140
        poDS->SetMetadataItem("PROCESSING_TIME", pszItem);
5✔
1141
    }
1142

1143
    /*--------------------------------------------------------------------- */
1144
    /*      Collect Map projection/Geotransform information, if present     */
1145
    /* -------------------------------------------------------------------- */
1146
    CPLXMLNode *psMapProjection =
1147
        CPLGetXMLNode(psImageAttributes, "geographicInformation.mapProjection");
5✔
1148

1149
    if (psMapProjection != nullptr)
5✔
1150
    {
1151
        CPLXMLNode *psPos =
1152
            CPLGetXMLNode(psMapProjection, "positioningInformation");
×
1153

1154
        pszItem =
1155
            CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "UNK");
×
1156
        poDS->SetMetadataItem("MAP_PROJECTION_DESCRIPTOR", pszItem);
×
1157

1158
        pszItem =
1159
            CPLGetXMLValue(psMapProjection, "mapProjectionOrientation", "UNK");
×
1160
        poDS->SetMetadataItem("MAP_PROJECTION_ORIENTATION", pszItem);
×
1161

1162
        pszItem = CPLGetXMLValue(psMapProjection, "resamplingKernel", "UNK");
×
1163
        poDS->SetMetadataItem("RESAMPLING_KERNEL", pszItem);
×
1164

1165
        pszItem = CPLGetXMLValue(psMapProjection, "satelliteHeading", "UNK");
×
1166
        poDS->SetMetadataItem("SATELLITE_HEADING", pszItem);
×
1167

1168
        if (psPos != nullptr)
×
1169
        {
1170
            const double tl_x = CPLStrtod(
×
1171
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.easting",
1172
                               "0.0"),
1173
                nullptr);
1174
            const double tl_y = CPLStrtod(
×
1175
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.northing",
1176
                               "0.0"),
1177
                nullptr);
1178
            const double bl_x = CPLStrtod(
×
1179
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.easting",
1180
                               "0.0"),
1181
                nullptr);
1182
            const double bl_y = CPLStrtod(
×
1183
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.northing",
1184
                               "0.0"),
1185
                nullptr);
1186
            const double tr_x = CPLStrtod(
×
1187
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.easting",
1188
                               "0.0"),
1189
                nullptr);
1190
            const double tr_y = CPLStrtod(
×
1191
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.northing",
1192
                               "0.0"),
1193
                nullptr);
1194
            poDS->adfGeoTransform[1] = (tr_x - tl_x) / (poDS->nRasterXSize - 1);
×
1195
            poDS->adfGeoTransform[4] = (tr_y - tl_y) / (poDS->nRasterXSize - 1);
×
1196
            poDS->adfGeoTransform[2] = (bl_x - tl_x) / (poDS->nRasterYSize - 1);
×
1197
            poDS->adfGeoTransform[5] = (bl_y - tl_y) / (poDS->nRasterYSize - 1);
×
1198
            poDS->adfGeoTransform[0] = (tl_x - 0.5 * poDS->adfGeoTransform[1] -
×
1199
                                        0.5 * poDS->adfGeoTransform[2]);
×
1200
            poDS->adfGeoTransform[3] = (tl_y - 0.5 * poDS->adfGeoTransform[4] -
×
1201
                                        0.5 * poDS->adfGeoTransform[5]);
×
1202

1203
            /* Use bottom right pixel to test geotransform */
1204
            const double br_x = CPLStrtod(
×
1205
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.easting",
1206
                               "0.0"),
1207
                nullptr);
1208
            const double br_y = CPLStrtod(
×
1209
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.northing",
1210
                               "0.0"),
1211
                nullptr);
1212
            const double testx =
×
1213
                poDS->adfGeoTransform[0] +
×
1214
                poDS->adfGeoTransform[1] * (poDS->nRasterXSize - 0.5) +
×
1215
                poDS->adfGeoTransform[2] * (poDS->nRasterYSize - 0.5);
×
1216
            const double testy =
×
1217
                poDS->adfGeoTransform[3] +
×
1218
                poDS->adfGeoTransform[4] * (poDS->nRasterXSize - 0.5) +
×
1219
                poDS->adfGeoTransform[5] * (poDS->nRasterYSize - 0.5);
×
1220

1221
            /* Give 1/4 pixel numerical error leeway in calculating location
1222
               based on affine transform */
1223
            if ((fabs(testx - br_x) >
×
1224
                 fabs(0.25 *
×
1225
                      (poDS->adfGeoTransform[1] + poDS->adfGeoTransform[2]))) ||
×
1226
                (fabs(testy - br_y) > fabs(0.25 * (poDS->adfGeoTransform[4] +
×
1227
                                                   poDS->adfGeoTransform[5]))))
×
1228
            {
1229
                CPLError(CE_Warning, CPLE_AppDefined,
×
1230
                         "Unexpected error in calculating affine transform: "
1231
                         "corner coordinates inconsistent.");
1232
            }
1233
            else
1234
            {
1235
                poDS->bHaveGeoTransform = TRUE;
×
1236
            }
1237
        }
1238
    }
1239

1240
    /* -------------------------------------------------------------------- */
1241
    /*      Collect Projection String Information                           */
1242
    /* -------------------------------------------------------------------- */
1243
    CPLXMLNode *psEllipsoid =
1244
        CPLGetXMLNode(psImageAttributes,
5✔
1245
                      "geographicInformation.referenceEllipsoidParameters");
1246

1247
    if (psEllipsoid != nullptr)
5✔
1248
    {
1249
        OGRSpatialReference oLL, oPrj;
10✔
1250
        oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5✔
1251
        oPrj.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5✔
1252

1253
        const char *pszGeodeticTerrainHeight =
1254
            CPLGetXMLValue(psEllipsoid, "geodeticTerrainHeight", "UNK");
5✔
1255
        poDS->SetMetadataItem("GEODETIC_TERRAIN_HEIGHT",
5✔
1256
                              pszGeodeticTerrainHeight);
5✔
1257

1258
        const char *pszEllipsoidName =
1259
            CPLGetXMLValue(psEllipsoid, "ellipsoidName", "");
5✔
1260
        double minor_axis =
1261
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMinorAxis", "0.0"));
5✔
1262
        double major_axis =
1263
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMajorAxis", "0.0"));
5✔
1264

1265
        if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) ||
5✔
1266
            (major_axis == 0.0))
1267
        {
1268
            CPLError(CE_Warning, CPLE_AppDefined,
×
1269
                     "Warning- incomplete"
1270
                     " ellipsoid information.  Using wgs-84 parameters.\n");
1271
            oLL.SetWellKnownGeogCS("WGS84");
×
1272
            oPrj.SetWellKnownGeogCS("WGS84");
×
1273
        }
1274
        else if (EQUAL(pszEllipsoidName, "WGS84") ||
5✔
1275
                 EQUAL(pszEllipsoidName, "WGS 1984"))
×
1276
        {
1277
            oLL.SetWellKnownGeogCS("WGS84");
5✔
1278
            oPrj.SetWellKnownGeogCS("WGS84");
5✔
1279
        }
1280
        else
1281
        {
1282
            const double inv_flattening =
×
1283
                major_axis / (major_axis - minor_axis);
×
1284
            oLL.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening);
×
1285
            oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis,
×
1286
                           inv_flattening);
1287
        }
1288

1289
        if (psMapProjection != nullptr)
5✔
1290
        {
1291
            const char *pszProj =
1292
                CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "");
×
1293
            bool bUseProjInfo = false;
×
1294

1295
            CPLXMLNode *psUtmParams =
1296
                CPLGetXMLNode(psMapProjection, "utmProjectionParameters");
×
1297

1298
            CPLXMLNode *psNspParams =
1299
                CPLGetXMLNode(psMapProjection, "nspProjectionParameters");
×
1300

1301
            if ((psUtmParams != nullptr) && poDS->bHaveGeoTransform)
×
1302
            {
1303
                /* double origEasting, origNorthing; */
1304
                bool bNorth = true;
×
1305

1306
                const int utmZone =
1307
                    atoi(CPLGetXMLValue(psUtmParams, "utmZone", ""));
×
1308
                const char *pszHemisphere =
1309
                    CPLGetXMLValue(psUtmParams, "hemisphere", "");
×
1310
#if 0
1311
                origEasting = CPLStrtod(CPLGetXMLValue(
1312
                    psUtmParams, "mapOriginFalseEasting", "0.0" ), nullptr);
1313
                origNorthing = CPLStrtod(CPLGetXMLValue(
1314
                    psUtmParams, "mapOriginFalseNorthing", "0.0" ), nullptr);
1315
#endif
1316
                if (STARTS_WITH_CI(pszHemisphere, "southern"))
×
1317
                    bNorth = FALSE;
×
1318

1319
                if (STARTS_WITH_CI(pszProj, "UTM"))
×
1320
                {
1321
                    oPrj.SetUTM(utmZone, bNorth);
×
1322
                    bUseProjInfo = true;
×
1323
                }
×
1324
            }
1325
            else if ((psNspParams != nullptr) && poDS->bHaveGeoTransform)
×
1326
            {
1327
                const double origEasting = CPLStrtod(
×
1328
                    CPLGetXMLValue(psNspParams, "mapOriginFalseEasting", "0.0"),
1329
                    nullptr);
1330
                const double origNorthing =
1331
                    CPLStrtod(CPLGetXMLValue(psNspParams,
×
1332
                                             "mapOriginFalseNorthing", "0.0"),
1333
                              nullptr);
1334
                const double copLong = CPLStrtod(
×
1335
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLongitude",
1336
                                   "0.0"),
1337
                    nullptr);
1338
                const double copLat = CPLStrtod(
×
1339
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLatitude",
1340
                                   "0.0"),
1341
                    nullptr);
1342
                const double sP1 = CPLStrtod(
×
1343
                    CPLGetXMLValue(psNspParams, "standardParallels1", "0.0"),
1344
                    nullptr);
1345
                const double sP2 = CPLStrtod(
×
1346
                    CPLGetXMLValue(psNspParams, "standardParallels2", "0.0"),
1347
                    nullptr);
1348

1349
                if (STARTS_WITH_CI(pszProj, "ARC"))
×
1350
                {
1351
                    /* Albers Conical Equal Area */
1352
                    oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting,
×
1353
                                 origNorthing);
1354
                    bUseProjInfo = true;
×
1355
                }
1356
                else if (STARTS_WITH_CI(pszProj, "LCC"))
×
1357
                {
1358
                    /* Lambert Conformal Conic */
1359
                    oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting,
×
1360
                                origNorthing);
1361
                    bUseProjInfo = true;
×
1362
                }
1363
                else if (STARTS_WITH_CI(pszProj, "STPL"))
×
1364
                {
1365
                    /* State Plate
1366
                       ASSUMPTIONS: "zone" in product.xml matches USGS
1367
                       definition as expected by ogr spatial reference; NAD83
1368
                       zones (versus NAD27) are assumed. */
1369

1370
                    const int nSPZone =
1371
                        atoi(CPLGetXMLValue(psNspParams, "zone", "1"));
×
1372

1373
                    oPrj.SetStatePlane(nSPZone, TRUE, nullptr, 0.0);
×
1374
                    bUseProjInfo = true;
×
1375
                }
1376
            }
1377

1378
            if (bUseProjInfo)
×
1379
            {
1380
                poDS->m_oSRS = std::move(oPrj);
×
1381
            }
1382
            else
1383
            {
1384
                CPLError(CE_Warning, CPLE_AppDefined,
×
1385
                         "Unable to interpret "
1386
                         "projection information; check mapProjection info in "
1387
                         "product.xml!");
1388
            }
1389
        }
1390

1391
        poDS->m_oGCPSRS = std::move(oLL);
5✔
1392
    }
1393

1394
    /* -------------------------------------------------------------------- */
1395
    /*      Collect GCPs.                                                   */
1396
    /* -------------------------------------------------------------------- */
1397
    CPLXMLNode *psGeoGrid = CPLGetXMLNode(
5✔
1398
        psImageAttributes, "geographicInformation.geolocationGrid");
1399

1400
    if (psGeoGrid != nullptr)
5✔
1401
    {
1402
        /* count GCPs */
1403
        poDS->nGCPCount = 0;
5✔
1404

1405
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
25✔
1406
             psNode = psNode->psNext)
20✔
1407
        {
1408
            if (EQUAL(psNode->pszValue, "imageTiePoint"))
20✔
1409
                poDS->nGCPCount++;
20✔
1410
        }
1411

1412
        poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>(
5✔
1413
            CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount));
5✔
1414

1415
        poDS->nGCPCount = 0;
5✔
1416

1417
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
25✔
1418
             psNode = psNode->psNext)
20✔
1419
        {
1420
            GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount;
20✔
1421

1422
            if (!EQUAL(psNode->pszValue, "imageTiePoint"))
20✔
1423
                continue;
×
1424

1425
            poDS->nGCPCount++;
20✔
1426

1427
            char szID[32];
1428
            snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount);
20✔
1429
            psGCP->pszId = CPLStrdup(szID);
20✔
1430
            psGCP->pszInfo = CPLStrdup("");
20✔
1431
            psGCP->dfGCPPixel =
20✔
1432
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.pixel", "0")) +
20✔
1433
                0.5;
1434
            psGCP->dfGCPLine =
20✔
1435
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.line", "0")) +
20✔
1436
                0.5;
1437
            psGCP->dfGCPX = CPLAtof(
20✔
1438
                CPLGetXMLValue(psNode, "geodeticCoordinate.longitude", ""));
1439
            psGCP->dfGCPY = CPLAtof(
20✔
1440
                CPLGetXMLValue(psNode, "geodeticCoordinate.latitude", ""));
1441
            psGCP->dfGCPZ = CPLAtof(
20✔
1442
                CPLGetXMLValue(psNode, "geodeticCoordinate.height", ""));
1443
        }
1444
    }
1445

1446
    CPLFree(pszPath);
5✔
1447

1448
    /* -------------------------------------------------------------------- */
1449
    /*      Collect RPC.                                                   */
1450
    /* -------------------------------------------------------------------- */
1451
    CPLXMLNode *psRationalFunctions = CPLGetXMLNode(
5✔
1452
        psImageAttributes, "geographicInformation.rationalFunctions");
1453
    if (psRationalFunctions != nullptr)
5✔
1454
    {
1455
        char **papszRPC = nullptr;
5✔
1456
        static const char *const apszXMLToGDALMapping[] = {
1457
            "biasError",
1458
            "ERR_BIAS",
1459
            "randomError",
1460
            "ERR_RAND",
1461
            //"lineFitQuality", "????",
1462
            //"pixelFitQuality", "????",
1463
            "lineOffset",
1464
            "LINE_OFF",
1465
            "pixelOffset",
1466
            "SAMP_OFF",
1467
            "latitudeOffset",
1468
            "LAT_OFF",
1469
            "longitudeOffset",
1470
            "LONG_OFF",
1471
            "heightOffset",
1472
            "HEIGHT_OFF",
1473
            "lineScale",
1474
            "LINE_SCALE",
1475
            "pixelScale",
1476
            "SAMP_SCALE",
1477
            "latitudeScale",
1478
            "LAT_SCALE",
1479
            "longitudeScale",
1480
            "LONG_SCALE",
1481
            "heightScale",
1482
            "HEIGHT_SCALE",
1483
            "lineNumeratorCoefficients",
1484
            "LINE_NUM_COEFF",
1485
            "lineDenominatorCoefficients",
1486
            "LINE_DEN_COEFF",
1487
            "pixelNumeratorCoefficients",
1488
            "SAMP_NUM_COEFF",
1489
            "pixelDenominatorCoefficients",
1490
            "SAMP_DEN_COEFF",
1491
        };
1492
        for (size_t i = 0; i < CPL_ARRAYSIZE(apszXMLToGDALMapping); i += 2)
85✔
1493
        {
1494
            const char *pszValue = CPLGetXMLValue(
160✔
1495
                psRationalFunctions, apszXMLToGDALMapping[i], nullptr);
80✔
1496
            if (pszValue)
80✔
1497
                papszRPC = CSLSetNameValue(
80✔
1498
                    papszRPC, apszXMLToGDALMapping[i + 1], pszValue);
80✔
1499
        }
1500
        poDS->GDALDataset::SetMetadata(papszRPC, "RPC");
5✔
1501
        CSLDestroy(papszRPC);
5✔
1502
    }
1503

1504
    /* -------------------------------------------------------------------- */
1505
    /*      Initialize any PAM information.                                 */
1506
    /* -------------------------------------------------------------------- */
1507
    CPLString osDescription;
5✔
1508

1509
    switch (eCalib)
5✔
1510
    {
1511
        case Sigma0:
×
1512
            osDescription.Printf("RADARSAT_2_CALIB:SIGMA0:%s",
1513
                                 osMDFilename.c_str());
×
1514
            break;
×
1515
        case Beta0:
1✔
1516
            osDescription.Printf("RADARSAT_2_CALIB:BETA0:%s",
1517
                                 osMDFilename.c_str());
1✔
1518
            break;
1✔
1519
        case Gamma:
×
1520
            osDescription.Printf("RADARSAT_2_CALIB:GAMMA0:%s",
1521
                                 osMDFilename.c_str());
×
1522
            break;
×
1523
        case Uncalib:
×
1524
            osDescription.Printf("RADARSAT_2_CALIB:UNCALIB:%s",
1525
                                 osMDFilename.c_str());
×
1526
            break;
×
1527
        default:
4✔
1528
            osDescription = osMDFilename;
4✔
1529
    }
1530

1531
    if (eCalib != None)
5✔
1532
        poDS->papszExtraFiles =
1✔
1533
            CSLAddString(poDS->papszExtraFiles, osMDFilename);
1✔
1534

1535
    /* -------------------------------------------------------------------- */
1536
    /*      Initialize any PAM information.                                 */
1537
    /* -------------------------------------------------------------------- */
1538
    poDS->SetDescription(osDescription);
5✔
1539

1540
    poDS->SetPhysicalFilename(osMDFilename);
5✔
1541
    poDS->SetSubdatasetName(osDescription);
5✔
1542

1543
    poDS->TryLoadXML();
5✔
1544

1545
    /* -------------------------------------------------------------------- */
1546
    /*      Check for overviews.                                            */
1547
    /* -------------------------------------------------------------------- */
1548
    poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
5✔
1549

1550
    return poDS;
5✔
1551
}
1552

1553
/************************************************************************/
1554
/*                            GetGCPCount()                             */
1555
/************************************************************************/
1556

1557
int RS2Dataset::GetGCPCount()
×
1558

1559
{
1560
    return nGCPCount;
×
1561
}
1562

1563
/************************************************************************/
1564
/*                          GetGCPSpatialRef()                          */
1565
/************************************************************************/
1566

1567
const OGRSpatialReference *RS2Dataset::GetGCPSpatialRef() const
×
1568

1569
{
1570
    return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
×
1571
}
1572

1573
/************************************************************************/
1574
/*                               GetGCPs()                              */
1575
/************************************************************************/
1576

1577
const GDAL_GCP *RS2Dataset::GetGCPs()
×
1578

1579
{
1580
    return pasGCPList;
×
1581
}
1582

1583
/************************************************************************/
1584
/*                          GetSpatialRef()                             */
1585
/************************************************************************/
1586

1587
const OGRSpatialReference *RS2Dataset::GetSpatialRef() const
×
1588

1589
{
1590
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
×
1591
}
1592

1593
/************************************************************************/
1594
/*                          GetGeoTransform()                           */
1595
/************************************************************************/
1596

1597
CPLErr RS2Dataset::GetGeoTransform(double *padfTransform)
×
1598

1599
{
1600
    memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
×
1601

1602
    if (bHaveGeoTransform)
×
1603
        return CE_None;
×
1604

1605
    return CE_Failure;
×
1606
}
1607

1608
/************************************************************************/
1609
/*                      GetMetadataDomainList()                         */
1610
/************************************************************************/
1611

1612
char **RS2Dataset::GetMetadataDomainList()
×
1613
{
1614
    return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
×
1615
                                   "SUBDATASETS", nullptr);
×
1616
}
1617

1618
/************************************************************************/
1619
/*                            GetMetadata()                             */
1620
/************************************************************************/
1621

1622
char **RS2Dataset::GetMetadata(const char *pszDomain)
1✔
1623

1624
{
1625
    if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS") &&
1✔
1626
        papszSubDatasets != nullptr)
×
1627
        return papszSubDatasets;
×
1628

1629
    return GDALDataset::GetMetadata(pszDomain);
1✔
1630
}
1631

1632
/************************************************************************/
1633
/*                         GDALRegister_RS2()                          */
1634
/************************************************************************/
1635

1636
void GDALRegister_RS2()
1,911✔
1637

1638
{
1639
    if (GDALGetDriverByName("RS2") != nullptr)
1,911✔
1640
        return;
282✔
1641

1642
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1643

1644
    poDriver->SetDescription("RS2");
1,629✔
1645
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1646
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "RadarSat 2 XML Product");
1,629✔
1647
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rs2.html");
1,629✔
1648
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1,629✔
1649
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1650

1651
    poDriver->pfnOpen = RS2Dataset::Open;
1,629✔
1652
    poDriver->pfnIdentify = RS2Dataset::Identify;
1,629✔
1653

1654
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1655
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc