Coverage Report

Created: 2025-06-09 08:44

/src/gdal/frmts/rs2/rs2dataset.cpp
Line
Count
Source (jump to first uncovered line)
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)
31
0
{
32
    /* Return true for valid xml file, false otherwise */
33
0
    char *pszLutFile =
34
0
        VSIStrdup(CPLFormFilenameSafe(pszPath, pszLut, nullptr).c_str());
35
36
0
    CPLXMLTreeCloser psLut(CPLParseXMLFile(pszLutFile));
37
38
0
    CPLFree(pszLutFile);
39
40
0
    return psLut.get() != nullptr;
41
0
}
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,
56
                                      GDALDataset *poBandFile)
57
0
{
58
59
0
    GDALRasterBand *poBand1 = poBandFile->GetRasterBand(1);
60
0
    GDALDataType eBandDataType1 = poBand1->GetRasterDataType();
61
62
    // if there is one band and it has the same datatype, the band file gets
63
    // passed straight through
64
0
    if (poBandFile->GetRasterCount() == 1 && eDataType == eBandDataType1)
65
0
        return STRAIGHT;
66
67
    // if the band file has 2 bands, they should represent I+Q
68
    // and be a compatible data type
69
0
    if (poBandFile->GetRasterCount() == 2 && GDALDataTypeIsComplex(eDataType))
70
0
    {
71
0
        GDALRasterBand *band2 = poBandFile->GetRasterBand(2);
72
73
0
        if (eBandDataType1 != band2->GetRasterDataType())
74
0
            return BANDERROR;  // both bands must be same datatype
75
76
        // check compatible types - there are 4 complex types in GDAL
77
0
        if ((eDataType == GDT_CInt16 && eBandDataType1 == GDT_Int16) ||
78
0
            (eDataType == GDT_CInt32 && eBandDataType1 == GDT_Int32) ||
79
0
            (eDataType == GDT_CFloat32 && eBandDataType1 == GDT_Float32) ||
80
0
            (eDataType == GDT_CFloat64 && eBandDataType1 == GDT_Float64))
81
0
            return TWOBANDCOMPLEX;
82
0
    }
83
0
    return BANDERROR;  // don't accept any other combinations
84
0
}
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
  protected:
107
    virtual int CloseDependentDatasets() override;
108
109
  public:
110
    RS2Dataset();
111
    virtual ~RS2Dataset();
112
113
    virtual int GetGCPCount() override;
114
    const OGRSpatialReference *GetGCPSpatialRef() const override;
115
    virtual const GDAL_GCP *GetGCPs() override;
116
117
    const OGRSpatialReference *GetSpatialRef() const override;
118
    virtual CPLErr GetGeoTransform(double *) override;
119
120
    virtual char **GetMetadataDomainList() override;
121
    virtual char **GetMetadata(const char *pszDomain = "") override;
122
    virtual char **GetFileList(void) override;
123
124
    static GDALDataset *Open(GDALOpenInfo *);
125
    static int Identify(GDALOpenInfo *);
126
127
    CPLXMLNode *GetProduct()
128
0
    {
129
0
        return psProduct;
130
0
    }
131
};
132
133
/************************************************************************/
134
/* ==================================================================== */
135
/*                    RS2RasterBand                           */
136
/* ==================================================================== */
137
/************************************************************************/
138
139
class RS2RasterBand final : public GDALPamRasterBand
140
{
141
    GDALDataset *poBandFile = nullptr;
142
143
    // 2 bands representing I+Q -> one complex band
144
    // otherwise poBandFile is passed straight through
145
    bool bIsTwoBandComplex = false;
146
147
  public:
148
    RS2RasterBand(RS2Dataset *poDSIn, GDALDataType eDataTypeIn,
149
                  const char *pszPole, GDALDataset *poBandFile,
150
                  bool bTwoBandComplex = false);
151
    virtual ~RS2RasterBand();
152
153
    virtual CPLErr IReadBlock(int, int, void *) override;
154
155
    static GDALDataset *Open(GDALOpenInfo *);
156
};
157
158
/************************************************************************/
159
/*                            RS2RasterBand                             */
160
/************************************************************************/
161
162
RS2RasterBand::RS2RasterBand(RS2Dataset *poDSIn, GDALDataType eDataTypeIn,
163
                             const char *pszPole, GDALDataset *poBandFileIn,
164
                             bool bTwoBandComplex)
165
0
    : poBandFile(poBandFileIn)
166
0
{
167
0
    poDS = poDSIn;
168
169
0
    GDALRasterBand *poSrcBand = poBandFile->GetRasterBand(1);
170
171
0
    poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
172
173
0
    eDataType = eDataTypeIn;
174
175
0
    bIsTwoBandComplex = bTwoBandComplex;
176
177
0
    if (*pszPole != '\0')
178
0
        SetMetadataItem("POLARIMETRIC_INTERP", pszPole);
179
0
}
180
181
/************************************************************************/
182
/*                            RSRasterBand()                            */
183
/************************************************************************/
184
185
RS2RasterBand::~RS2RasterBand()
186
187
0
{
188
0
    if (poBandFile != nullptr)
189
0
        GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
190
0
}
191
192
/************************************************************************/
193
/*                             IReadBlock()                             */
194
/************************************************************************/
195
196
CPLErr RS2RasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
197
198
0
{
199
    /* -------------------------------------------------------------------- */
200
    /*      If the last strip is partial, we need to avoid                  */
201
    /*      over-requesting.  We also need to initialize the extra part     */
202
    /*      of the block to zero.                                           */
203
    /* -------------------------------------------------------------------- */
204
0
    int nRequestYSize;
205
0
    if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
206
0
    {
207
0
        nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
208
0
        memset(pImage, 0,
209
0
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
210
0
                   nBlockXSize * nBlockYSize);
211
0
    }
212
0
    else
213
0
    {
214
0
        nRequestYSize = nBlockYSize;
215
0
    }
216
217
    /*-------------------------------------------------------------------- */
218
    /*      If the input imagery is tiled, also need to avoid over-        */
219
    /*      requesting in the X-direction.                                 */
220
    /* ------------------------------------------------------------------- */
221
0
    int nRequestXSize;
222
0
    if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize)
223
0
    {
224
0
        nRequestXSize = nRasterXSize - nBlockXOff * nBlockXSize;
225
0
        memset(pImage, 0,
226
0
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
227
0
                   nBlockXSize * nBlockYSize);
228
0
    }
229
0
    else
230
0
    {
231
0
        nRequestXSize = nBlockXSize;
232
0
    }
233
0
    if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 2)
234
0
        return poBandFile->RasterIO(
235
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
236
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
237
0
            GDT_Int16, 2, nullptr, 4, nBlockXSize * 4, 2, nullptr);
238
239
    /* -------------------------------------------------------------------- */
240
    /*      File has one sample marked as sample format void, a 32bits.     */
241
    /* -------------------------------------------------------------------- */
242
0
    else if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 1)
243
0
    {
244
0
        CPLErr eErr = poBandFile->RasterIO(
245
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
246
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
247
0
            GDT_UInt32, 1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
248
249
0
#ifdef CPL_LSB
250
        /* First, undo the 32bit swap. */
251
0
        GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
252
253
        /* Then apply 16 bit swap. */
254
0
        GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
255
0
#endif
256
257
0
        return eErr;
258
0
    }
259
260
    /* -------------------------------------------------------------------- */
261
    /*      The 16bit case is straight forward.  The underlying file        */
262
    /*      looks like a 16bit unsigned data too.                           */
263
    /* -------------------------------------------------------------------- */
264
0
    else if (eDataType == GDT_UInt16)
265
0
        return poBandFile->RasterIO(
266
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
267
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
268
0
            GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, nullptr);
269
0
    else if (eDataType == GDT_Byte)
270
        /* Ticket #2104: Support for ScanSAR products */
271
0
        return poBandFile->RasterIO(
272
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
273
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
274
0
            GDT_Byte, 1, nullptr, 1, nBlockXSize, 0, nullptr);
275
276
0
    CPLAssert(false);
277
0
    return CE_Failure;
278
0
}
279
280
/************************************************************************/
281
/* ==================================================================== */
282
/*                         RS2CalibRasterBand                           */
283
/* ==================================================================== */
284
/************************************************************************/
285
/* Returns data that has been calibrated to sigma nought, gamma         */
286
/* or beta nought.                                                      */
287
/************************************************************************/
288
289
class RS2CalibRasterBand final : public GDALPamRasterBand
290
{
291
  private:
292
    // eCalibration m_eCalib;
293
    GDALDataset *m_poBandDataset;
294
    GDALDataType m_eType; /* data type of data being ingested */
295
    float *m_nfTable;
296
    int m_nTableSize;
297
    float m_nfOffset;
298
    char *m_pszLUTFile;
299
300
    void ReadLUT();
301
302
  public:
303
    RS2CalibRasterBand(RS2Dataset *poDataset, const char *pszPolarization,
304
                       GDALDataType eType, GDALDataset *poBandDataset,
305
                       eCalibration eCalib, const char *pszLUT);
306
    ~RS2CalibRasterBand();
307
308
    CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
309
};
310
311
/************************************************************************/
312
/*                            ReadLUT()                                 */
313
/************************************************************************/
314
/* Read the provided LUT in to m_ndTable                                */
315
/************************************************************************/
316
void RS2CalibRasterBand::ReadLUT()
317
0
{
318
0
    CPLXMLNode *psLUT = CPLParseXMLFile(m_pszLUTFile);
319
320
0
    this->m_nfOffset = static_cast<float>(
321
0
        CPLAtof(CPLGetXMLValue(psLUT, "=lut.offset", "0.0")));
322
323
0
    char **papszLUTList = CSLTokenizeString2(
324
0
        CPLGetXMLValue(psLUT, "=lut.gains", ""), " ", CSLT_HONOURSTRINGS);
325
326
0
    m_nTableSize = CSLCount(papszLUTList);
327
328
0
    m_nfTable =
329
0
        reinterpret_cast<float *>(CPLMalloc(sizeof(float) * m_nTableSize));
330
331
0
    for (int i = 0; i < m_nTableSize; i++)
332
0
    {
333
0
        m_nfTable[i] = static_cast<float>(CPLAtof(papszLUTList[i]));
334
0
    }
335
336
0
    CPLDestroyXMLNode(psLUT);
337
338
0
    CSLDestroy(papszLUTList);
339
0
}
340
341
/************************************************************************/
342
/*                        RS2CalibRasterBand()                          */
343
/************************************************************************/
344
345
RS2CalibRasterBand::RS2CalibRasterBand(
346
    RS2Dataset *poDataset, const char *pszPolarization, GDALDataType eType,
347
    GDALDataset *poBandDataset, eCalibration /* eCalib */, const char *pszLUT)
348
    :  // m_eCalib(eCalib),
349
0
      m_poBandDataset(poBandDataset), m_eType(eType), m_nfTable(nullptr),
350
0
      m_nTableSize(0), m_nfOffset(0), m_pszLUTFile(VSIStrdup(pszLUT))
351
0
{
352
0
    poDS = poDataset;
353
354
0
    if (*pszPolarization != '\0')
355
0
    {
356
0
        SetMetadataItem("POLARIMETRIC_INTERP", pszPolarization);
357
0
    }
358
359
0
    if (eType == GDT_CInt16)
360
0
        eDataType = GDT_CFloat32;
361
0
    else
362
0
        eDataType = GDT_Float32;
363
364
0
    GDALRasterBand *poRasterBand = poBandDataset->GetRasterBand(1);
365
0
    poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
366
367
0
    ReadLUT();
368
0
}
369
370
/************************************************************************/
371
/*                       ~RS2CalibRasterBand()                          */
372
/************************************************************************/
373
374
RS2CalibRasterBand::~RS2CalibRasterBand()
375
0
{
376
0
    CPLFree(m_nfTable);
377
0
    CPLFree(m_pszLUTFile);
378
0
    GDALClose(m_poBandDataset);
379
0
}
380
381
/************************************************************************/
382
/*                        IReadBlock()                                  */
383
/************************************************************************/
384
385
CPLErr RS2CalibRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
386
                                      void *pImage)
387
0
{
388
    /* -------------------------------------------------------------------- */
389
    /*      If the last strip is partial, we need to avoid                  */
390
    /*      over-requesting.  We also need to initialize the extra part     */
391
    /*      of the block to zero.                                           */
392
    /* -------------------------------------------------------------------- */
393
0
    int nRequestYSize;
394
0
    if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
395
0
    {
396
0
        nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
397
0
        memset(pImage, 0,
398
0
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
399
0
                   nBlockXSize * nBlockYSize);
400
0
    }
401
0
    else
402
0
    {
403
0
        nRequestYSize = nBlockYSize;
404
0
    }
405
406
0
    CPLErr eErr;
407
0
    if (m_eType == GDT_CInt16)
408
0
    {
409
        /* read in complex values */
410
0
        GInt16 *pnImageTmp = reinterpret_cast<GInt16 *>(
411
0
            CPLMalloc(2 * nBlockXSize * nBlockYSize *
412
0
                      GDALGetDataTypeSize(GDT_Int16) / 8));
413
0
        if (m_poBandDataset->GetRasterCount() == 2)
414
0
        {
415
0
            eErr = m_poBandDataset->RasterIO(
416
0
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
417
0
                nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize,
418
0
                nRequestYSize, GDT_Int16, 2, nullptr, 4, nBlockXSize * 4, 2,
419
0
                nullptr);
420
0
        }
421
0
        else
422
0
        {
423
0
            eErr = m_poBandDataset->RasterIO(
424
0
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
425
0
                nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize,
426
0
                nRequestYSize, GDT_UInt32, 1, nullptr, 4, nBlockXSize * 4, 0,
427
0
                nullptr);
428
429
0
#ifdef CPL_LSB
430
            /* First, undo the 32bit swap. */
431
0
            GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
432
433
            /* Then apply 16 bit swap. */
434
0
            GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
435
0
#endif
436
0
        }
437
438
        /* calibrate the complex values */
439
0
        for (int i = 0; i < nBlockYSize; i++)
440
0
        {
441
0
            for (int j = 0; j < nBlockXSize; j++)
442
0
            {
443
                /* calculate pixel offset in memory*/
444
0
                int nPixOff = (2 * (i * nBlockXSize)) + (j * 2);
445
446
0
                reinterpret_cast<float *>(pImage)[nPixOff] =
447
0
                    static_cast<float>(pnImageTmp[nPixOff]) /
448
0
                    (m_nfTable[nBlockXOff + j]);
449
0
                reinterpret_cast<float *>(pImage)[nPixOff + 1] =
450
0
                    static_cast<float>(pnImageTmp[nPixOff + 1]) /
451
0
                    (m_nfTable[nBlockXOff + j]);
452
0
            }
453
0
        }
454
0
        CPLFree(pnImageTmp);
455
0
    }
456
    // If the underlying file is NITF CFloat32
457
0
    else if (eDataType == GDT_CFloat32 &&
458
0
             m_poBandDataset->GetRasterCount() == 1)
459
0
    {
460
0
        eErr = m_poBandDataset->RasterIO(
461
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
462
0
            nBlockXSize, nRequestYSize, pImage, nBlockXSize, nRequestYSize,
463
0
            GDT_CFloat32, 1, nullptr, 2 * static_cast<int>(sizeof(float)),
464
0
            nBlockXSize * 2 * static_cast<GSpacing>(sizeof(float)), 0, nullptr);
465
466
        /* calibrate the complex values */
467
0
        for (int i = 0; i < nBlockYSize; i++)
468
0
        {
469
0
            for (int j = 0; j < nBlockXSize; j++)
470
0
            {
471
                /* calculate pixel offset in memory*/
472
0
                const int nPixOff = 2 * (i * nBlockXSize + j);
473
474
0
                reinterpret_cast<float *>(pImage)[nPixOff] /=
475
0
                    m_nfTable[nBlockXOff * nBlockXSize + j];
476
0
                reinterpret_cast<float *>(pImage)[nPixOff + 1] /=
477
0
                    m_nfTable[nBlockXOff * nBlockXSize + j];
478
0
            }
479
0
        }
480
0
    }
481
0
    else if (m_eType == GDT_UInt16)
482
0
    {
483
        /* read in detected values */
484
0
        GUInt16 *pnImageTmp = reinterpret_cast<GUInt16 *>(CPLMalloc(
485
0
            nBlockXSize * nBlockYSize * GDALGetDataTypeSize(GDT_UInt16) / 8));
486
0
        eErr = m_poBandDataset->RasterIO(
487
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
488
0
            nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize, nRequestYSize,
489
0
            GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, nullptr);
490
491
        /* iterate over detected values */
492
0
        for (int i = 0; i < nBlockYSize; i++)
493
0
        {
494
0
            for (int j = 0; j < nBlockXSize; j++)
495
0
            {
496
0
                int nPixOff = (i * nBlockXSize) + j;
497
498
0
                reinterpret_cast<float *>(pImage)[nPixOff] =
499
0
                    ((static_cast<float>(pnImageTmp[nPixOff]) *
500
0
                      static_cast<float>(pnImageTmp[nPixOff])) +
501
0
                     m_nfOffset) /
502
0
                    m_nfTable[nBlockXOff + j];
503
0
            }
504
0
        }
505
0
        CPLFree(pnImageTmp);
506
0
    } /* Ticket #2104: Support for ScanSAR products */
507
0
    else if (m_eType == GDT_Byte)
508
0
    {
509
0
        GByte *pnImageTmp = reinterpret_cast<GByte *>(CPLMalloc(
510
0
            nBlockXSize * nBlockYSize * GDALGetDataTypeSize(GDT_Byte) / 8));
511
0
        eErr = m_poBandDataset->RasterIO(
512
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
513
0
            nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize, nRequestYSize,
514
0
            GDT_Byte, 1, nullptr, 1, 1, 0, nullptr);
515
516
        /* iterate over detected values */
517
0
        for (int i = 0; i < nBlockYSize; i++)
518
0
        {
519
0
            for (int j = 0; j < nBlockXSize; j++)
520
0
            {
521
0
                int nPixOff = (i * nBlockXSize) + j;
522
523
0
                reinterpret_cast<float *>(pImage)[nPixOff] =
524
0
                    ((pnImageTmp[nPixOff] * pnImageTmp[nPixOff]) + m_nfOffset) /
525
0
                    m_nfTable[nBlockXOff + j];
526
0
            }
527
0
        }
528
0
        CPLFree(pnImageTmp);
529
0
    }
530
0
    else
531
0
    {
532
0
        CPLAssert(false);
533
0
        return CE_Failure;
534
0
    }
535
0
    return eErr;
536
0
}
537
538
/************************************************************************/
539
/* ==================================================================== */
540
/*                              RS2Dataset                              */
541
/* ==================================================================== */
542
/************************************************************************/
543
544
/************************************************************************/
545
/*                             RS2Dataset()                             */
546
/************************************************************************/
547
548
RS2Dataset::RS2Dataset()
549
0
    : psProduct(nullptr), nGCPCount(0), pasGCPList(nullptr),
550
0
      papszSubDatasets(nullptr), bHaveGeoTransform(FALSE),
551
0
      papszExtraFiles(nullptr)
552
0
{
553
0
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
554
0
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
555
0
    adfGeoTransform[0] = 0.0;
556
0
    adfGeoTransform[1] = 1.0;
557
0
    adfGeoTransform[2] = 0.0;
558
0
    adfGeoTransform[3] = 0.0;
559
0
    adfGeoTransform[4] = 0.0;
560
0
    adfGeoTransform[5] = 1.0;
561
0
}
562
563
/************************************************************************/
564
/*                            ~RS2Dataset()                             */
565
/************************************************************************/
566
567
RS2Dataset::~RS2Dataset()
568
569
0
{
570
0
    RS2Dataset::FlushCache(true);
571
572
0
    CPLDestroyXMLNode(psProduct);
573
574
0
    if (nGCPCount > 0)
575
0
    {
576
0
        GDALDeinitGCPs(nGCPCount, pasGCPList);
577
0
        CPLFree(pasGCPList);
578
0
    }
579
580
0
    RS2Dataset::CloseDependentDatasets();
581
582
0
    CSLDestroy(papszSubDatasets);
583
0
    CSLDestroy(papszExtraFiles);
584
0
}
585
586
/************************************************************************/
587
/*                      CloseDependentDatasets()                        */
588
/************************************************************************/
589
590
int RS2Dataset::CloseDependentDatasets()
591
0
{
592
0
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
593
594
0
    if (nBands != 0)
595
0
        bHasDroppedRef = TRUE;
596
597
0
    for (int iBand = 0; iBand < nBands; iBand++)
598
0
    {
599
0
        delete papoBands[iBand];
600
0
    }
601
0
    nBands = 0;
602
603
0
    return bHasDroppedRef;
604
0
}
605
606
/************************************************************************/
607
/*                            GetFileList()                             */
608
/************************************************************************/
609
610
char **RS2Dataset::GetFileList()
611
612
0
{
613
0
    char **papszFileList = GDALPamDataset::GetFileList();
614
615
0
    papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
616
617
0
    return papszFileList;
618
0
}
619
620
/************************************************************************/
621
/*                             Identify()                               */
622
/************************************************************************/
623
624
int RS2Dataset::Identify(GDALOpenInfo *poOpenInfo)
625
920k
{
626
    /* Check for the case where we're trying to read the calibrated data: */
627
920k
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "RADARSAT_2_CALIB:"))
628
0
    {
629
0
        return TRUE;
630
0
    }
631
632
    /* Check for directory access when there is a product.xml file in the
633
       directory. */
634
920k
    if (poOpenInfo->bIsDirectory)
635
6.52k
    {
636
6.52k
        const CPLString osMDFilename = CPLFormCIFilenameSafe(
637
6.52k
            poOpenInfo->pszFilename, "product.xml", nullptr);
638
639
6.52k
        GDALOpenInfo oOpenInfo(osMDFilename.c_str(), GA_ReadOnly);
640
6.52k
        return Identify(&oOpenInfo);
641
6.52k
    }
642
643
    /* otherwise, do our normal stuff */
644
913k
    if (strlen(poOpenInfo->pszFilename) < 11 ||
645
913k
        !EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 11,
646
913k
               "product.xml"))
647
907k
        return FALSE;
648
649
6.55k
    if (poOpenInfo->nHeaderBytes < 100)
650
6.52k
        return FALSE;
651
652
24
    if (strstr((const char *)poOpenInfo->pabyHeader, "/rs2") == nullptr ||
653
24
        strstr((const char *)poOpenInfo->pabyHeader, "<product") == nullptr)
654
24
        return FALSE;
655
656
0
    return TRUE;
657
24
}
658
659
/************************************************************************/
660
/*                                Open()                                */
661
/************************************************************************/
662
663
GDALDataset *RS2Dataset::Open(GDALOpenInfo *poOpenInfo)
664
665
0
{
666
    /* -------------------------------------------------------------------- */
667
    /*      Is this a RADARSAT-2 Product.xml definition?                   */
668
    /* -------------------------------------------------------------------- */
669
0
    if (!RS2Dataset::Identify(poOpenInfo))
670
0
    {
671
0
        return nullptr;
672
0
    }
673
674
    /* -------------------------------------------------------------------- */
675
    /*        Get subdataset information, if relevant */
676
    /* -------------------------------------------------------------------- */
677
0
    const char *pszFilename = poOpenInfo->pszFilename;
678
0
    eCalibration eCalib = None;
679
680
0
    if (STARTS_WITH_CI(pszFilename, "RADARSAT_2_CALIB:"))
681
0
    {
682
0
        pszFilename += 17;
683
684
0
        if (STARTS_WITH_CI(pszFilename, "BETA0"))
685
0
            eCalib = Beta0;
686
0
        else if (STARTS_WITH_CI(pszFilename, "SIGMA0"))
687
0
            eCalib = Sigma0;
688
0
        else if (STARTS_WITH_CI(pszFilename, "GAMMA"))
689
0
            eCalib = Gamma;
690
0
        else if (STARTS_WITH_CI(pszFilename, "UNCALIB"))
691
0
            eCalib = Uncalib;
692
0
        else
693
0
            eCalib = None;
694
695
        /* advance the pointer to the actual filename */
696
0
        while (*pszFilename != '\0' && *pszFilename != ':')
697
0
            pszFilename++;
698
699
0
        if (*pszFilename == ':')
700
0
            pszFilename++;
701
702
        // need to redo the directory check:
703
        // the GDALOpenInfo check would have failed because of the calibration
704
        // string on the filename
705
0
        VSIStatBufL sStat;
706
0
        if (VSIStatL(pszFilename, &sStat) == 0)
707
0
            poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode);
708
0
    }
709
710
0
    CPLString osMDFilename;
711
0
    if (poOpenInfo->bIsDirectory)
712
0
    {
713
0
        osMDFilename =
714
0
            CPLFormCIFilenameSafe(pszFilename, "product.xml", nullptr);
715
0
    }
716
0
    else
717
0
        osMDFilename = pszFilename;
718
719
    /* -------------------------------------------------------------------- */
720
    /*      Ingest the Product.xml file.                                    */
721
    /* -------------------------------------------------------------------- */
722
0
    CPLXMLNode *psProduct = CPLParseXMLFile(osMDFilename);
723
0
    if (psProduct == nullptr)
724
0
        return nullptr;
725
726
    /* -------------------------------------------------------------------- */
727
    /*      Confirm the requested access is supported.                      */
728
    /* -------------------------------------------------------------------- */
729
0
    if (poOpenInfo->eAccess == GA_Update)
730
0
    {
731
0
        CPLDestroyXMLNode(psProduct);
732
0
        ReportUpdateNotSupportedByDriver("RS2");
733
0
        return nullptr;
734
0
    }
735
736
0
    CPLXMLNode *psImageAttributes =
737
0
        CPLGetXMLNode(psProduct, "=product.imageAttributes");
738
0
    if (psImageAttributes == nullptr)
739
0
    {
740
0
        CPLDestroyXMLNode(psProduct);
741
0
        CPLError(CE_Failure, CPLE_OpenFailed,
742
0
                 "Failed to find <imageAttributes> in document.");
743
0
        return nullptr;
744
0
    }
745
746
0
    CPLXMLNode *psImageGenerationParameters =
747
0
        CPLGetXMLNode(psProduct, "=product.imageGenerationParameters");
748
0
    if (psImageGenerationParameters == nullptr)
749
0
    {
750
0
        CPLDestroyXMLNode(psProduct);
751
0
        CPLError(CE_Failure, CPLE_OpenFailed,
752
0
                 "Failed to find <imageGenerationParameters> in document.");
753
0
        return nullptr;
754
0
    }
755
756
    /* -------------------------------------------------------------------- */
757
    /*      Create the dataset.                                             */
758
    /* -------------------------------------------------------------------- */
759
0
    RS2Dataset *poDS = new RS2Dataset();
760
761
0
    poDS->psProduct = psProduct;
762
763
    /* -------------------------------------------------------------------- */
764
    /*      Get overall image information.                                  */
765
    /* -------------------------------------------------------------------- */
766
0
    poDS->nRasterXSize = atoi(CPLGetXMLValue(
767
0
        psImageAttributes, "rasterAttributes.numberOfSamplesPerLine", "-1"));
768
0
    poDS->nRasterYSize = atoi(CPLGetXMLValue(
769
0
        psImageAttributes, "rasterAttributes.numberofLines", "-1"));
770
0
    if (poDS->nRasterXSize <= 1 || poDS->nRasterYSize <= 1)
771
0
    {
772
0
        CPLError(
773
0
            CE_Failure, CPLE_OpenFailed,
774
0
            "Non-sane raster dimensions provided in product.xml. If this is "
775
0
            "a valid RADARSAT-2 scene, please contact your data provider for "
776
0
            "a corrected dataset.");
777
0
        delete poDS;
778
0
        return nullptr;
779
0
    }
780
781
    /* -------------------------------------------------------------------- */
782
    /*      Check product type, as to determine if there are LUTs for       */
783
    /*      calibration purposes.                                           */
784
    /* -------------------------------------------------------------------- */
785
786
0
    const char *pszProductType =
787
0
        CPLGetXMLValue(psImageGenerationParameters,
788
0
                       "generalProcessingInformation.productType", "UNK");
789
790
0
    poDS->SetMetadataItem("PRODUCT_TYPE", pszProductType);
791
792
    /* the following cases can be assumed to have no LUTs, as per
793
     * RN-RP-51-2713, but also common sense
794
     */
795
0
    bool bCanCalib = false;
796
0
    if (!(STARTS_WITH_CI(pszProductType, "UNK") ||
797
0
          STARTS_WITH_CI(pszProductType, "SSG") ||
798
0
          STARTS_WITH_CI(pszProductType, "SPG")))
799
0
    {
800
0
        bCanCalib = true;
801
0
    }
802
803
    /* -------------------------------------------------------------------- */
804
    /*      Get dataType (so we can recognise complex data), and the        */
805
    /*      bitsPerSample.                                                  */
806
    /* -------------------------------------------------------------------- */
807
0
    const char *pszDataType =
808
0
        CPLGetXMLValue(psImageAttributes, "rasterAttributes.dataType", "");
809
0
    const int nBitsPerSample = atoi(CPLGetXMLValue(
810
0
        psImageAttributes, "rasterAttributes.bitsPerSample", ""));
811
812
0
    GDALDataType eDataType;
813
0
    if (nBitsPerSample == 16 && EQUAL(pszDataType, "Complex"))
814
0
        eDataType = GDT_CInt16;
815
0
    else if (nBitsPerSample == 32 &&
816
0
             EQUAL(pszDataType,
817
0
                   "Complex"))  // NITF datasets can come in this configuration
818
0
        eDataType = GDT_CFloat32;
819
0
    else if (nBitsPerSample == 16 && STARTS_WITH_CI(pszDataType, "Mag"))
820
0
        eDataType = GDT_UInt16;
821
0
    else if (nBitsPerSample == 8 && STARTS_WITH_CI(pszDataType, "Mag"))
822
0
        eDataType = GDT_Byte;
823
0
    else
824
0
    {
825
0
        delete poDS;
826
0
        CPLError(
827
0
            CE_Failure, CPLE_AppDefined,
828
0
            "dataType=%s, bitsPerSample=%d: not a supported configuration.",
829
0
            pszDataType, nBitsPerSample);
830
0
        return nullptr;
831
0
    }
832
833
    /* while we're at it, extract the pixel spacing information */
834
0
    const char *pszPixelSpacing = CPLGetXMLValue(
835
0
        psImageAttributes, "rasterAttributes.sampledPixelSpacing", "UNK");
836
0
    poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing);
837
838
0
    const char *pszLineSpacing = CPLGetXMLValue(
839
0
        psImageAttributes, "rasterAttributes.sampledLineSpacing", "UNK");
840
0
    poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing);
841
842
    /* -------------------------------------------------------------------- */
843
    /*      Open each of the data files as a complex band.                  */
844
    /* -------------------------------------------------------------------- */
845
0
    CPLString osBeta0LUT;
846
0
    CPLString osGammaLUT;
847
0
    CPLString osSigma0LUT;
848
849
0
    char *pszPath = CPLStrdup(CPLGetPathSafe(osMDFilename).c_str());
850
0
    const int nFLen = static_cast<int>(osMDFilename.size());
851
852
0
    CPLXMLNode *psNode = psImageAttributes->psChild;
853
0
    for (; psNode != nullptr; psNode = psNode->psNext)
854
0
    {
855
0
        if (psNode->eType != CXT_Element ||
856
0
            !(EQUAL(psNode->pszValue, "fullResolutionImageData") ||
857
0
              EQUAL(psNode->pszValue, "lookupTable")))
858
0
            continue;
859
860
0
        if (EQUAL(psNode->pszValue, "lookupTable") && bCanCalib)
861
0
        {
862
            /* Determine which incidence angle correction this is */
863
0
            const char *pszLUTType =
864
0
                CPLGetXMLValue(psNode, "incidenceAngleCorrection", "");
865
0
            const char *pszLUTFile = CPLGetXMLValue(psNode, "", "");
866
0
            CPLString osLUTFilePath =
867
0
                CPLFormFilenameSafe(pszPath, pszLUTFile, nullptr);
868
869
0
            if (EQUAL(pszLUTType, ""))
870
0
                continue;
871
0
            else if (EQUAL(pszLUTType, "Beta Nought") &&
872
0
                     IsValidXMLFile(pszPath, pszLUTFile))
873
0
            {
874
0
                poDS->papszExtraFiles =
875
0
                    CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
876
877
0
                const size_t nBufLen = nFLen + 27;
878
0
                char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
879
0
                osBeta0LUT = pszLUTFile;
880
0
                poDS->SetMetadataItem("BETA_NOUGHT_LUT", pszLUTFile);
881
882
0
                snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:BETA0:%s",
883
0
                         osMDFilename.c_str());
884
0
                poDS->papszSubDatasets = CSLSetNameValue(
885
0
                    poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf);
886
0
                poDS->papszSubDatasets =
887
0
                    CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_3_DESC",
888
0
                                    "Beta Nought calibrated");
889
0
                CPLFree(pszBuf);
890
0
            }
891
0
            else if (EQUAL(pszLUTType, "Sigma Nought") &&
892
0
                     IsValidXMLFile(pszPath, pszLUTFile))
893
0
            {
894
0
                poDS->papszExtraFiles =
895
0
                    CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
896
897
0
                const size_t nBufLen = nFLen + 27;
898
0
                char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
899
0
                osSigma0LUT = pszLUTFile;
900
0
                poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", pszLUTFile);
901
902
0
                snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:SIGMA0:%s",
903
0
                         osMDFilename.c_str());
904
0
                poDS->papszSubDatasets = CSLSetNameValue(
905
0
                    poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf);
906
0
                poDS->papszSubDatasets =
907
0
                    CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_2_DESC",
908
0
                                    "Sigma Nought calibrated");
909
0
                CPLFree(pszBuf);
910
0
            }
911
0
            else if (EQUAL(pszLUTType, "Gamma") &&
912
0
                     IsValidXMLFile(pszPath, pszLUTFile))
913
0
            {
914
0
                poDS->papszExtraFiles =
915
0
                    CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
916
917
0
                const size_t nBufLen = nFLen + 27;
918
0
                char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
919
0
                osGammaLUT = pszLUTFile;
920
0
                poDS->SetMetadataItem("GAMMA_LUT", pszLUTFile);
921
0
                snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:GAMMA:%s",
922
0
                         osMDFilename.c_str());
923
0
                poDS->papszSubDatasets = CSLSetNameValue(
924
0
                    poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf);
925
0
                poDS->papszSubDatasets =
926
0
                    CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_4_DESC",
927
0
                                    "Gamma calibrated");
928
0
                CPLFree(pszBuf);
929
0
            }
930
0
            continue;
931
0
        }
932
933
        /* --------------------------------------------------------------------
934
         */
935
        /*      Fetch filename. */
936
        /* --------------------------------------------------------------------
937
         */
938
0
        const char *pszBasename = CPLGetXMLValue(psNode, "", "");
939
0
        if (*pszBasename == '\0')
940
0
            continue;
941
942
        /* --------------------------------------------------------------------
943
         */
944
        /*      Form full filename (path of product.xml + basename). */
945
        /* --------------------------------------------------------------------
946
         */
947
0
        char *pszFullname = CPLStrdup(
948
0
            CPLFormFilenameSafe(pszPath, pszBasename, nullptr).c_str());
949
950
        /* --------------------------------------------------------------------
951
         */
952
        /*      Try and open the file. */
953
        /* --------------------------------------------------------------------
954
         */
955
0
        GDALDataset *poBandFile =
956
0
            GDALDataset::FromHandle(GDALOpen(pszFullname, GA_ReadOnly));
957
0
        if (poBandFile == nullptr)
958
0
        {
959
0
            CPLFree(pszFullname);
960
0
            continue;
961
0
        }
962
0
        if (poBandFile->GetRasterCount() == 0)
963
0
        {
964
0
            GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
965
0
            CPLFree(pszFullname);
966
0
            continue;
967
0
        }
968
969
        /* Some CFloat32 NITF files have nBitsPerSample incorrectly reported */
970
        /* as 16, and get misinterpreted as CInt16.  Check the underlying NITF
971
         */
972
        /* and override if this is the case. */
973
0
        if (poBandFile->GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
974
0
            eDataType = GDT_CFloat32;
975
976
0
        BandMapping b = GetBandFileMapping(eDataType, poBandFile);
977
0
        const bool twoBandComplex = b == TWOBANDCOMPLEX;
978
979
0
        poDS->papszExtraFiles =
980
0
            CSLAddString(poDS->papszExtraFiles, pszFullname);
981
982
        /* --------------------------------------------------------------------
983
         */
984
        /*      Create the band. */
985
        /* --------------------------------------------------------------------
986
         */
987
0
        if (eCalib == None || eCalib == Uncalib)
988
0
        {
989
0
            RS2RasterBand *poBand = new RS2RasterBand(
990
0
                poDS, eDataType, CPLGetXMLValue(psNode, "pole", ""), poBandFile,
991
0
                twoBandComplex);
992
993
0
            poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
994
0
        }
995
0
        else
996
0
        {
997
0
            const char *pszLUT = nullptr;
998
0
            switch (eCalib)
999
0
            {
1000
0
                case Sigma0:
1001
0
                    pszLUT = osSigma0LUT;
1002
0
                    break;
1003
0
                case Beta0:
1004
0
                    pszLUT = osBeta0LUT;
1005
0
                    break;
1006
0
                case Gamma:
1007
0
                    pszLUT = osGammaLUT;
1008
0
                    break;
1009
0
                default:
1010
                    /* we should bomb gracefully... */
1011
0
                    pszLUT = osSigma0LUT;
1012
0
            }
1013
0
            RS2CalibRasterBand *poBand = new RS2CalibRasterBand(
1014
0
                poDS, CPLGetXMLValue(psNode, "pole", ""), eDataType, poBandFile,
1015
0
                eCalib, CPLFormFilenameSafe(pszPath, pszLUT, nullptr).c_str());
1016
0
            poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
1017
0
        }
1018
1019
0
        CPLFree(pszFullname);
1020
0
    }
1021
1022
0
    if (poDS->papszSubDatasets != nullptr && eCalib == None)
1023
0
    {
1024
0
        const size_t nBufLen = nFLen + 28;
1025
0
        char *pszBuf = reinterpret_cast<char *>(CPLMalloc(nBufLen));
1026
0
        snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:UNCALIB:%s",
1027
0
                 osMDFilename.c_str());
1028
0
        poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
1029
0
                                                 "SUBDATASET_1_NAME", pszBuf);
1030
0
        poDS->papszSubDatasets =
1031
0
            CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_1_DESC",
1032
0
                            "Uncalibrated digital numbers");
1033
0
        CPLFree(pszBuf);
1034
0
    }
1035
0
    else if (poDS->papszSubDatasets != nullptr)
1036
0
    {
1037
0
        CSLDestroy(poDS->papszSubDatasets);
1038
0
        poDS->papszSubDatasets = nullptr;
1039
0
    }
1040
1041
    /* -------------------------------------------------------------------- */
1042
    /*      Set the appropriate MATRIX_REPRESENTATION.                      */
1043
    /* -------------------------------------------------------------------- */
1044
1045
0
    if (poDS->GetRasterCount() == 4 &&
1046
0
        (eDataType == GDT_CInt16 || eDataType == GDT_CFloat32))
1047
0
    {
1048
0
        poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
1049
0
    }
1050
1051
    /* -------------------------------------------------------------------- */
1052
    /*      Collect a few useful metadata items                             */
1053
    /* -------------------------------------------------------------------- */
1054
1055
0
    CPLXMLNode *psSourceAttrs =
1056
0
        CPLGetXMLNode(psProduct, "=product.sourceAttributes");
1057
0
    const char *pszItem = CPLGetXMLValue(psSourceAttrs, "satellite", "");
1058
0
    poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem);
1059
1060
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "sensor", "");
1061
0
    poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem);
1062
1063
0
    if (psSourceAttrs != nullptr)
1064
0
    {
1065
        /* Get beam mode mnemonic */
1066
0
        pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeMnemonic", "UNK");
1067
0
        poDS->SetMetadataItem("BEAM_MODE", pszItem);
1068
0
        pszItem = CPLGetXMLValue(psSourceAttrs, "rawDataStartTime", "UNK");
1069
0
        poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem);
1070
1071
0
        pszItem =
1072
0
            CPLGetXMLValue(psSourceAttrs, "inputDatasetFacilityId", "UNK");
1073
0
        poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem);
1074
1075
0
        pszItem = CPLGetXMLValue(
1076
0
            psSourceAttrs, "orbitAndAttitude.orbitInformation.passDirection",
1077
0
            "UNK");
1078
0
        poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem);
1079
0
        pszItem = CPLGetXMLValue(
1080
0
            psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataSource",
1081
0
            "UNK");
1082
0
        poDS->SetMetadataItem("ORBIT_DATA_SOURCE", pszItem);
1083
0
        pszItem = CPLGetXMLValue(
1084
0
            psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataFile",
1085
0
            "UNK");
1086
0
        poDS->SetMetadataItem("ORBIT_DATA_FILE", pszItem);
1087
0
    }
1088
1089
0
    CPLXMLNode *psSarProcessingInformation =
1090
0
        CPLGetXMLNode(psProduct, "=product.imageGenerationParameters");
1091
1092
0
    if (psSarProcessingInformation != nullptr)
1093
0
    {
1094
        /* Get incidence angle information */
1095
0
        pszItem = CPLGetXMLValue(
1096
0
            psSarProcessingInformation,
1097
0
            "sarProcessingInformation.incidenceAngleNearRange", "UNK");
1098
0
        poDS->SetMetadataItem("NEAR_RANGE_INCIDENCE_ANGLE", pszItem);
1099
1100
0
        pszItem = CPLGetXMLValue(
1101
0
            psSarProcessingInformation,
1102
0
            "sarProcessingInformation.incidenceAngleFarRange", "UNK");
1103
0
        poDS->SetMetadataItem("FAR_RANGE_INCIDENCE_ANGLE", pszItem);
1104
1105
0
        pszItem = CPLGetXMLValue(psSarProcessingInformation,
1106
0
                                 "sarProcessingInformation.slantRangeNearEdge",
1107
0
                                 "UNK");
1108
0
        poDS->SetMetadataItem("SLANT_RANGE_NEAR_EDGE", pszItem);
1109
1110
0
        pszItem = CPLGetXMLValue(
1111
0
            psSarProcessingInformation,
1112
0
            "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK");
1113
0
        poDS->SetMetadataItem("FIRST_LINE_TIME", pszItem);
1114
1115
0
        pszItem = CPLGetXMLValue(
1116
0
            psSarProcessingInformation,
1117
0
            "sarProcessingInformation.zeroDopplerTimeLastLine", "UNK");
1118
0
        poDS->SetMetadataItem("LAST_LINE_TIME", pszItem);
1119
1120
0
        pszItem =
1121
0
            CPLGetXMLValue(psSarProcessingInformation,
1122
0
                           "generalProcessingInformation.productType", "UNK");
1123
0
        poDS->SetMetadataItem("PRODUCT_TYPE", pszItem);
1124
1125
0
        pszItem = CPLGetXMLValue(
1126
0
            psSarProcessingInformation,
1127
0
            "generalProcessingInformation.processingFacility", "UNK");
1128
0
        poDS->SetMetadataItem("PROCESSING_FACILITY", pszItem);
1129
1130
0
        pszItem = CPLGetXMLValue(psSarProcessingInformation,
1131
0
                                 "generalProcessingInformation.processingTime",
1132
0
                                 "UNK");
1133
0
        poDS->SetMetadataItem("PROCESSING_TIME", pszItem);
1134
0
    }
1135
1136
    /*--------------------------------------------------------------------- */
1137
    /*      Collect Map projection/Geotransform information, if present     */
1138
    /* -------------------------------------------------------------------- */
1139
0
    CPLXMLNode *psMapProjection =
1140
0
        CPLGetXMLNode(psImageAttributes, "geographicInformation.mapProjection");
1141
1142
0
    if (psMapProjection != nullptr)
1143
0
    {
1144
0
        CPLXMLNode *psPos =
1145
0
            CPLGetXMLNode(psMapProjection, "positioningInformation");
1146
1147
0
        pszItem =
1148
0
            CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "UNK");
1149
0
        poDS->SetMetadataItem("MAP_PROJECTION_DESCRIPTOR", pszItem);
1150
1151
0
        pszItem =
1152
0
            CPLGetXMLValue(psMapProjection, "mapProjectionOrientation", "UNK");
1153
0
        poDS->SetMetadataItem("MAP_PROJECTION_ORIENTATION", pszItem);
1154
1155
0
        pszItem = CPLGetXMLValue(psMapProjection, "resamplingKernel", "UNK");
1156
0
        poDS->SetMetadataItem("RESAMPLING_KERNEL", pszItem);
1157
1158
0
        pszItem = CPLGetXMLValue(psMapProjection, "satelliteHeading", "UNK");
1159
0
        poDS->SetMetadataItem("SATELLITE_HEADING", pszItem);
1160
1161
0
        if (psPos != nullptr)
1162
0
        {
1163
0
            const double tl_x = CPLStrtod(
1164
0
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.easting",
1165
0
                               "0.0"),
1166
0
                nullptr);
1167
0
            const double tl_y = CPLStrtod(
1168
0
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.northing",
1169
0
                               "0.0"),
1170
0
                nullptr);
1171
0
            const double bl_x = CPLStrtod(
1172
0
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.easting",
1173
0
                               "0.0"),
1174
0
                nullptr);
1175
0
            const double bl_y = CPLStrtod(
1176
0
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.northing",
1177
0
                               "0.0"),
1178
0
                nullptr);
1179
0
            const double tr_x = CPLStrtod(
1180
0
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.easting",
1181
0
                               "0.0"),
1182
0
                nullptr);
1183
0
            const double tr_y = CPLStrtod(
1184
0
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.northing",
1185
0
                               "0.0"),
1186
0
                nullptr);
1187
0
            poDS->adfGeoTransform[1] = (tr_x - tl_x) / (poDS->nRasterXSize - 1);
1188
0
            poDS->adfGeoTransform[4] = (tr_y - tl_y) / (poDS->nRasterXSize - 1);
1189
0
            poDS->adfGeoTransform[2] = (bl_x - tl_x) / (poDS->nRasterYSize - 1);
1190
0
            poDS->adfGeoTransform[5] = (bl_y - tl_y) / (poDS->nRasterYSize - 1);
1191
0
            poDS->adfGeoTransform[0] = (tl_x - 0.5 * poDS->adfGeoTransform[1] -
1192
0
                                        0.5 * poDS->adfGeoTransform[2]);
1193
0
            poDS->adfGeoTransform[3] = (tl_y - 0.5 * poDS->adfGeoTransform[4] -
1194
0
                                        0.5 * poDS->adfGeoTransform[5]);
1195
1196
            /* Use bottom right pixel to test geotransform */
1197
0
            const double br_x = CPLStrtod(
1198
0
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.easting",
1199
0
                               "0.0"),
1200
0
                nullptr);
1201
0
            const double br_y = CPLStrtod(
1202
0
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.northing",
1203
0
                               "0.0"),
1204
0
                nullptr);
1205
0
            const double testx =
1206
0
                poDS->adfGeoTransform[0] +
1207
0
                poDS->adfGeoTransform[1] * (poDS->nRasterXSize - 0.5) +
1208
0
                poDS->adfGeoTransform[2] * (poDS->nRasterYSize - 0.5);
1209
0
            const double testy =
1210
0
                poDS->adfGeoTransform[3] +
1211
0
                poDS->adfGeoTransform[4] * (poDS->nRasterXSize - 0.5) +
1212
0
                poDS->adfGeoTransform[5] * (poDS->nRasterYSize - 0.5);
1213
1214
            /* Give 1/4 pixel numerical error leeway in calculating location
1215
               based on affine transform */
1216
0
            if ((fabs(testx - br_x) >
1217
0
                 fabs(0.25 *
1218
0
                      (poDS->adfGeoTransform[1] + poDS->adfGeoTransform[2]))) ||
1219
0
                (fabs(testy - br_y) > fabs(0.25 * (poDS->adfGeoTransform[4] +
1220
0
                                                   poDS->adfGeoTransform[5]))))
1221
0
            {
1222
0
                CPLError(CE_Warning, CPLE_AppDefined,
1223
0
                         "Unexpected error in calculating affine transform: "
1224
0
                         "corner coordinates inconsistent.");
1225
0
            }
1226
0
            else
1227
0
            {
1228
0
                poDS->bHaveGeoTransform = TRUE;
1229
0
            }
1230
0
        }
1231
0
    }
1232
1233
    /* -------------------------------------------------------------------- */
1234
    /*      Collect Projection String Information                           */
1235
    /* -------------------------------------------------------------------- */
1236
0
    CPLXMLNode *psEllipsoid =
1237
0
        CPLGetXMLNode(psImageAttributes,
1238
0
                      "geographicInformation.referenceEllipsoidParameters");
1239
1240
0
    if (psEllipsoid != nullptr)
1241
0
    {
1242
0
        OGRSpatialReference oLL, oPrj;
1243
0
        oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1244
0
        oPrj.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1245
1246
0
        const char *pszGeodeticTerrainHeight =
1247
0
            CPLGetXMLValue(psEllipsoid, "geodeticTerrainHeight", "UNK");
1248
0
        poDS->SetMetadataItem("GEODETIC_TERRAIN_HEIGHT",
1249
0
                              pszGeodeticTerrainHeight);
1250
1251
0
        const char *pszEllipsoidName =
1252
0
            CPLGetXMLValue(psEllipsoid, "ellipsoidName", "");
1253
0
        double minor_axis =
1254
0
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMinorAxis", "0.0"));
1255
0
        double major_axis =
1256
0
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMajorAxis", "0.0"));
1257
1258
0
        if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) ||
1259
0
            (major_axis == 0.0))
1260
0
        {
1261
0
            CPLError(CE_Warning, CPLE_AppDefined,
1262
0
                     "Warning- incomplete"
1263
0
                     " ellipsoid information.  Using wgs-84 parameters.\n");
1264
0
            oLL.SetWellKnownGeogCS("WGS84");
1265
0
            oPrj.SetWellKnownGeogCS("WGS84");
1266
0
        }
1267
0
        else if (EQUAL(pszEllipsoidName, "WGS84") ||
1268
0
                 EQUAL(pszEllipsoidName, "WGS 1984"))
1269
0
        {
1270
0
            oLL.SetWellKnownGeogCS("WGS84");
1271
0
            oPrj.SetWellKnownGeogCS("WGS84");
1272
0
        }
1273
0
        else
1274
0
        {
1275
0
            const double inv_flattening =
1276
0
                major_axis / (major_axis - minor_axis);
1277
0
            oLL.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening);
1278
0
            oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis,
1279
0
                           inv_flattening);
1280
0
        }
1281
1282
0
        if (psMapProjection != nullptr)
1283
0
        {
1284
0
            const char *pszProj =
1285
0
                CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "");
1286
0
            bool bUseProjInfo = false;
1287
1288
0
            CPLXMLNode *psUtmParams =
1289
0
                CPLGetXMLNode(psMapProjection, "utmProjectionParameters");
1290
1291
0
            CPLXMLNode *psNspParams =
1292
0
                CPLGetXMLNode(psMapProjection, "nspProjectionParameters");
1293
1294
0
            if ((psUtmParams != nullptr) && poDS->bHaveGeoTransform)
1295
0
            {
1296
                /* double origEasting, origNorthing; */
1297
0
                bool bNorth = true;
1298
1299
0
                const int utmZone =
1300
0
                    atoi(CPLGetXMLValue(psUtmParams, "utmZone", ""));
1301
0
                const char *pszHemisphere =
1302
0
                    CPLGetXMLValue(psUtmParams, "hemisphere", "");
1303
#if 0
1304
                origEasting = CPLStrtod(CPLGetXMLValue(
1305
                    psUtmParams, "mapOriginFalseEasting", "0.0" ), nullptr);
1306
                origNorthing = CPLStrtod(CPLGetXMLValue(
1307
                    psUtmParams, "mapOriginFalseNorthing", "0.0" ), nullptr);
1308
#endif
1309
0
                if (STARTS_WITH_CI(pszHemisphere, "southern"))
1310
0
                    bNorth = FALSE;
1311
1312
0
                if (STARTS_WITH_CI(pszProj, "UTM"))
1313
0
                {
1314
0
                    oPrj.SetUTM(utmZone, bNorth);
1315
0
                    bUseProjInfo = true;
1316
0
                }
1317
0
            }
1318
0
            else if ((psNspParams != nullptr) && poDS->bHaveGeoTransform)
1319
0
            {
1320
0
                const double origEasting = CPLStrtod(
1321
0
                    CPLGetXMLValue(psNspParams, "mapOriginFalseEasting", "0.0"),
1322
0
                    nullptr);
1323
0
                const double origNorthing =
1324
0
                    CPLStrtod(CPLGetXMLValue(psNspParams,
1325
0
                                             "mapOriginFalseNorthing", "0.0"),
1326
0
                              nullptr);
1327
0
                const double copLong = CPLStrtod(
1328
0
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLongitude",
1329
0
                                   "0.0"),
1330
0
                    nullptr);
1331
0
                const double copLat = CPLStrtod(
1332
0
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLatitude",
1333
0
                                   "0.0"),
1334
0
                    nullptr);
1335
0
                const double sP1 = CPLStrtod(
1336
0
                    CPLGetXMLValue(psNspParams, "standardParallels1", "0.0"),
1337
0
                    nullptr);
1338
0
                const double sP2 = CPLStrtod(
1339
0
                    CPLGetXMLValue(psNspParams, "standardParallels2", "0.0"),
1340
0
                    nullptr);
1341
1342
0
                if (STARTS_WITH_CI(pszProj, "ARC"))
1343
0
                {
1344
                    /* Albers Conical Equal Area */
1345
0
                    oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting,
1346
0
                                 origNorthing);
1347
0
                    bUseProjInfo = true;
1348
0
                }
1349
0
                else if (STARTS_WITH_CI(pszProj, "LCC"))
1350
0
                {
1351
                    /* Lambert Conformal Conic */
1352
0
                    oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting,
1353
0
                                origNorthing);
1354
0
                    bUseProjInfo = true;
1355
0
                }
1356
0
                else if (STARTS_WITH_CI(pszProj, "STPL"))
1357
0
                {
1358
                    /* State Plate
1359
                       ASSUMPTIONS: "zone" in product.xml matches USGS
1360
                       definition as expected by ogr spatial reference; NAD83
1361
                       zones (versus NAD27) are assumed. */
1362
1363
0
                    const int nSPZone =
1364
0
                        atoi(CPLGetXMLValue(psNspParams, "zone", "1"));
1365
1366
0
                    oPrj.SetStatePlane(nSPZone, TRUE, nullptr, 0.0);
1367
0
                    bUseProjInfo = true;
1368
0
                }
1369
0
            }
1370
1371
0
            if (bUseProjInfo)
1372
0
            {
1373
0
                poDS->m_oSRS = std::move(oPrj);
1374
0
            }
1375
0
            else
1376
0
            {
1377
0
                CPLError(CE_Warning, CPLE_AppDefined,
1378
0
                         "Unable to interpret "
1379
0
                         "projection information; check mapProjection info in "
1380
0
                         "product.xml!");
1381
0
            }
1382
0
        }
1383
1384
0
        poDS->m_oGCPSRS = std::move(oLL);
1385
0
    }
1386
1387
    /* -------------------------------------------------------------------- */
1388
    /*      Collect GCPs.                                                   */
1389
    /* -------------------------------------------------------------------- */
1390
0
    CPLXMLNode *psGeoGrid = CPLGetXMLNode(
1391
0
        psImageAttributes, "geographicInformation.geolocationGrid");
1392
1393
0
    if (psGeoGrid != nullptr)
1394
0
    {
1395
        /* count GCPs */
1396
0
        poDS->nGCPCount = 0;
1397
1398
0
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
1399
0
             psNode = psNode->psNext)
1400
0
        {
1401
0
            if (EQUAL(psNode->pszValue, "imageTiePoint"))
1402
0
                poDS->nGCPCount++;
1403
0
        }
1404
1405
0
        poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>(
1406
0
            CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount));
1407
1408
0
        poDS->nGCPCount = 0;
1409
1410
0
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
1411
0
             psNode = psNode->psNext)
1412
0
        {
1413
0
            GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount;
1414
1415
0
            if (!EQUAL(psNode->pszValue, "imageTiePoint"))
1416
0
                continue;
1417
1418
0
            poDS->nGCPCount++;
1419
1420
0
            char szID[32];
1421
0
            snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount);
1422
0
            psGCP->pszId = CPLStrdup(szID);
1423
0
            psGCP->pszInfo = CPLStrdup("");
1424
0
            psGCP->dfGCPPixel =
1425
0
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.pixel", "0")) +
1426
0
                0.5;
1427
0
            psGCP->dfGCPLine =
1428
0
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.line", "0")) +
1429
0
                0.5;
1430
0
            psGCP->dfGCPX = CPLAtof(
1431
0
                CPLGetXMLValue(psNode, "geodeticCoordinate.longitude", ""));
1432
0
            psGCP->dfGCPY = CPLAtof(
1433
0
                CPLGetXMLValue(psNode, "geodeticCoordinate.latitude", ""));
1434
0
            psGCP->dfGCPZ = CPLAtof(
1435
0
                CPLGetXMLValue(psNode, "geodeticCoordinate.height", ""));
1436
0
        }
1437
0
    }
1438
1439
0
    CPLFree(pszPath);
1440
1441
    /* -------------------------------------------------------------------- */
1442
    /*      Collect RPC.                                                   */
1443
    /* -------------------------------------------------------------------- */
1444
0
    CPLXMLNode *psRationalFunctions = CPLGetXMLNode(
1445
0
        psImageAttributes, "geographicInformation.rationalFunctions");
1446
0
    if (psRationalFunctions != nullptr)
1447
0
    {
1448
0
        char **papszRPC = nullptr;
1449
0
        static const char *const apszXMLToGDALMapping[] = {
1450
0
            "biasError",
1451
0
            "ERR_BIAS",
1452
0
            "randomError",
1453
0
            "ERR_RAND",
1454
            //"lineFitQuality", "????",
1455
            //"pixelFitQuality", "????",
1456
0
            "lineOffset",
1457
0
            "LINE_OFF",
1458
0
            "pixelOffset",
1459
0
            "SAMP_OFF",
1460
0
            "latitudeOffset",
1461
0
            "LAT_OFF",
1462
0
            "longitudeOffset",
1463
0
            "LONG_OFF",
1464
0
            "heightOffset",
1465
0
            "HEIGHT_OFF",
1466
0
            "lineScale",
1467
0
            "LINE_SCALE",
1468
0
            "pixelScale",
1469
0
            "SAMP_SCALE",
1470
0
            "latitudeScale",
1471
0
            "LAT_SCALE",
1472
0
            "longitudeScale",
1473
0
            "LONG_SCALE",
1474
0
            "heightScale",
1475
0
            "HEIGHT_SCALE",
1476
0
            "lineNumeratorCoefficients",
1477
0
            "LINE_NUM_COEFF",
1478
0
            "lineDenominatorCoefficients",
1479
0
            "LINE_DEN_COEFF",
1480
0
            "pixelNumeratorCoefficients",
1481
0
            "SAMP_NUM_COEFF",
1482
0
            "pixelDenominatorCoefficients",
1483
0
            "SAMP_DEN_COEFF",
1484
0
        };
1485
0
        for (size_t i = 0; i < CPL_ARRAYSIZE(apszXMLToGDALMapping); i += 2)
1486
0
        {
1487
0
            const char *pszValue = CPLGetXMLValue(
1488
0
                psRationalFunctions, apszXMLToGDALMapping[i], nullptr);
1489
0
            if (pszValue)
1490
0
                papszRPC = CSLSetNameValue(
1491
0
                    papszRPC, apszXMLToGDALMapping[i + 1], pszValue);
1492
0
        }
1493
0
        poDS->GDALDataset::SetMetadata(papszRPC, "RPC");
1494
0
        CSLDestroy(papszRPC);
1495
0
    }
1496
1497
    /* -------------------------------------------------------------------- */
1498
    /*      Initialize any PAM information.                                 */
1499
    /* -------------------------------------------------------------------- */
1500
0
    CPLString osDescription;
1501
1502
0
    switch (eCalib)
1503
0
    {
1504
0
        case Sigma0:
1505
0
            osDescription.Printf("RADARSAT_2_CALIB:SIGMA0:%s",
1506
0
                                 osMDFilename.c_str());
1507
0
            break;
1508
0
        case Beta0:
1509
0
            osDescription.Printf("RADARSAT_2_CALIB:BETA0:%s",
1510
0
                                 osMDFilename.c_str());
1511
0
            break;
1512
0
        case Gamma:
1513
0
            osDescription.Printf("RADARSAT_2_CALIB:GAMMA0:%s",
1514
0
                                 osMDFilename.c_str());
1515
0
            break;
1516
0
        case Uncalib:
1517
0
            osDescription.Printf("RADARSAT_2_CALIB:UNCALIB:%s",
1518
0
                                 osMDFilename.c_str());
1519
0
            break;
1520
0
        default:
1521
0
            osDescription = osMDFilename;
1522
0
    }
1523
1524
0
    if (eCalib != None)
1525
0
        poDS->papszExtraFiles =
1526
0
            CSLAddString(poDS->papszExtraFiles, osMDFilename);
1527
1528
    /* -------------------------------------------------------------------- */
1529
    /*      Initialize any PAM information.                                 */
1530
    /* -------------------------------------------------------------------- */
1531
0
    poDS->SetDescription(osDescription);
1532
1533
0
    poDS->SetPhysicalFilename(osMDFilename);
1534
0
    poDS->SetSubdatasetName(osDescription);
1535
1536
0
    poDS->TryLoadXML();
1537
1538
    /* -------------------------------------------------------------------- */
1539
    /*      Check for overviews.                                            */
1540
    /* -------------------------------------------------------------------- */
1541
0
    poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
1542
1543
0
    return poDS;
1544
0
}
1545
1546
/************************************************************************/
1547
/*                            GetGCPCount()                             */
1548
/************************************************************************/
1549
1550
int RS2Dataset::GetGCPCount()
1551
1552
0
{
1553
0
    return nGCPCount;
1554
0
}
1555
1556
/************************************************************************/
1557
/*                          GetGCPSpatialRef()                          */
1558
/************************************************************************/
1559
1560
const OGRSpatialReference *RS2Dataset::GetGCPSpatialRef() const
1561
1562
0
{
1563
0
    return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
1564
0
}
1565
1566
/************************************************************************/
1567
/*                               GetGCPs()                              */
1568
/************************************************************************/
1569
1570
const GDAL_GCP *RS2Dataset::GetGCPs()
1571
1572
0
{
1573
0
    return pasGCPList;
1574
0
}
1575
1576
/************************************************************************/
1577
/*                          GetSpatialRef()                             */
1578
/************************************************************************/
1579
1580
const OGRSpatialReference *RS2Dataset::GetSpatialRef() const
1581
1582
0
{
1583
0
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
1584
0
}
1585
1586
/************************************************************************/
1587
/*                          GetGeoTransform()                           */
1588
/************************************************************************/
1589
1590
CPLErr RS2Dataset::GetGeoTransform(double *padfTransform)
1591
1592
0
{
1593
0
    memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
1594
1595
0
    if (bHaveGeoTransform)
1596
0
        return CE_None;
1597
1598
0
    return CE_Failure;
1599
0
}
1600
1601
/************************************************************************/
1602
/*                      GetMetadataDomainList()                         */
1603
/************************************************************************/
1604
1605
char **RS2Dataset::GetMetadataDomainList()
1606
0
{
1607
0
    return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
1608
0
                                   "SUBDATASETS", nullptr);
1609
0
}
1610
1611
/************************************************************************/
1612
/*                            GetMetadata()                             */
1613
/************************************************************************/
1614
1615
char **RS2Dataset::GetMetadata(const char *pszDomain)
1616
1617
0
{
1618
0
    if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS") &&
1619
0
        papszSubDatasets != nullptr)
1620
0
        return papszSubDatasets;
1621
1622
0
    return GDALDataset::GetMetadata(pszDomain);
1623
0
}
1624
1625
/************************************************************************/
1626
/*                         GDALRegister_RS2()                          */
1627
/************************************************************************/
1628
1629
void GDALRegister_RS2()
1630
1631
24
{
1632
24
    if (GDALGetDriverByName("RS2") != nullptr)
1633
0
        return;
1634
1635
24
    GDALDriver *poDriver = new GDALDriver();
1636
1637
24
    poDriver->SetDescription("RS2");
1638
24
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1639
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "RadarSat 2 XML Product");
1640
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rs2.html");
1641
24
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1642
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1643
1644
24
    poDriver->pfnOpen = RS2Dataset::Open;
1645
24
    poDriver->pfnIdentify = RS2Dataset::Identify;
1646
1647
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
1648
24
}