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

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

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

64.56
/frmts/rcm/rcmdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  DRDC Ottawa GEOINT
4
 * Purpose:  Radarsat Constellation Mission - XML Products (product.xml) driver
5
 * Author:   Roberto Caron, MDA
6
 *           on behalf of DRDC Ottawa
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2020, DRDC Ottawa
10
 *
11
 * Based on the RS2 Dataset Class
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15

16
#include <time.h>
17
#include <stdio.h>
18
#include <sstream>
19

20
#include "cpl_minixml.h"
21
#include "gdal_frmts.h"
22
#include "gdal_pam.h"
23
#include "ogr_spatialref.h"
24
#include "rcmdataset.h"
25
#include "rcmdrivercore.h"
26

27
#include <limits>
28

29
constexpr int max_space_for_string = 32;
30

31
/* RCM has a special folder that contains all LUT, Incidence Angle and Noise
32
 * Level files */
33
constexpr const char *CALIBRATION_FOLDER = "calibration";
34

35
/*** Function to format calibration for unique identification for Layer Name
36
 * ***/
37
/*
38
 *  RCM_CALIB : { SIGMA0 | GAMMA0 | BETA0 | UNCALIB } : product.xml full path
39
 */
40
inline CPLString FormatCalibration(const char *pszCalibName,
56✔
41
                                   const char *pszFilename)
42
{
43
    CPLString ptr;
56✔
44

45
    // Always begin by the layer calibration name
46
    ptr.append(szLayerCalibration);
56✔
47

48
    // A separator is needed before concat calibration name
49
    ptr += chLayerSeparator;
56✔
50
    // Add calibration name
51
    ptr.append(pszCalibName);
56✔
52

53
    // A separator is needed before concat full filename name
54
    ptr += chLayerSeparator;
56✔
55
    // Add full filename name
56
    ptr.append(pszFilename);
56✔
57

58
    /* return calibration format */
59
    return ptr;
56✔
60
}
61

62
/*** Function to test for valid LUT files ***/
63
static bool IsValidXMLFile(const char *pszPath)
72✔
64
{
65
    /* Return true for valid xml file, false otherwise */
66
    CPLXMLTreeCloser psLut(CPLParseXMLFile(pszPath));
72✔
67

68
    if (psLut.get() == nullptr)
72✔
69
    {
70
        CPLError(CE_Failure, CPLE_OpenFailed,
×
71
                 "ERROR: Failed to open the LUT file %s", pszPath);
72
    }
73

74
    return psLut.get() != nullptr;
144✔
75
}
76

77
static double *InterpolateValues(CSLConstList papszList, int tableSize,
14✔
78
                                 int stepSize, int numberOfValues,
79
                                 int pixelFirstLutValue)
80
{
81
    /* Allocate the right LUT size according to the product range pixel */
82
    double *table =
83
        static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), tableSize));
14✔
84
    if (!table)
14✔
85
        return nullptr;
×
86

87
    if (stepSize <= 0)
14✔
88
    {
89
        /* When negative, the range of pixel is calculated from the opposite
90
         * starting from the end of gains array */
91
        /* Just step the range with positive value */
92
        const int positiveStepSize = abs(stepSize);
14✔
93

94
        int k = 0;
14✔
95

96
        if (positiveStepSize == 1)
14✔
97
        {
98
            // Be fast and just copy the values because all gain values
99
            // represent all image wide pixel
100
            /* Start at the end position and store in the opposite */
101
            for (int i = pixelFirstLutValue; i >= 0; i--)
×
102
            {
103
                const double value = CPLAtof(papszList[i]);
×
104
                table[k++] = value;
×
105
            }
106
        }
107
        else
108
        {
109

110
            /* Interpolation between 2 numbers */
111
            for (int i = numberOfValues - 1; i >= 0; i--)
422✔
112
            {
113
                // We will consider the same value to cover the case that we
114
                // will hit the last pixel
115
                double valueFrom = CPLAtof(papszList[i]);
408✔
116
                double valueTo = valueFrom;
408✔
117

118
                if (i > 0)
408✔
119
                {
120
                    // We have room to pick the previous number to interpolate
121
                    // with
122
                    valueTo = CPLAtof(papszList[i - 1]);
394✔
123
                }
124

125
                // If the valueFrom minus ValueTo equal 0, it means to finish
126
                // off with the same number until the end of the table size
127
                double interp = (valueTo - valueFrom) / positiveStepSize;
408✔
128

129
                // Always begin with the value FROM found
130
                table[k++] = valueFrom;
408✔
131

132
                // Then add interpolation, don't forget. The stepSize is
133
                // actually counting our valueFrom number thus we add
134
                // interpolation until the last step - 1
135
                for (int j = 0; j < positiveStepSize - 1; j++)
116,341✔
136
                {
137
                    valueFrom += interp;
115,933✔
138
                    table[k++] = valueFrom;
115,933✔
139
                }
140
            }
141
        }
142
    }
143
    else
144
    {
145
        /* When positive, the range of pixel is calculated from the beginning of
146
         * gains array */
147
        if (stepSize == 1)
×
148
        {
149
            // Be fast and just copy the values because all gain values
150
            // represent all image wide pixel
151
            for (int i = 0; i < numberOfValues; i++)
×
152
            {
153
                const double value = CPLAtof(papszList[i]);
×
154
                table[i] = value;
×
155
            }
156
        }
157
        else
158
        {
159
            /* Interpolation between 2 numbers */
160
            int k = 0;
×
161
            for (int i = 0; i < numberOfValues; i++)
×
162
            {
163
                // We will consider the same value to cover the case that we
164
                // will hit the last pixel
165
                double valueFrom = CPLAtof(papszList[i]);
×
166
                double valueTo = valueFrom;
×
167

168
                if (i < (numberOfValues)-1)
×
169
                {
170
                    // We have room to pick the next number to interpolate with
171
                    valueTo = CPLAtof(papszList[i + 1]);
×
172
                }
173

174
                // If the valueFrom minus ValueTo equal 0, it means to finish
175
                // off with the same number until the end of the table size
176
                double interp = (valueTo - valueFrom) / stepSize;
×
177

178
                // Always begin with the value FROM found
179
                table[k++] = valueFrom;
×
180

181
                // Then add interpolation, don't forget. The stepSize is
182
                // actually counting our valueFrom number thus we add
183
                // interpolation until the last step - 1
184
                for (int j = 0; j < stepSize - 1; j++)
×
185
                {
186
                    valueFrom += interp;
×
187
                    table[k++] = valueFrom;
×
188
                }
189
            }
190
        }
191
    }
192

193
    return table;
14✔
194
}
195

196
/*** check that the referenced dataset for each band has the
197
correct data type and returns whether a 2 band I+Q dataset should
198
be mapped onto a single complex band.
199
Returns BANDERROR for error, STRAIGHT for 1:1 mapping, TWOBANDCOMPLEX for 2
200
bands -> 1 complex band
201
*/
202
typedef enum
203
{
204
    BANDERROR,
205
    STRAIGHT,
206
    TWOBANDCOMPLEX
207
} BandMappingRCM;
208

209
static BandMappingRCM checkBandFileMappingRCM(GDALDataType dataType,
16✔
210
                                              GDALDataset *poBandFile,
211
                                              bool isNITF)
212
{
213

214
    GDALRasterBand *band1 = poBandFile->GetRasterBand(1);
16✔
215
    GDALDataType bandfileType = band1->GetRasterDataType();
16✔
216
    // if there is one band and it has the same datatype, the band file gets
217
    // passed straight through
218
    if ((poBandFile->GetRasterCount() == 1 ||
16✔
219
         poBandFile->GetRasterCount() == 4) &&
16✔
220
        dataType == bandfileType)
221
        return STRAIGHT;
16✔
222

223
    // if the band file has 2 bands, they should represent I+Q
224
    // and be a compatible data type
225
    if (poBandFile->GetRasterCount() == 2 && GDALDataTypeIsComplex(dataType))
×
226
    {
227
        GDALRasterBand *band2 = poBandFile->GetRasterBand(2);
×
228

229
        if (bandfileType != band2->GetRasterDataType())
×
230
            return BANDERROR;  // both bands must be same datatype
×
231

232
        // check compatible types - there are 4 complex types in GDAL
233
        if ((dataType == GDT_CInt16 && bandfileType == GDT_Int16) ||
×
234
            (dataType == GDT_CInt32 && bandfileType == GDT_Int32) ||
×
235
            (dataType == GDT_CFloat32 && bandfileType == GDT_Float32) ||
×
236
            (dataType == GDT_CFloat64 && bandfileType == GDT_Float64))
×
237
            return TWOBANDCOMPLEX;
×
238

239
        if ((dataType == GDT_CInt16 && bandfileType == GDT_CInt16) ||
×
240
            (dataType == GDT_CInt32 && bandfileType == GDT_CInt32) ||
×
241
            (dataType == GDT_CFloat32 && bandfileType == GDT_CFloat32) ||
×
242
            (dataType == GDT_CFloat64 && bandfileType == GDT_CFloat64))
×
243
            return TWOBANDCOMPLEX;
×
244
    }
245

246
    if (isNITF)
×
247
    {
248
        return STRAIGHT;
×
249
    }
250

251
    return BANDERROR;  // don't accept any other combinations
×
252
}
253

254
/************************************************************************/
255
/*                            RCMRasterBand                             */
256
/************************************************************************/
257

258
RCMRasterBand::RCMRasterBand(RCMDataset *poDSIn, int nBandIn,
10✔
259
                             GDALDataType eDataTypeIn, const char *pszPole,
260
                             GDALDataset *poBandFileIn, bool bTwoBandComplex,
261
                             bool isOneFilePerPolIn, bool isNITFIn)
10✔
262
    : poBandFile(poBandFileIn), poRCMDataset(poDSIn),
263
      twoBandComplex(bTwoBandComplex), isOneFilePerPol(isOneFilePerPolIn),
264
      isNITF(isNITFIn)
10✔
265
{
266
    poDS = poDSIn;
10✔
267
    this->nBand = nBandIn;
10✔
268
    eDataType = eDataTypeIn;
10✔
269

270
    /*Check image type, whether there is one file per polarization or
271
     *one file containing all polarizations*/
272
    if (this->isOneFilePerPol)
10✔
273
    {
274
        poBand = poBandFile->GetRasterBand(1);
10✔
275
    }
276
    else
277
    {
278
        poBand = poBandFile->GetRasterBand(this->nBand);
×
279
    }
280

281
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
10✔
282

283
    if (pszPole != nullptr && strlen(pszPole) != 0)
10✔
284
    {
285
        SetMetadataItem("POLARIMETRIC_INTERP", pszPole);
10✔
286
    }
287
}
10✔
288

289
/************************************************************************/
290
/*                            RCMRasterBand()                            */
291
/************************************************************************/
292

293
RCMRasterBand::~RCMRasterBand()
20✔
294

295
{
296
    if (poBandFile != nullptr)
10✔
297
        GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
10✔
298
}
20✔
299

300
/************************************************************************/
301
/*                             IReadBlock()                             */
302
/************************************************************************/
303

304
CPLErr RCMRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
13✔
305

306
{
307
    int nRequestXSize = 0;
13✔
308
    int nRequestYSize = 0;
13✔
309
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nRequestXSize, &nRequestYSize);
13✔
310

311
    // Zero initial partial right-most and bottom-most blocks
312
    if (nRequestXSize < nBlockXSize || nRequestYSize < nBlockYSize)
13✔
313
    {
314
        memset(pImage, 0,
1✔
315
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
1✔
316
                   nBlockXSize * nBlockYSize);
1✔
317
    }
318

319
    int dataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
13✔
320
    GDALDataType bandFileType =
321
        poBandFile->GetRasterBand(1)->GetRasterDataType();
13✔
322
    int bandFileSize = GDALGetDataTypeSizeBytes(bandFileType);
13✔
323

324
    // case: 2 bands representing I+Q -> one complex band
325
    if (twoBandComplex && !this->isNITF)
13✔
326
    {
327
        // int bandFileSize = GDALGetDataTypeSizeBytes(bandFileType);
328
        // this data type is the complex version of the band file
329
        // Roberto: don't check that for the moment: CPLAssert(dataTypeSize ==
330
        // bandFileSize * 2);
331

332
        return
333
            // I and Q from each band are pixel-interleaved into this complex
334
            // band
335
            poBandFile->RasterIO(
×
336
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
337
                nRequestXSize, nRequestYSize, pImage, nRequestXSize,
338
                nRequestYSize, bandFileType, 2, nullptr, dataTypeSize,
339
                static_cast<GSpacing>(dataTypeSize) * nBlockXSize, bandFileSize,
×
340
                nullptr);
×
341
    }
342
    else if (twoBandComplex && this->isNITF)
13✔
343
    {
344
        return poBand->RasterIO(
×
345
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
346
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
347
            eDataType, 0, static_cast<GSpacing>(dataTypeSize) * nBlockXSize,
×
348
            nullptr);
×
349
    }
350

351
    if (poRCMDataset->IsComplexData())
13✔
352
    {
353
        // this data type is the complex version of the band file
354
        // Roberto: don't check that for the moment: CPLAssert(dataTypeSize ==
355
        // bandFileSize * 2);
356
        return
357
            // I and Q from each band are pixel-interleaved into this complex
358
            // band
359
            poBandFile->RasterIO(
×
360
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
361
                nRequestXSize, nRequestYSize, pImage, nRequestXSize,
362
                nRequestYSize, bandFileType, 2, nullptr, dataTypeSize,
363
                static_cast<GSpacing>(dataTypeSize) * nBlockXSize, bandFileSize,
×
364
                nullptr);
×
365
    }
366

367
    // case: band file == this band
368
    // NOTE: if the underlying band is opened with the NITF driver, it may
369
    // combine 2 band I+Q -> complex band
370
    else if (poBandFile->GetRasterBand(1)->GetRasterDataType() == eDataType)
13✔
371
    {
372
        return poBand->RasterIO(
26✔
373
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
13✔
374
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
375
            eDataType, 0, static_cast<GSpacing>(dataTypeSize) * nBlockXSize,
13✔
376
            nullptr);
13✔
377
    }
378
    else
379
    {
380
        CPLAssert(FALSE);
×
381
        return CE_Failure;
382
    }
383
}
384

385
/************************************************************************/
386
/*                            ReadLUT()                                 */
387
/************************************************************************/
388
/* Read the provided LUT in to m_ndTable                                */
389
/* 1. The gains list spans the range extent covered by all              */
390
/*    beams(if applicable).                                             */
391
/* 2. The mapping between the entry of gains                            */
392
/*    list and the range sample index is : the range sample             */
393
/*    index = gains entry index * stepSize + pixelFirstLutValue,        */
394
/*    where the gains entry index starts with ‘0’.For ScanSAR SLC,      */
395
/*    the range sample index refers to the index on the COPG            */
396
/************************************************************************/
397
void RCMCalibRasterBand::ReadLUT()
6✔
398
{
399

400
    char bandNumber[12];
401
    snprintf(bandNumber, sizeof(bandNumber), "%d", poDS->GetRasterCount() + 1);
6✔
402

403
    CPLXMLTreeCloser psLUT(CPLParseXMLFile(m_pszLUTFile));
6✔
404
    if (!psLUT)
6✔
405
        return;
×
406

407
    this->m_nfOffset =
6✔
408
        CPLAtof(CPLGetXMLValue(psLUT.get(), "=lut.offset", "0.0"));
6✔
409

410
    this->pixelFirstLutValue =
6✔
411
        atoi(CPLGetXMLValue(psLUT.get(), "=lut.pixelFirstLutValue", "0"));
6✔
412

413
    this->stepSize = atoi(CPLGetXMLValue(psLUT.get(), "=lut.stepSize", "0"));
6✔
414

415
    this->numberOfValues =
6✔
416
        atoi(CPLGetXMLValue(psLUT.get(), "=lut.numberOfValues", "0"));
6✔
417

418
    if (this->numberOfValues <= 0)
6✔
419
    {
420
        CPLError(
×
421
            CE_Failure, CPLE_NotSupported,
422
            "ERROR: The RCM driver does not support the LUT Number Of Values  "
423
            "equal or lower than zero.");
424
        return;
×
425
    }
426

427
    const CPLStringList aosLUTList(
428
        CSLTokenizeString2(CPLGetXMLValue(psLUT.get(), "=lut.gains", ""), " ",
6✔
429
                           CSLT_HONOURSTRINGS));
6✔
430

431
    if (this->stepSize <= 0)
6✔
432
    {
433
        if (this->pixelFirstLutValue <= 0)
6✔
434
        {
435
            CPLError(
×
436
                CE_Failure, CPLE_NotSupported,
437
                "ERROR: The RCM driver does not support LUT Pixel First Lut "
438
                "Value equal or lower than zero when the product is "
439
                "descending.");
440
            return;
×
441
        }
442
    }
443

444
    /* Get the Pixel Per range */
445
    if (this->stepSize == 0 || this->stepSize == INT_MIN ||
6✔
446
        this->numberOfValues == INT_MIN ||
6✔
447
        abs(this->stepSize) > INT_MAX / abs(this->numberOfValues))
6✔
448
    {
449
        CPLError(CE_Failure, CPLE_AppDefined,
×
450
                 "Bad values of stepSize / numberOfValues");
451
        return;
×
452
    }
453

454
    this->m_nTableSize = abs(this->stepSize) * abs(this->numberOfValues);
6✔
455

456
    if (this->m_nTableSize < this->m_poBandDataset->GetRasterXSize())
6✔
457
    {
458
        CPLError(
×
459
            CE_Failure, CPLE_NotSupported,
460
            "ERROR: The RCM driver does not support range of LUT gain values "
461
            "lower than the full image pixel range.");
462
        return;
×
463
    }
464

465
    // Avoid excessive memory allocation
466
    if (this->m_nTableSize > 1000 * 1000)
6✔
467
    {
468
        CPLError(CE_Failure, CPLE_NotSupported, "Too many elements in LUT: %d",
×
469
                 this->m_nTableSize);
470
        return;
×
471
    }
472

473
    /* Allocate the right LUT size according to the product range pixel */
474
    this->m_nfTable =
6✔
475
        InterpolateValues(aosLUTList.List(), this->m_nTableSize, this->stepSize,
6✔
476
                          this->numberOfValues, this->pixelFirstLutValue);
477
    if (!this->m_nfTable)
6✔
478
        return;
×
479

480
    // 32 max + space
481
    char *lut_gains = static_cast<char *>(
482
        VSI_CALLOC_VERBOSE(this->m_nTableSize, max_space_for_string));
6✔
483
    if (!lut_gains)
6✔
484
        return;
×
485

486
    for (int i = 0; i < this->m_nTableSize; i++)
107,496✔
487
    {
488
        char lut[max_space_for_string];
489
        // 6.123004711900930e+04  %e Scientific annotation
490
        snprintf(lut, sizeof(lut), "%e ", this->m_nfTable[i]);
107,490✔
491
        strcat(lut_gains, lut);
107,490✔
492
    }
493

494
    poDS->SetMetadataItem(CPLString("LUT_GAINS_").append(bandNumber).c_str(),
6✔
495
                          lut_gains);
6✔
496
    // Can free this because the function SetMetadataItem takes a copy
497
    CPLFree(lut_gains);
6✔
498

499
    char snum[256];
500
    if (this->m_eCalib == eCalibration::Sigma0)
6✔
501
    {
502
        poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(),
2✔
503
                              "SIGMA0");
2✔
504
    }
505
    else if (this->m_eCalib == eCalibration::Beta0)
4✔
506
    {
507
        poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(),
2✔
508
                              "BETA0");
2✔
509
    }
510
    else if (this->m_eCalib == eCalibration::Gamma)
2✔
511
    {
512
        poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(),
2✔
513
                              "GAMMA");
2✔
514
    }
515
    snprintf(snum, sizeof(snum), "%d", this->m_nTableSize);
6✔
516
    poDS->SetMetadataItem(CPLString("LUT_SIZE_").append(bandNumber).c_str(),
6✔
517
                          snum);
6✔
518
    snprintf(snum, sizeof(snum), "%f", this->m_nfOffset);
6✔
519
    poDS->SetMetadataItem(CPLString("LUT_OFFSET_").append(bandNumber).c_str(),
6✔
520
                          snum);
6✔
521
}
522

523
/************************************************************************/
524
/*                            ReadNoiseLevels()                         */
525
/************************************************************************/
526
/* Read the provided LUT in to m_nfTableNoiseLevels                     */
527
/* 1. The gains list spans the range extent covered by all              */
528
/*    beams(if applicable).                                             */
529
/* 2. The mapping between the entry of gains                            */
530
/*    list and the range sample index is : the range sample             */
531
/*    index = gains entry index * stepSize + pixelFirstLutValue,        */
532
/*    where the gains entry index starts with ‘0’.For ScanSAR SLC,      */
533
/*    the range sample index refers to the index on the COPG            */
534
/************************************************************************/
535
void RCMCalibRasterBand::ReadNoiseLevels()
6✔
536
{
537

538
    this->m_nfTableNoiseLevels = nullptr;
6✔
539

540
    if (this->m_pszNoiseLevelsFile == nullptr)
6✔
541
    {
542
        return;
×
543
    }
544

545
    char bandNumber[12];
546
    snprintf(bandNumber, sizeof(bandNumber), "%d", poDS->GetRasterCount() + 1);
6✔
547

548
    CPLXMLTreeCloser psNoiseLevels(CPLParseXMLFile(this->m_pszNoiseLevelsFile));
6✔
549
    if (!psNoiseLevels)
6✔
550
        return;
×
551

552
    // Load Beta Nought, Sigma Nought, Gamma noise levels
553
    // Loop through all nodes with spaces
554
    CPLXMLNode *psReferenceNoiseLevelNode =
555
        CPLGetXMLNode(psNoiseLevels.get(), "=noiseLevels");
6✔
556
    if (!psReferenceNoiseLevelNode)
6✔
557
        return;
×
558

559
    CPLXMLNode *psNodeInc;
560
    for (psNodeInc = psReferenceNoiseLevelNode->psChild; psNodeInc != nullptr;
234✔
561
         psNodeInc = psNodeInc->psNext)
228✔
562
    {
563
        if (EQUAL(psNodeInc->pszValue, "referenceNoiseLevel"))
228✔
564
        {
565
            CPLXMLNode *psCalibType =
566
                CPLGetXMLNode(psNodeInc, "sarCalibrationType");
18✔
567
            CPLXMLNode *psPixelFirstNoiseValue =
568
                CPLGetXMLNode(psNodeInc, "pixelFirstNoiseValue");
18✔
569
            CPLXMLNode *psStepSize = CPLGetXMLNode(psNodeInc, "stepSize");
18✔
570
            CPLXMLNode *psNumberOfValues =
571
                CPLGetXMLNode(psNodeInc, "numberOfValues");
18✔
572
            CPLXMLNode *psNoiseLevelValues =
573
                CPLGetXMLNode(psNodeInc, "noiseLevelValues");
18✔
574

575
            if (psCalibType != nullptr && psPixelFirstNoiseValue != nullptr &&
18✔
576
                psStepSize != nullptr && psNumberOfValues != nullptr &&
18✔
577
                psNoiseLevelValues != nullptr)
578
            {
579
                const char *calibType = CPLGetXMLValue(psCalibType, "", "");
18✔
580
                this->pixelFirstLutValueNoiseLevels =
18✔
581
                    atoi(CPLGetXMLValue(psPixelFirstNoiseValue, "", "0"));
18✔
582
                this->stepSizeNoiseLevels =
18✔
583
                    atoi(CPLGetXMLValue(psStepSize, "", "0"));
18✔
584
                this->numberOfValuesNoiseLevels =
18✔
585
                    atoi(CPLGetXMLValue(psNumberOfValues, "", "0"));
18✔
586
                const char *noiseLevelValues =
587
                    CPLGetXMLValue(psNoiseLevelValues, "", "");
18✔
588
                if (this->stepSizeNoiseLevels > 0 &&
18✔
589
                    this->numberOfValuesNoiseLevels != INT_MIN &&
×
590
                    abs(this->numberOfValuesNoiseLevels) <
×
591
                        INT_MAX / this->stepSizeNoiseLevels)
×
592
                {
593
                    char **papszNoiseLevelList = CSLTokenizeString2(
×
594
                        noiseLevelValues, " ", CSLT_HONOURSTRINGS);
595
                    /* Get the Pixel Per range */
596
                    this->m_nTableNoiseLevelsSize =
×
597
                        abs(this->stepSizeNoiseLevels) *
×
598
                        abs(this->numberOfValuesNoiseLevels);
×
599

600
                    if ((EQUAL(calibType, "Beta Nought") &&
×
601
                         this->m_eCalib == Beta0) ||
×
602
                        (EQUAL(calibType, "Sigma Nought") &&
×
603
                         this->m_eCalib == Sigma0) ||
×
604
                        (EQUAL(calibType, "Gamma") && this->m_eCalib == Gamma))
×
605
                    {
606
                        /* Allocate the right Noise Levels size according to the
607
                         * product range pixel */
608
                        this->m_nfTableNoiseLevels = InterpolateValues(
×
609
                            papszNoiseLevelList, this->m_nTableNoiseLevelsSize,
610
                            this->stepSizeNoiseLevels,
611
                            this->numberOfValuesNoiseLevels,
612
                            this->pixelFirstLutValueNoiseLevels);
613
                    }
614

615
                    CSLDestroy(papszNoiseLevelList);
×
616
                }
617

618
                if (this->m_nfTableNoiseLevels != nullptr)
18✔
619
                {
620
                    break;  // We are done
×
621
                }
622
            }
623
        }
624
    }
625
}
626

627
/************************************************************************/
628
/*                        RCMCalibRasterBand()                          */
629
/************************************************************************/
630

631
RCMCalibRasterBand::RCMCalibRasterBand(
6✔
632
    RCMDataset *poDataset, const char *pszPolarization, GDALDataType eType,
633
    GDALDataset *poBandDataset, eCalibration eCalib, const char *pszLUT,
634
    const char *pszNoiseLevels, GDALDataType eOriginalType)
6✔
635
    : m_eCalib(eCalib), m_poBandDataset(poBandDataset),
636
      m_eOriginalType(eOriginalType), m_pszLUTFile(VSIStrdup(pszLUT)),
12✔
637
      m_pszNoiseLevelsFile(VSIStrdup(pszNoiseLevels))
6✔
638
{
639
    this->poDS = poDataset;
6✔
640

641
    if (pszPolarization != nullptr && strlen(pszPolarization) != 0)
6✔
642
    {
643
        SetMetadataItem("POLARIMETRIC_INTERP", pszPolarization);
6✔
644
    }
645

646
    if ((eType == GDT_CInt16) || (eType == GDT_CFloat32))
6✔
647
        this->eDataType = GDT_CFloat32;
×
648
    else
649
        this->eDataType = GDT_Float32;
6✔
650

651
    GDALRasterBand *poRasterBand = poBandDataset->GetRasterBand(1);
6✔
652
    poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
6✔
653

654
    ReadLUT();
6✔
655
    ReadNoiseLevels();
6✔
656
}
6✔
657

658
/************************************************************************/
659
/*                       ~RCMCalibRasterBand()                          */
660
/************************************************************************/
661

662
RCMCalibRasterBand::~RCMCalibRasterBand()
12✔
663
{
664
    CPLFree(m_nfTable);
6✔
665
    CPLFree(m_nfTableNoiseLevels);
6✔
666
    CPLFree(m_pszLUTFile);
6✔
667
    CPLFree(m_pszNoiseLevelsFile);
6✔
668
    GDALClose(m_poBandDataset);
6✔
669
}
12✔
670

671
/************************************************************************/
672
/*                        IReadBlock()                                  */
673
/************************************************************************/
674

675
CPLErr RCMCalibRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
×
676
                                      void *pImage)
677
{
678
    CPLErr eErr;
679
    int nRequestXSize = 0;
×
680
    int nRequestYSize = 0;
×
681
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nRequestXSize, &nRequestYSize);
×
682

683
    // Zero initial partial right-most and bottom-most blocks
684
    if (nRequestXSize < nBlockXSize || nRequestYSize < nBlockYSize)
×
685
    {
686
        memset(pImage, 0,
×
687
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
×
688
                   nBlockXSize * nBlockYSize);
×
689
    }
690

691
    if (this->m_eOriginalType == GDT_CInt16)
×
692
    {
693
        /* read in complex values */
694
        GInt16 *panImageTmp = static_cast<GInt16 *>(
695
            VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize,
×
696
                                GDALGetDataTypeSizeBytes(m_eOriginalType)));
697
        if (!panImageTmp)
×
698
            return CE_Failure;
×
699

700
        if (m_poBandDataset->GetRasterCount() == 2)
×
701
        {
702
            eErr = m_poBandDataset->RasterIO(
×
703
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
704
                nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize,
705
                nRequestYSize, this->m_eOriginalType, 2, nullptr, 4,
706
                nBlockXSize * 4, 4, nullptr);
×
707

708
            /*
709
            eErr = m_poBandDataset->RasterIO(GF_Read,
710
                nBlockXOff * nBlockXSize,
711
                nBlockYOff * nBlockYSize,
712
                nRequestXSize, nRequestYSize,
713
                panImageTmp, nRequestXSize, nRequestYSize,
714
                GDT_Int32,
715
                2, nullptr, 4, nBlockXSize * 4, 2, nullptr);
716
            */
717
        }
718
        else
719
        {
720
            eErr = m_poBandDataset->RasterIO(
×
721
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
722
                nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize,
723
                nRequestYSize, this->m_eOriginalType, 1, nullptr, 4,
724
                nBlockXSize * 4, 0, nullptr);
×
725

726
            /*
727
            eErr =
728
                m_poBandDataset->RasterIO(GF_Read,
729
                    nBlockXOff * nBlockXSize,
730
                    nBlockYOff * nBlockYSize,
731
                    nRequestXSize, nRequestYSize,
732
                    panImageTmp, nRequestXSize, nRequestYSize,
733
                    GDT_UInt32,
734
                    1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
735
            */
736

737
#ifdef CPL_LSB
738
            /* First, undo the 32bit swap. */
739
            GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
×
740

741
            /* Then apply 16 bit swap. */
742
            GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
×
743
#endif
744
        }
745

746
        /* calibrate the complex values */
747
        for (int i = 0; i < nRequestYSize; i++)
×
748
        {
749
            for (int j = 0; j < nRequestXSize; j++)
×
750
            {
751
                /* calculate pixel offset in memory*/
752
                const int nPixOff = 2 * (i * nBlockXSize + j);
×
753
                const int nTruePixOff = (i * nBlockXSize) + j;
×
754

755
                // Formula for Complex Q+J
756
                const float real = static_cast<float>(panImageTmp[nPixOff]);
×
757
                const float img = static_cast<float>(panImageTmp[nPixOff + 1]);
×
758
                const float digitalValue = (real * real) + (img * img);
×
759
                const float lutValue =
×
760
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
×
761
                const float calibValue = digitalValue / (lutValue * lutValue);
×
762

763
                reinterpret_cast<float *>(pImage)[nTruePixOff] = calibValue;
×
764
            }
765
        }
766

767
        CPLFree(panImageTmp);
×
768
    }
769

770
    // If the underlying file is NITF CFloat32
771
    else if (this->m_eOriginalType == GDT_CFloat32 ||
×
772
             this->m_eOriginalType == GDT_CFloat64)
×
773
    {
774
        /* read in complex values */
775
        const int dataTypeSize =
776
            GDALGetDataTypeSizeBytes(this->m_eOriginalType);
×
777
        const GDALDataType bandFileType = this->m_eOriginalType;
×
778
        const int bandFileDataTypeSize = GDALGetDataTypeSizeBytes(bandFileType);
×
779

780
        /* read the original image complex values in a temporary image space */
781
        float *pafImageTmp = static_cast<float *>(VSI_MALLOC3_VERBOSE(
×
782
            nBlockXSize, nBlockYSize, 2 * bandFileDataTypeSize));
783
        if (!pafImageTmp)
×
784
            return CE_Failure;
×
785

786
        eErr =
787
            // I and Q from each band are pixel-interleaved into this complex
788
            // band
789
            m_poBandDataset->RasterIO(
×
790
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
791
                nRequestXSize, nRequestYSize, pafImageTmp, nRequestXSize,
792
                nRequestYSize, bandFileType, 2, nullptr, dataTypeSize,
793
                static_cast<GSpacing>(dataTypeSize) * nBlockXSize,
×
794
                bandFileDataTypeSize, nullptr);
795

796
        /* calibrate the complex values */
797
        for (int i = 0; i < nRequestYSize; i++)
×
798
        {
799
            for (int j = 0; j < nRequestXSize; j++)
×
800
            {
801
                /* calculate pixel offset in memory*/
802
                const int nPixOff = 2 * (i * nBlockXSize + j);
×
803
                const int nTruePixOff = (i * nBlockXSize) + j;
×
804

805
                // Formula for Complex Q+J
806
                const float real = pafImageTmp[nPixOff];
×
807
                const float img = pafImageTmp[nPixOff + 1];
×
808
                const float digitalValue = (real * real) + (img * img);
×
809
                const float lutValue =
×
810
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
×
811
                const float calibValue = digitalValue / (lutValue * lutValue);
×
812

813
                reinterpret_cast<float *>(pImage)[nTruePixOff] = calibValue;
×
814
            }
815
        }
816

817
        CPLFree(pafImageTmp);
×
818
    }
819

820
    else if (this->m_eOriginalType == GDT_Float32)
×
821
    {
822
        /* A Float32 is actual 4 bytes */
823
        eErr = m_poBandDataset->RasterIO(
×
824
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
825
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
826
            GDT_Float32, 1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
×
827

828
        /* iterate over detected values */
829
        for (int i = 0; i < nRequestYSize; i++)
×
830
        {
831
            for (int j = 0; j < nRequestXSize; j++)
×
832
            {
833
                int nPixOff = (i * nBlockXSize) + j;
×
834

835
                /* For detected products, in order to convert the digital number
836
                of a given range sample to a calibrated value, the digital value
837
                is first squared, then the offset(B) is added and the result is
838
                divided by the gains value(A) corresponding to the range sample.
839
                RCM-SP-53-0419  Issue 2/5:  January 2, 2018  Page 7-56 */
840
                const float digitalValue =
×
841
                    reinterpret_cast<float *>(pImage)[nPixOff];
×
842
                const float A =
×
843
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
×
844
                reinterpret_cast<float *>(pImage)[nPixOff] =
×
845
                    ((digitalValue * digitalValue) +
×
846
                     static_cast<float>(this->m_nfOffset)) /
×
847
                    A;
848
            }
849
        }
850
    }
851

852
    else if (this->m_eOriginalType == GDT_Float64)
×
853
    {
854
        /* A Float64 is actual 8 bytes */
855
        eErr = m_poBandDataset->RasterIO(
×
856
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
857
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
858
            GDT_Float64, 1, nullptr, 8, nBlockXSize * 8, 0, nullptr);
×
859

860
        /* iterate over detected values */
861
        for (int i = 0; i < nRequestYSize; i++)
×
862
        {
863
            for (int j = 0; j < nRequestXSize; j++)
×
864
            {
865
                int nPixOff = (i * nBlockXSize) + j;
×
866

867
                /* For detected products, in order to convert the digital number
868
                of a given range sample to a calibrated value, the digital value
869
                is first squared, then the offset(B) is added and the result is
870
                divided by the gains value(A) corresponding to the range sample.
871
                RCM-SP-53-0419  Issue 2/5:  January 2, 2018  Page 7-56 */
872
                const float digitalValue =
×
873
                    reinterpret_cast<float *>(pImage)[nPixOff];
×
874
                const float A =
×
875
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
×
876
                reinterpret_cast<float *>(pImage)[nPixOff] =
×
877
                    ((digitalValue * digitalValue) +
×
878
                     static_cast<float>(this->m_nfOffset)) /
×
879
                    A;
880
            }
881
        }
882
    }
883

884
    else if (this->m_eOriginalType == GDT_UInt16)
×
885
    {
886
        /* read in detected values */
887
        GUInt16 *panImageTmp = static_cast<GUInt16 *>(VSI_MALLOC3_VERBOSE(
×
888
            nBlockXSize, nBlockYSize, GDALGetDataTypeSizeBytes(GDT_UInt16)));
889
        if (!panImageTmp)
×
890
            return CE_Failure;
×
891
        eErr = m_poBandDataset->RasterIO(
×
892
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
893
            nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize,
894
            nRequestYSize, GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0,
×
895
            nullptr);
896

897
        /* iterate over detected values */
898
        for (int i = 0; i < nRequestYSize; i++)
×
899
        {
900
            for (int j = 0; j < nRequestXSize; j++)
×
901
            {
902
                const int nPixOff = (i * nBlockXSize) + j;
×
903

904
                const float digitalValue =
×
905
                    static_cast<float>(panImageTmp[nPixOff]);
×
906
                const float A =
×
907
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
×
908
                reinterpret_cast<float *>(pImage)[nPixOff] =
×
909
                    ((digitalValue * digitalValue) +
×
910
                     static_cast<float>(this->m_nfOffset)) /
×
911
                    A;
912
            }
913
        }
914
        CPLFree(panImageTmp);
×
915
    } /* Ticket #2104: Support for ScanSAR products */
916

917
    else if (this->m_eOriginalType == GDT_Byte)
×
918
    {
919
        GByte *pabyImageTmp =
920
            static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nBlockXSize, nBlockYSize));
×
921
        if (!pabyImageTmp)
×
922
            return CE_Failure;
×
923
        eErr = m_poBandDataset->RasterIO(
×
924
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
×
925
            nRequestXSize, nRequestYSize, pabyImageTmp, nRequestXSize,
926
            nRequestYSize, GDT_Byte, 1, nullptr, 1, nBlockXSize, 0, nullptr);
×
927

928
        /* iterate over detected values */
929
        for (int i = 0; i < nRequestYSize; i++)
×
930
        {
931
            for (int j = 0; j < nRequestXSize; j++)
×
932
            {
933
                const int nPixOff = (i * nBlockXSize) + j;
×
934

935
                const float digitalValue =
×
936
                    static_cast<float>(pabyImageTmp[nPixOff]);
×
937
                const float A =
×
938
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
×
939
                reinterpret_cast<float *>(pImage)[nPixOff] =
×
940
                    ((digitalValue * digitalValue) +
×
941
                     static_cast<float>(this->m_nfOffset)) /
×
942
                    A;
943
            }
944
        }
945
        CPLFree(pabyImageTmp);
×
946
    }
947
    else
948
    {
949
        CPLAssert(FALSE);
×
950
        return CE_Failure;
951
    }
952
    return eErr;
×
953
}
954

955
/************************************************************************/
956
/* ==================================================================== */
957
/*                              RCMDataset                              */
958
/* ==================================================================== */
959
/************************************************************************/
960

961
/************************************************************************/
962
/*                            ~RCMDataset()                             */
963
/************************************************************************/
964

965
RCMDataset::~RCMDataset()
16✔
966

967
{
968
    RCMDataset::FlushCache(true);
8✔
969

970
    if (nGCPCount > 0)
8✔
971
    {
972
        GDALDeinitGCPs(nGCPCount, pasGCPList);
8✔
973
        CPLFree(pasGCPList);
8✔
974
    }
975

976
    RCMDataset::CloseDependentDatasets();
8✔
977

978
    if (papszSubDatasets != nullptr)
8✔
979
        CSLDestroy(papszSubDatasets);
4✔
980

981
    if (papszExtraFiles != nullptr)
8✔
982
        CSLDestroy(papszExtraFiles);
8✔
983

984
    if (m_nfIncidenceAngleTable != nullptr)
8✔
985
        CPLFree(m_nfIncidenceAngleTable);
8✔
986
}
16✔
987

988
/************************************************************************/
989
/*                      CloseDependentDatasets()                        */
990
/************************************************************************/
991

992
int RCMDataset::CloseDependentDatasets()
8✔
993
{
994
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
8✔
995

996
    if (nBands != 0)
8✔
997
        bHasDroppedRef = TRUE;
8✔
998

999
    for (int iBand = 0; iBand < nBands; iBand++)
24✔
1000
    {
1001
        delete papoBands[iBand];
16✔
1002
    }
1003
    nBands = 0;
8✔
1004

1005
    return bHasDroppedRef;
8✔
1006
}
1007

1008
/************************************************************************/
1009
/*                            GetFileList()                             */
1010
/************************************************************************/
1011

1012
char **RCMDataset::GetFileList()
×
1013

1014
{
1015
    char **papszFileList = GDALPamDataset::GetFileList();
×
1016

1017
    papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
×
1018

1019
    return papszFileList;
×
1020
}
1021

1022
/************************************************************************/
1023
/*                                Open()                                */
1024
/************************************************************************/
1025

1026
GDALDataset *RCMDataset::Open(GDALOpenInfo *poOpenInfo)
10✔
1027

1028
{
1029
    /* -------------------------------------------------------------------- */
1030
    /*      Is this a RCM Product.xml definition?                           */
1031
    /* -------------------------------------------------------------------- */
1032
    if (!RCMDatasetIdentify(poOpenInfo))
10✔
1033
    {
1034
        return nullptr;
×
1035
    }
1036

1037
    /* -------------------------------------------------------------------- */
1038
    /*        Get subdataset information, if relevant                       */
1039
    /* -------------------------------------------------------------------- */
1040
    const char *pszFilename = poOpenInfo->pszFilename;
10✔
1041
    eCalibration eCalib = None;
10✔
1042

1043
    if (STARTS_WITH_CI(pszFilename, szLayerCalibration) &&
10✔
1044
        pszFilename[strlen(szLayerCalibration)] == chLayerSeparator)
6✔
1045
    {
1046
        // The calibration name and filename begins after the hard coded layer
1047
        // name
1048
        pszFilename += strlen(szLayerCalibration) + 1;
6✔
1049

1050
        if (STARTS_WITH_CI(pszFilename, szBETA0))
6✔
1051
        {
1052
            eCalib = Beta0;
1✔
1053
        }
1054
        else if (STARTS_WITH_CI(pszFilename, szSIGMA0))
5✔
1055
        {
1056
            eCalib = Sigma0;
1✔
1057
        }
1058
        else if (STARTS_WITH_CI(pszFilename, szGAMMA) ||
4✔
1059
                 STARTS_WITH_CI(pszFilename, "GAMMA0"))  // Cover both situation
3✔
1060
        {
1061
            eCalib = Gamma;
1✔
1062
        }
1063
        else if (STARTS_WITH_CI(pszFilename, szUNCALIB))
3✔
1064
        {
1065
            eCalib = Uncalib;
2✔
1066
        }
1067
        else
1068
        {
1069
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
1070
                     "Unsupported calibration type");
1071
            return nullptr;
1✔
1072
        }
1073

1074
        /* advance the pointer to the actual filename */
1075
        while (*pszFilename != '\0' && *pszFilename != ':')
35✔
1076
            pszFilename++;
30✔
1077

1078
        if (*pszFilename == ':')
5✔
1079
            pszFilename++;
5✔
1080

1081
        // need to redo the directory check:
1082
        // the GDALOpenInfo check would have failed because of the calibration
1083
        // string on the filename
1084
        VSIStatBufL sStat;
1085
        if (VSIStatL(pszFilename, &sStat) == 0)
5✔
1086
            poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode);
4✔
1087
    }
1088

1089
    CPLString osMDFilename;
18✔
1090
    if (poOpenInfo->bIsDirectory)
9✔
1091
    {
1092
        /* Check for directory access when there is a product.xml file in the
1093
        directory. */
1094
        osMDFilename =
1095
            CPLFormCIFilenameSafe(pszFilename, "product.xml", nullptr);
2✔
1096

1097
        VSIStatBufL sStat;
1098
        if (VSIStatL(osMDFilename, &sStat) != 0)
2✔
1099
        {
1100
            /* If not, check for directory extra 'metadata' access when there is
1101
            a product.xml file in the directory. */
1102
            osMDFilename = CPLFormCIFilenameSafe(pszFilename,
1✔
1103
                                                 GetMetadataProduct(), nullptr);
2✔
1104
        }
1105
    }
1106
    else
1107
        osMDFilename = pszFilename;
7✔
1108

1109
    /* -------------------------------------------------------------------- */
1110
    /*      Ingest the Product.xml file.                                    */
1111
    /* -------------------------------------------------------------------- */
1112
    CPLXMLTreeCloser psProduct(CPLParseXMLFile(osMDFilename));
18✔
1113
    if (!psProduct)
9✔
1114
        return nullptr;
1✔
1115

1116
    /* -------------------------------------------------------------------- */
1117
    /*      Confirm the requested access is supported.                      */
1118
    /* -------------------------------------------------------------------- */
1119
    if (poOpenInfo->eAccess == GA_Update)
8✔
1120
    {
1121
        ReportUpdateNotSupportedByDriver("RCM");
×
1122
        return nullptr;
×
1123
    }
1124

1125
    CPLXMLNode *psSceneAttributes =
1126
        CPLGetXMLNode(psProduct.get(), "=product.sceneAttributes");
8✔
1127
    if (psSceneAttributes == nullptr)
8✔
1128
    {
1129
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1130
                 "ERROR: Failed to find <sceneAttributes> in document.");
1131
        return nullptr;
×
1132
    }
1133

1134
    CPLXMLNode *psImageAttributes = CPLGetXMLNode(
8✔
1135
        psProduct.get(), "=product.sceneAttributes.imageAttributes");
1136
    if (psImageAttributes == nullptr)
8✔
1137
    {
1138
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1139
                 "ERROR: Failed to find <sceneAttributes.imageAttributes> in "
1140
                 "document.");
1141
        return nullptr;
×
1142
    }
1143

1144
    int numberOfEntries =
1145
        atoi(CPLGetXMLValue(psSceneAttributes, "numberOfEntries", "0"));
8✔
1146
    if (numberOfEntries != 1)
8✔
1147
    {
1148
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1149
                 "ERROR: Only RCM with Complex Single-beam is supported.");
1150
        return nullptr;
×
1151
    }
1152

1153
    CPLXMLNode *psImageReferenceAttributes =
1154
        CPLGetXMLNode(psProduct.get(), "=product.imageReferenceAttributes");
8✔
1155
    if (psImageReferenceAttributes == nullptr)
8✔
1156
    {
1157
        CPLError(
×
1158
            CE_Failure, CPLE_OpenFailed,
1159
            "ERROR: Failed to find <imageReferenceAttributes> in document.");
1160
        return nullptr;
×
1161
    }
1162

1163
    CPLXMLNode *psImageGenerationParameters =
1164
        CPLGetXMLNode(psProduct.get(), "=product.imageGenerationParameters");
8✔
1165
    if (psImageGenerationParameters == nullptr)
8✔
1166
    {
1167
        CPLError(
×
1168
            CE_Failure, CPLE_OpenFailed,
1169
            "ERROR: Failed to find <imageGenerationParameters> in document.");
1170
        return nullptr;
×
1171
    }
1172

1173
    /* -------------------------------------------------------------------- */
1174
    /*      Create the dataset.                                             */
1175
    /* -------------------------------------------------------------------- */
1176
    auto poDS = std::make_unique<RCMDataset>();
16✔
1177

1178
    poDS->psProduct = std::move(psProduct);
8✔
1179

1180
    /* -------------------------------------------------------------------- */
1181
    /*      Get overall image information.                                  */
1182
    /* -------------------------------------------------------------------- */
1183
    poDS->nRasterXSize = atoi(CPLGetXMLValue(
8✔
1184
        psSceneAttributes, "imageAttributes.samplesPerLine", "-1"));
1185
    poDS->nRasterYSize = atoi(
8✔
1186
        CPLGetXMLValue(psSceneAttributes, "imageAttributes.numLines", "-1"));
1187
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
8✔
1188
    {
1189
        return nullptr;
×
1190
    }
1191

1192
    /* -------------------------------------------------------------------- */
1193
    /*      Check product type, as to determine if there are LUTs for       */
1194
    /*      calibration purposes.                                           */
1195
    /* -------------------------------------------------------------------- */
1196

1197
    const char *pszItem =
1198
        CPLGetXMLValue(psImageGenerationParameters,
8✔
1199
                       "generalProcessingInformation.productType", "UNK");
1200
    poDS->SetMetadataItem("PRODUCT_TYPE", pszItem);
8✔
1201
    const char *pszProductType = pszItem;
8✔
1202

1203
    pszItem =
1204
        CPLGetXMLValue(poDS->psProduct.get(), "=product.productId", "UNK");
8✔
1205
    poDS->SetMetadataItem("PRODUCT_ID", pszItem);
8✔
1206

1207
    pszItem = CPLGetXMLValue(
8✔
1208
        poDS->psProduct.get(),
8✔
1209
        "=product.securityAttributes.securityClassification", "UNK");
1210
    poDS->SetMetadataItem("SECURITY_CLASSIFICATION", pszItem);
8✔
1211

1212
    pszItem =
1213
        CPLGetXMLValue(poDS->psProduct.get(),
8✔
1214
                       "=product.sourceAttributes.polarizationDataMode", "UNK");
1215
    poDS->SetMetadataItem("POLARIZATION_DATA_MODE", pszItem);
8✔
1216

1217
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
8✔
1218
                             "generalProcessingInformation.processingFacility",
1219
                             "UNK");
1220
    poDS->SetMetadataItem("PROCESSING_FACILITY", pszItem);
8✔
1221

1222
    pszItem =
1223
        CPLGetXMLValue(psImageGenerationParameters,
8✔
1224
                       "generalProcessingInformation.processingTime", "UNK");
1225
    poDS->SetMetadataItem("PROCESSING_TIME", pszItem);
8✔
1226

1227
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
8✔
1228
                             "sarProcessingInformation.satelliteHeight", "UNK");
1229
    poDS->SetMetadataItem("SATELLITE_HEIGHT", pszItem);
8✔
1230

1231
    pszItem = CPLGetXMLValue(
8✔
1232
        psImageGenerationParameters,
1233
        "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK");
1234
    poDS->SetMetadataItem("FIRST_LINE_TIME", pszItem);
8✔
1235

1236
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
8✔
1237
                             "sarProcessingInformation.zeroDopplerTimeLastLine",
1238
                             "UNK");
1239
    poDS->SetMetadataItem("LAST_LINE_TIME", pszItem);
8✔
1240

1241
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
8✔
1242
                             "sarProcessingInformation.lutApplied", "");
1243
    poDS->SetMetadataItem("LUT_APPLIED", pszItem);
8✔
1244

1245
    /*---------------------------------------------------------------------
1246
           If true, a polarization dependent application LUT has been applied
1247
           for each polarization channel. Otherwise the same application LUT
1248
           has been applied for all polarization channels. Not applicable to
1249
           lookupTable = "Unity*" or if dataType = "Floating-Point".
1250
      --------------------------------------------------------------------- */
1251
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
8✔
1252
                             "sarProcessingInformation.perPolarizationScaling",
1253
                             "false");
1254
    poDS->SetMetadataItem("PER_POLARIZATION_SCALING", pszItem);
8✔
1255
    if (EQUAL(pszItem, "true") || EQUAL(pszItem, "TRUE"))
8✔
1256
    {
1257
        poDS->bPerPolarizationScaling = TRUE;
8✔
1258
    }
1259

1260
    /* the following cases can be assumed to have no LUTs, as per
1261
     * RN-RP-51-2713, but also common sense
1262
     * SLC represents a SLant range georeferenced Complex product
1263
     * (i.e., equivalent to a Single-Look Complex product for RADARSAT-1 or
1264
     * RADARSAT-2). • GRD or GRC represent GRound range georeferenced Detected
1265
     * or Complex products (GRD is equivalent to an SGX, SCN or SCW product for
1266
     * RADARSAT1 or RADARSAT-2). • GCD or GCC represent GeoCoded Detected or
1267
     * Complex products (GCD is equivalent to an SSG or SPG product for
1268
     * RADARSAT-1 or RADARSAT-2).
1269
     */
1270
    bool bCanCalib = false;
8✔
1271
    if (!(STARTS_WITH_CI(pszProductType, "UNK") ||
8✔
1272
          STARTS_WITH_CI(pszProductType, "GCD") ||
8✔
1273
          STARTS_WITH_CI(pszProductType, "GCC")))
8✔
1274
    {
1275
        bCanCalib = true;
8✔
1276
    }
1277

1278
    /* -------------------------------------------------------------------- */
1279
    /*      Get dataType (so we can recognise complex data), and the        */
1280
    /*      bitsPerSample.                                                  */
1281
    /* -------------------------------------------------------------------- */
1282
    const char *pszSampleDataType = CPLGetXMLValue(
8✔
1283
        psImageReferenceAttributes, "rasterAttributes.sampleType", "");
1284
    poDS->SetMetadataItem("SAMPLE_TYPE", pszSampleDataType);
8✔
1285

1286
    /* Either Integer (16 bits) or Floating-Point (32 bits) */
1287
    const char *pszDataType = CPLGetXMLValue(psImageReferenceAttributes,
8✔
1288
                                             "rasterAttributes.dataType", "");
1289
    poDS->SetMetadataItem("DATA_TYPE", pszDataType);
8✔
1290

1291
    /* 2 entries for complex data 1 entry for magnitude detected data */
1292
    const char *pszBitsPerSample = CPLGetXMLValue(
8✔
1293
        psImageReferenceAttributes, "rasterAttributes.bitsPerSample", "");
1294
    const int nBitsPerSample = atoi(pszBitsPerSample);
8✔
1295
    poDS->SetMetadataItem("BITS_PER_SAMPLE", pszBitsPerSample);
8✔
1296

1297
    const char *pszSampledPixelSpacingTime =
1298
        CPLGetXMLValue(psImageReferenceAttributes,
8✔
1299
                       "rasterAttributes.sampledPixelSpacingTime", "UNK");
1300
    poDS->SetMetadataItem("SAMPLED_PIXEL_SPACING_TIME",
8✔
1301
                          pszSampledPixelSpacingTime);
8✔
1302

1303
    const char *pszSampledLineSpacingTime =
1304
        CPLGetXMLValue(psImageReferenceAttributes,
8✔
1305
                       "rasterAttributes.sampledLineSpacingTime", "UNK");
1306
    poDS->SetMetadataItem("SAMPLED_LINE_SPACING_TIME",
8✔
1307
                          pszSampledLineSpacingTime);
8✔
1308

1309
    GDALDataType eDataType;
1310

1311
    if (EQUAL(pszSampleDataType, "Mixed")) /* RCM MLC has Mixed sampleType */
8✔
1312
    {
1313
        poDS->isComplexData = false; /* RCM MLC is detected, non-complex */
1✔
1314
        if (nBitsPerSample == 32)
1✔
1315
        {
1316
            eDataType = GDT_Float32; /* 32 bits, check read block */
1✔
1317
            poDS->magnitudeBits = 32;
1✔
1318
        }
1319
        else
1320
        {
1321
            eDataType = GDT_UInt16; /* 16 bits, check read block */
×
1322
            poDS->magnitudeBits = 16;
×
1323
        }
1324
    }
1325
    else if (EQUAL(pszSampleDataType, "Complex"))
7✔
1326
    {
1327
        poDS->isComplexData = true;
×
1328
        /* Usually this is the same bits for both */
1329
        poDS->realBitsComplexData = nBitsPerSample;
×
1330
        poDS->imaginaryBitsComplexData = nBitsPerSample;
×
1331

1332
        if (nBitsPerSample == 32)
×
1333
        {
1334
            eDataType = GDT_CFloat32; /* 32 bits, check read block */
×
1335
        }
1336
        else
1337
        {
1338
            eDataType = GDT_CInt16; /* 16 bits, check read block */
×
1339
        }
1340
    }
1341
    else if (nBitsPerSample == 32 &&
7✔
1342
             EQUAL(pszSampleDataType, "Magnitude Detected"))
×
1343
    {
1344
        /* Actually, I don't need to test the 'dataType'=' Floating-Point', we
1345
         * know that a 32 bits per sample */
1346
        eDataType = GDT_Float32;
×
1347
        poDS->isComplexData = false;
×
1348
        poDS->magnitudeBits = 32;
×
1349
    }
1350
    else if (nBitsPerSample == 16 &&
7✔
1351
             EQUAL(pszSampleDataType, "Magnitude Detected"))
7✔
1352
    {
1353
        /* Actually, I don't need to test the 'dataType'=' Integer', we know
1354
         * that a 16 bits per sample */
1355
        eDataType = GDT_UInt16;
7✔
1356
        poDS->isComplexData = false;
7✔
1357
        poDS->magnitudeBits = 16;
7✔
1358
    }
1359
    else
1360
    {
1361
        CPLError(CE_Failure, CPLE_AppDefined,
×
1362
                 "ERROR: dataType=%s and bitsPerSample=%d are not a supported "
1363
                 "configuration.",
1364
                 pszDataType, nBitsPerSample);
1365
        return nullptr;
×
1366
    }
1367

1368
    /* Indicates whether pixel number (i.e., range) increases or decreases with
1369
    range time. For GCD and GCC products, this applies to intermediate ground
1370
    range image data prior to geocoding. */
1371
    const char *pszPixelTimeOrdering =
1372
        CPLGetXMLValue(psImageReferenceAttributes,
8✔
1373
                       "rasterAttributes.pixelTimeOrdering", "UNK");
1374
    poDS->SetMetadataItem("PIXEL_TIME_ORDERING", pszPixelTimeOrdering);
8✔
1375

1376
    /* Indicates whether line numbers (i.e., azimuth) increase or decrease with
1377
    azimuth time. For GCD and GCC products, this applies to intermediate ground
1378
    range image data prior to geocoding. */
1379
    const char *pszLineTimeOrdering = CPLGetXMLValue(
8✔
1380
        psImageReferenceAttributes, "rasterAttributes.lineTimeOrdering", "UNK");
1381
    poDS->SetMetadataItem("LINE_TIME_ORDERING", pszLineTimeOrdering);
8✔
1382

1383
    /* while we're at it, extract the pixel spacing information */
1384
    const char *pszPixelSpacing =
1385
        CPLGetXMLValue(psImageReferenceAttributes,
8✔
1386
                       "rasterAttributes.sampledPixelSpacing", "UNK");
1387
    poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing);
8✔
1388

1389
    const char *pszLineSpacing =
1390
        CPLGetXMLValue(psImageReferenceAttributes,
8✔
1391
                       "rasterAttributes.sampledLineSpacing", "UNK");
1392
    poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing);
8✔
1393

1394
    /* -------------------------------------------------------------------- */
1395
    /*      Open each of the data files as a complex band.                  */
1396
    /* -------------------------------------------------------------------- */
1397
    char *pszBeta0LUT = nullptr;
8✔
1398
    char *pszGammaLUT = nullptr;
8✔
1399
    char *pszSigma0LUT = nullptr;
8✔
1400
    // Same file for all calibrations except the calibration is plit inside the
1401
    // XML
1402
    std::string osNoiseLevelsValues;
16✔
1403

1404
    const CPLString osPath = CPLGetPathSafe(osMDFilename);
16✔
1405

1406
    /* Get a list of all polarizations */
1407
    CPLXMLNode *psSourceAttrs =
1408
        CPLGetXMLNode(poDS->psProduct.get(), "=product.sourceAttributes");
8✔
1409
    if (psSourceAttrs == nullptr)
8✔
1410
    {
1411
        CPLError(
×
1412
            CE_Failure, CPLE_OpenFailed,
1413
            "ERROR: RCM source attributes is missing. Please contact your data "
1414
            "provider for a corrected dataset.");
1415
        return nullptr;
×
1416
    }
1417

1418
    CPLXMLNode *psRadarParameters = CPLGetXMLNode(
8✔
1419
        poDS->psProduct.get(), "=product.sourceAttributes.radarParameters");
8✔
1420
    if (psRadarParameters == nullptr)
8✔
1421
    {
1422
        CPLError(
×
1423
            CE_Failure, CPLE_OpenFailed,
1424
            "ERROR: RCM radar parameters is missing. Please contact your data "
1425
            "provider for a corrected dataset.");
1426
        return nullptr;
×
1427
    }
1428

1429
    const char *pszPolarizations =
1430
        CPLGetXMLValue(psRadarParameters, "polarizations", "");
8✔
1431
    if (pszPolarizations == nullptr || strlen(pszPolarizations) == 0)
8✔
1432
    {
1433
        CPLError(
×
1434
            CE_Failure, CPLE_OpenFailed,
1435
            "ERROR: RCM polarizations list is missing. Please contact your "
1436
            "data provider for a corrected dataset.");
1437
        return nullptr;
×
1438
    }
1439
    poDS->SetMetadataItem("POLARIZATIONS", pszPolarizations);
8✔
1440

1441
    const char *psAcquisitionType =
1442
        CPLGetXMLValue(psRadarParameters, "acquisitionType", "UNK");
8✔
1443
    poDS->SetMetadataItem("ACQUISITION_TYPE", psAcquisitionType);
8✔
1444

1445
    const char *psBeams = CPLGetXMLValue(psRadarParameters, "beams", "UNK");
8✔
1446
    poDS->SetMetadataItem("BEAMS", psBeams);
8✔
1447

1448
    const CPLStringList aosPolarizationsGrids(
1449
        CSLTokenizeString2(pszPolarizations, " ", 0));
16✔
1450
    CPLStringList imageBandList;
16✔
1451
    CPLStringList imageBandFileList;
16✔
1452
    const int nPolarizationsGridCount = aosPolarizationsGrids.size();
8✔
1453

1454
    /* File names for full resolution IPDFs. For GeoTIFF format, one entry per
1455
    pole; For NITF 2.1 format, only one entry. */
1456
    bool bIsNITF = false;
8✔
1457
    const char *pszNITF_Filename = nullptr;
8✔
1458
    int imageBandFileCount = 0;
8✔
1459
    int imageBandCount = 0;
8✔
1460

1461
    /* Split the polarization string*/
1462
    auto iss = std::istringstream((CPLString(pszPolarizations)).c_str());
24✔
1463
    auto pol = std::string{};
16✔
1464
    /* Count number of polarizations*/
1465
    while (iss >> pol)
24✔
1466
        imageBandCount++;
16✔
1467

1468
    CPLXMLNode *psNode = psImageAttributes->psChild;
8✔
1469
    for (; psNode != nullptr; psNode = psNode->psNext)
131✔
1470
    {
1471
        /* Find the tif or ntf filename */
1472
        if (psNode->eType != CXT_Element || !(EQUAL(psNode->pszValue, "ipdf")))
123✔
1473
            continue;
107✔
1474

1475
        /* --------------------------------------------------------------------
1476
         */
1477
        /*      Fetch ipdf image. Could be either tif or ntf. */
1478
        /*      Replace / by \\ */
1479
        /* --------------------------------------------------------------------
1480
         */
1481
        const char *pszBasedFilename = CPLGetXMLValue(psNode, "", "");
17✔
1482
        if (*pszBasedFilename == '\0')
17✔
1483
            continue;
×
1484

1485
        /* Count number of image names within ipdf tag*/
1486
        imageBandFileCount++;
17✔
1487

1488
        CPLString pszUpperBasedFilename(CPLString(pszBasedFilename).toupper());
17✔
1489

1490
        const bool bEndsWithNTF =
1491
            strlen(pszUpperBasedFilename.c_str()) > 4 &&
34✔
1492
            EQUAL(pszUpperBasedFilename.c_str() +
17✔
1493
                      strlen(pszUpperBasedFilename.c_str()) - 4,
1494
                  ".NTF");
1495

1496
        if (bEndsWithNTF)
17✔
1497
        {
1498
            /* Found it! It would not exist one more */
1499
            bIsNITF = true;
×
1500
            pszNITF_Filename = pszBasedFilename;
×
1501
            break;
×
1502
        }
1503
        else
1504
        {
1505
            /* Keep adding polarizations filename according to the pole */
1506
            const char *pszPole = CPLGetXMLValue(psNode, "pole", "");
17✔
1507
            if (*pszPole == '\0')
17✔
1508
            {
1509
                /* Guard against case when pole is a null string, skip it */
1510
                imageBandFileCount--;
×
1511
                continue;
×
1512
            }
1513

1514
            if (EQUAL(pszPole, "XC"))
17✔
1515
            {
1516
                /* Skip RCM MLC's 3rd band file ##XC.tif */
1517
                imageBandFileCount--;
1✔
1518
                continue;
1✔
1519
            }
1520

1521
            imageBandList.AddString(CPLString(pszPole).toupper());
16✔
1522
            imageBandFileList.AddString(pszBasedFilename);
16✔
1523
        }
1524
    }
1525

1526
    /* -------------------------------------------------------------------- */
1527
    /*      Incidence Angle in a sub-folder                                 */
1528
    /*      called 'calibration' from the 'metadata' folder                 */
1529
    /* -------------------------------------------------------------------- */
1530

1531
    const char *pszIncidenceAngleFileName = CPLGetXMLValue(
8✔
1532
        psImageReferenceAttributes, "incidenceAngleFileName", "");
1533

1534
    if (pszIncidenceAngleFileName != nullptr)
8✔
1535
    {
1536
        CPLString osIncidenceAngleFilePath = CPLFormFilenameSafe(
16✔
1537
            CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr).c_str(),
8✔
1538
            pszIncidenceAngleFileName, nullptr);
16✔
1539

1540
        /* Check if the file exist */
1541
        if (IsValidXMLFile(osIncidenceAngleFilePath))
8✔
1542
        {
1543
            CPLXMLTreeCloser psIncidenceAngle(
1544
                CPLParseXMLFile(osIncidenceAngleFilePath));
16✔
1545

1546
            int pixelFirstLutValue = atoi(
8✔
1547
                CPLGetXMLValue(psIncidenceAngle.get(),
8✔
1548
                               "=incidenceAngles.pixelFirstAnglesValue", "0"));
1549

1550
            const int stepSize = atoi(CPLGetXMLValue(
8✔
1551
                psIncidenceAngle.get(), "=incidenceAngles.stepSize", "0"));
8✔
1552
            const int numberOfValues =
1553
                atoi(CPLGetXMLValue(psIncidenceAngle.get(),
8✔
1554
                                    "=incidenceAngles.numberOfValues", "0"));
1555

1556
            if (!(stepSize == 0 || stepSize == INT_MIN ||
8✔
1557
                  numberOfValues == INT_MIN ||
1558
                  abs(numberOfValues) > INT_MAX / abs(stepSize)))
8✔
1559
            {
1560
                /* Get the Pixel Per range */
1561
                const int tableSize = abs(stepSize) * abs(numberOfValues);
8✔
1562

1563
                CPLString angles;
16✔
1564
                // Loop through all nodes with spaces
1565
                CPLXMLNode *psNextNode =
1566
                    CPLGetXMLNode(psIncidenceAngle.get(), "=incidenceAngles");
8✔
1567

1568
                CPLXMLNode *psNodeInc;
1569
                for (psNodeInc = psNextNode->psChild; psNodeInc != nullptr;
458✔
1570
                     psNodeInc = psNodeInc->psNext)
450✔
1571
                {
1572
                    if (EQUAL(psNodeInc->pszValue, "angles"))
450✔
1573
                    {
1574
                        if (angles.length() > 0)
402✔
1575
                        {
1576
                            angles.append(" "); /* separator */
394✔
1577
                        }
1578
                        const char *valAngle =
1579
                            CPLGetXMLValue(psNodeInc, "", "");
402✔
1580
                        angles.append(valAngle);
402✔
1581
                    }
1582
                }
1583

1584
                char **papszAngleList =
1585
                    CSLTokenizeString2(angles, " ", CSLT_HONOURSTRINGS);
8✔
1586

1587
                /* Allocate the right LUT size according to the product range pixel
1588
                 */
1589
                poDS->m_IncidenceAngleTableSize = tableSize;
8✔
1590
                poDS->m_nfIncidenceAngleTable =
16✔
1591
                    InterpolateValues(papszAngleList, tableSize, stepSize,
8✔
1592
                                      numberOfValues, pixelFirstLutValue);
1593

1594
                CSLDestroy(papszAngleList);
8✔
1595
            }
1596
        }
1597
    }
1598

1599
    for (int iPoleInx = 0; iPoleInx < nPolarizationsGridCount; iPoleInx++)
24✔
1600
    {
1601
        // Search for a specific band name
1602
        const CPLString pszPole =
1603
            CPLString(aosPolarizationsGrids[iPoleInx]).toupper();
16✔
1604

1605
        // Look if the NoiseLevel file xml exist for the
1606
        CPLXMLNode *psRefNode = psImageReferenceAttributes->psChild;
16✔
1607
        for (; psRefNode != nullptr; psRefNode = psRefNode->psNext)
232✔
1608
        {
1609
            if (EQUAL(psRefNode->pszValue, "noiseLevelFileName") && bCanCalib)
216✔
1610
            {
1611
                /* Determine which incidence angle correction this is */
1612
                const char *pszPoleToMatch =
1613
                    CPLGetXMLValue(psRefNode, "pole", "");
32✔
1614
                const char *pszNoiseLevelFile =
1615
                    CPLGetXMLValue(psRefNode, "", "");
32✔
1616

1617
                if (*pszPoleToMatch == '\0')
32✔
1618
                    continue;
16✔
1619

1620
                if (EQUAL(pszPoleToMatch, "XC"))
32✔
1621
                    /* Skip noise for RCM MLC's 3rd band XC */
1622
                    continue;
×
1623

1624
                if (EQUAL(pszNoiseLevelFile, ""))
32✔
1625
                    continue;
×
1626

1627
                /* --------------------------------------------------------------------
1628
                 */
1629
                /*      With RCM, LUT file is different per polarizarion (pole)
1630
                 */
1631
                /*      The following code make sure to loop through all
1632
                 * possible       */
1633
                /*      'noiseLevelFileName' and match the <ipdf 'pole' name */
1634
                /* --------------------------------------------------------------------
1635
                 */
1636
                if (pszPole.compare(pszPoleToMatch) != 0)
32✔
1637
                {
1638
                    continue;
16✔
1639
                }
1640

1641
                /* --------------------------------------------------------------------
1642
                 */
1643
                /*      LUT calibration is unique per pole in a sub-folder */
1644
                /*      called 'calibration' from the 'metadata' folder */
1645
                /* --------------------------------------------------------------------
1646
                 */
1647

1648
                CPLString oNoiseLevelPath = CPLFormFilenameSafe(
32✔
1649
                    CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr)
16✔
1650
                        .c_str(),
1651
                    pszNoiseLevelFile, nullptr);
32✔
1652
                if (IsValidXMLFile(oNoiseLevelPath))
16✔
1653
                {
1654
                    osNoiseLevelsValues = std::move(oNoiseLevelPath);
16✔
1655
                }
1656
            }
1657
        }
1658

1659
        // Search again with different file
1660
        psRefNode = psImageReferenceAttributes->psChild;
16✔
1661
        for (; psRefNode != nullptr; psRefNode = psRefNode->psNext)
232✔
1662
        {
1663
            if (EQUAL(psRefNode->pszValue, "lookupTableFileName") && bCanCalib)
216✔
1664
            {
1665
                /* Determine which incidence angle correction this is */
1666
                const char *pszLUTType =
1667
                    CPLGetXMLValue(psRefNode, "sarCalibrationType", "");
102✔
1668
                const char *pszPoleToMatch =
1669
                    CPLGetXMLValue(psRefNode, "pole", "");
102✔
1670
                const char *pszLUTFile = CPLGetXMLValue(psRefNode, "", "");
102✔
1671

1672
                if (*pszPoleToMatch == '\0')
102✔
1673
                    continue;
54✔
1674

1675
                if (EQUAL(pszPoleToMatch, "XC"))
102✔
1676
                    /* Skip Calib for RCM MLC's 3rd band XC */
1677
                    continue;
6✔
1678

1679
                if (*pszLUTType == '\0')
96✔
1680
                    continue;
×
1681

1682
                if (EQUAL(pszLUTType, ""))
96✔
1683
                    continue;
×
1684

1685
                /* --------------------------------------------------------------------
1686
                 */
1687
                /*      With RCM, LUT file is different per polarizarion (pole)
1688
                 */
1689
                /*      The following code make sure to loop through all
1690
                 * possible       */
1691
                /*      'lookupTableFileName' and match the <ipdf 'pole' name */
1692
                /* --------------------------------------------------------------------
1693
                 */
1694
                if (pszPole.compare(pszPoleToMatch) != 0)
96✔
1695
                {
1696
                    continue;
48✔
1697
                }
1698

1699
                /* --------------------------------------------------------------------
1700
                 */
1701
                /*      LUT calibration is unique per pole in a sub-folder */
1702
                /*      called 'calibration' from the 'metadata' folder */
1703
                /* --------------------------------------------------------------------
1704
                 */
1705
                const CPLString osLUTFilePath = CPLFormFilenameSafe(
96✔
1706
                    CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr)
48✔
1707
                        .c_str(),
1708
                    pszLUTFile, nullptr);
96✔
1709

1710
                if (EQUAL(pszLUTType, "Beta Nought") &&
64✔
1711
                    IsValidXMLFile(osLUTFilePath))
16✔
1712
                {
1713
                    poDS->papszExtraFiles =
32✔
1714
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
16✔
1715

1716
                    CPLString pszBuf(
1717
                        FormatCalibration(szBETA0, osMDFilename.c_str()));
16✔
1718
                    CPLFree(pszBeta0LUT);
16✔
1719
                    pszBeta0LUT = VSIStrdup(osLUTFilePath);
16✔
1720

1721
                    const char *oldLut =
1722
                        poDS->GetMetadataItem("BETA_NOUGHT_LUT");
16✔
1723
                    if (oldLut == nullptr)
16✔
1724
                    {
1725
                        poDS->SetMetadataItem("BETA_NOUGHT_LUT", osLUTFilePath);
8✔
1726
                    }
1727
                    else
1728
                    {
1729
                        /* Keep adding LUT file for all bands, should be planty
1730
                         * of space */
1731
                        char *ptrConcatLut =
1732
                            static_cast<char *>(CPLMalloc(2048));
8✔
1733
                        ptrConcatLut[0] =
8✔
1734
                            '\0'; /* Just initialize the first byte */
1735
                        strcat(ptrConcatLut, oldLut);
8✔
1736
                        strcat(ptrConcatLut, ",");
8✔
1737
                        strcat(ptrConcatLut, osLUTFilePath);
8✔
1738
                        poDS->SetMetadataItem("BETA_NOUGHT_LUT", ptrConcatLut);
8✔
1739
                        CPLFree(ptrConcatLut);
8✔
1740
                    }
1741

1742
                    poDS->papszSubDatasets = CSLSetNameValue(
32✔
1743
                        poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf);
16✔
1744
                    poDS->papszSubDatasets = CSLSetNameValue(
16✔
1745
                        poDS->papszSubDatasets, "SUBDATASET_3_DESC",
16✔
1746
                        "Beta Nought calibrated");
1747
                }
1748
                else if (EQUAL(pszLUTType, "Sigma Nought") &&
48✔
1749
                         IsValidXMLFile(osLUTFilePath))
16✔
1750
                {
1751
                    poDS->papszExtraFiles =
32✔
1752
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
16✔
1753

1754
                    CPLString pszBuf(
1755
                        FormatCalibration(szSIGMA0, osMDFilename.c_str()));
16✔
1756
                    CPLFree(pszSigma0LUT);
16✔
1757
                    pszSigma0LUT = VSIStrdup(osLUTFilePath);
16✔
1758

1759
                    const char *oldLut =
1760
                        poDS->GetMetadataItem("SIGMA_NOUGHT_LUT");
16✔
1761
                    if (oldLut == nullptr)
16✔
1762
                    {
1763
                        poDS->SetMetadataItem("SIGMA_NOUGHT_LUT",
16✔
1764
                                              osLUTFilePath);
8✔
1765
                    }
1766
                    else
1767
                    {
1768
                        /* Keep adding LUT file for all bands, should be planty
1769
                         * of space */
1770
                        char *ptrConcatLut =
1771
                            static_cast<char *>(CPLMalloc(2048));
8✔
1772
                        ptrConcatLut[0] =
8✔
1773
                            '\0'; /* Just initialize the first byte */
1774
                        strcat(ptrConcatLut, oldLut);
8✔
1775
                        strcat(ptrConcatLut, ",");
8✔
1776
                        strcat(ptrConcatLut, osLUTFilePath);
8✔
1777
                        poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", ptrConcatLut);
8✔
1778
                        CPLFree(ptrConcatLut);
8✔
1779
                    }
1780

1781
                    poDS->papszSubDatasets = CSLSetNameValue(
32✔
1782
                        poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf);
16✔
1783
                    poDS->papszSubDatasets = CSLSetNameValue(
16✔
1784
                        poDS->papszSubDatasets, "SUBDATASET_2_DESC",
16✔
1785
                        "Sigma Nought calibrated");
1786
                }
1787
                else if (EQUAL(pszLUTType, "Gamma") &&
32✔
1788
                         IsValidXMLFile(osLUTFilePath))
16✔
1789
                {
1790
                    poDS->papszExtraFiles =
32✔
1791
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
16✔
1792

1793
                    CPLString pszBuf(
1794
                        FormatCalibration(szGAMMA, osMDFilename.c_str()));
16✔
1795
                    CPLFree(pszGammaLUT);
16✔
1796
                    pszGammaLUT = VSIStrdup(osLUTFilePath);
16✔
1797

1798
                    const char *oldLut = poDS->GetMetadataItem("GAMMA_LUT");
16✔
1799
                    if (oldLut == nullptr)
16✔
1800
                    {
1801
                        poDS->SetMetadataItem("GAMMA_LUT", osLUTFilePath);
8✔
1802
                    }
1803
                    else
1804
                    {
1805
                        /* Keep adding LUT file for all bands, should be planty
1806
                         * of space */
1807
                        char *ptrConcatLut =
1808
                            static_cast<char *>(CPLMalloc(2048));
8✔
1809
                        ptrConcatLut[0] =
8✔
1810
                            '\0'; /* Just initialize the first byte */
1811
                        strcat(ptrConcatLut, oldLut);
8✔
1812
                        strcat(ptrConcatLut, ",");
8✔
1813
                        strcat(ptrConcatLut, osLUTFilePath);
8✔
1814
                        poDS->SetMetadataItem("GAMMA_LUT", ptrConcatLut);
8✔
1815
                        CPLFree(ptrConcatLut);
8✔
1816
                    }
1817

1818
                    poDS->papszSubDatasets = CSLSetNameValue(
32✔
1819
                        poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf);
16✔
1820
                    poDS->papszSubDatasets = CSLSetNameValue(
16✔
1821
                        poDS->papszSubDatasets, "SUBDATASET_4_DESC",
16✔
1822
                        "Gamma calibrated");
1823
                }
1824
            }
1825
        }
1826

1827
        /* --------------------------------------------------------------------
1828
         */
1829
        /*      Fetch ipdf image. Could be either tif or ntf. */
1830
        /*      Replace / by \\ */
1831
        /* --------------------------------------------------------------------
1832
         */
1833
        const char *pszBasedFilename;
1834
        if (bIsNITF)
16✔
1835
        {
1836
            pszBasedFilename = pszNITF_Filename;
×
1837
        }
1838
        else
1839
        {
1840
            const int bandPositionIndex = imageBandList.FindString(pszPole);
16✔
1841
            if (bandPositionIndex < 0)
16✔
1842
            {
1843
                CPLFree(imageBandList);
×
1844
                CPLFree(imageBandFileList);
×
1845

1846
                CPLError(CE_Failure, CPLE_OpenFailed,
×
1847
                         "ERROR: RCM cannot find the polarization %s. Please "
1848
                         "contact your data provider for a corrected dataset",
1849
                         pszPole.c_str());
1850
                return nullptr;
×
1851
            }
1852

1853
            pszBasedFilename = imageBandFileList[bandPositionIndex];
16✔
1854
        }
1855

1856
        /* --------------------------------------------------------------------
1857
         */
1858
        /*      Form full filename (path of product.xml + basename). */
1859
        /* --------------------------------------------------------------------
1860
         */
1861
        char *pszFullname = CPLStrdup(
16✔
1862
            CPLFormFilenameSafe(osPath, pszBasedFilename, nullptr).c_str());
32✔
1863

1864
        /* --------------------------------------------------------------------
1865
         */
1866
        /*      Try and open the file. */
1867
        /* --------------------------------------------------------------------
1868
         */
1869
        auto poBandFile = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1870
            pszFullname, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
16✔
1871
        if (poBandFile == nullptr)
16✔
1872
        {
1873
            CPLFree(pszFullname);
×
1874
            continue;
×
1875
        }
1876
        if (poBandFile->GetRasterCount() == 0)
16✔
1877
        {
1878
            CPLFree(pszFullname);
×
1879
            continue;
×
1880
        }
1881

1882
        poDS->papszExtraFiles =
32✔
1883
            CSLAddString(poDS->papszExtraFiles, pszFullname);
16✔
1884

1885
        /* Some CFloat32 NITF files have nBitsPerSample incorrectly reported */
1886
        /* as 16, and get misinterpreted as CInt16.  Check the underlying NITF
1887
         */
1888
        /* and override if this is the case. */
1889
        if (poBandFile->GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
16✔
1890
            eDataType = GDT_CFloat32;
×
1891

1892
        BandMappingRCM b =
1893
            checkBandFileMappingRCM(eDataType, poBandFile.get(), bIsNITF);
16✔
1894
        if (b == BANDERROR)
16✔
1895
        {
1896
            CPLFree(pszFullname);
×
1897
            CPLError(CE_Failure, CPLE_AppDefined,
×
1898
                     "The underlying band files do not have an appropriate "
1899
                     "data type.");
1900
            return nullptr;
×
1901
        }
1902
        bool twoBandComplex = b == TWOBANDCOMPLEX;
16✔
1903
        bool isOneFilePerPol = (imageBandCount == imageBandFileCount);
16✔
1904

1905
        /* --------------------------------------------------------------------
1906
         */
1907
        /*      Create the band. */
1908
        /* --------------------------------------------------------------------
1909
         */
1910
        int bandNum = poDS->GetRasterCount() + 1;
16✔
1911
        if (eCalib == None || eCalib == Uncalib)
16✔
1912
        {
1913
            RCMRasterBand *poBand = new RCMRasterBand(
1914
                poDS.get(), bandNum, eDataType, pszPole, poBandFile.release(),
20✔
1915
                twoBandComplex, isOneFilePerPol, bIsNITF);
20✔
1916

1917
            poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
10✔
1918
        }
1919
        else
1920
        {
1921
            const char *pszLUT = nullptr;
6✔
1922
            switch (eCalib)
6✔
1923
            {
1924
                case Sigma0:
2✔
1925
                    pszLUT = pszSigma0LUT;
2✔
1926
                    break;
2✔
1927
                case Beta0:
2✔
1928
                    pszLUT = pszBeta0LUT;
2✔
1929
                    break;
2✔
1930
                case Gamma:
2✔
1931
                    pszLUT = pszGammaLUT;
2✔
1932
                    break;
2✔
1933
                default:
×
1934
                    /* we should bomb gracefully... */
1935
                    pszLUT = pszSigma0LUT;
×
1936
            }
1937
            if (!pszLUT)
6✔
1938
            {
1939
                CPLFree(pszFullname);
×
1940
                CPLError(CE_Failure, CPLE_AppDefined, "LUT missing.");
×
1941
                return nullptr;
×
1942
            }
1943

1944
            // The variable 'osNoiseLevelsValues' is always the same for a ban
1945
            // name except the XML contains different calibration name
1946
            if (poDS->isComplexData)
6✔
1947
            {
1948
                // If Complex, always 32 bits
1949
                RCMCalibRasterBand *poBand = new RCMCalibRasterBand(
1950
                    poDS.get(), pszPole, GDT_Float32, poBandFile.release(),
×
1951
                    eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType);
×
1952
                poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
×
1953
            }
1954
            else
1955
            {
1956
                // Whatever the datatype was previoulsy set
1957
                RCMCalibRasterBand *poBand = new RCMCalibRasterBand(
1958
                    poDS.get(), pszPole, eDataType, poBandFile.release(),
12✔
1959
                    eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType);
12✔
1960
                poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
6✔
1961
            }
1962
        }
1963

1964
        CPLFree(pszFullname);
16✔
1965
    }
1966

1967
    if (poDS->papszSubDatasets != nullptr && eCalib == None)
8✔
1968
    {
1969
        CPLString pszBuf = FormatCalibration(szUNCALIB, osMDFilename.c_str());
4✔
1970
        poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
4✔
1971
                                                 "SUBDATASET_1_NAME", pszBuf);
1972
        poDS->papszSubDatasets =
4✔
1973
            CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_1_DESC",
4✔
1974
                            "Uncalibrated digital numbers");
1975
    }
1976
    else if (poDS->papszSubDatasets != nullptr)
4✔
1977
    {
1978
        CSLDestroy(poDS->papszSubDatasets);
4✔
1979
        poDS->papszSubDatasets = nullptr;
4✔
1980
    }
1981

1982
    /* -------------------------------------------------------------------- */
1983
    /*      Set the appropriate MATRIX_REPRESENTATION.                      */
1984
    /* -------------------------------------------------------------------- */
1985

1986
    if (poDS->GetRasterCount() == 4 &&
8✔
1987
        (eDataType == GDT_CInt16 || eDataType == GDT_CFloat32))
×
1988
    {
1989
        poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
×
1990
    }
1991

1992
    /* -------------------------------------------------------------------- */
1993
    /*      Collect a few useful metadata items.                            */
1994
    /* -------------------------------------------------------------------- */
1995

1996
    pszItem = CPLGetXMLValue(psSourceAttrs, "satellite", "");
8✔
1997
    poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem);
8✔
1998

1999
    pszItem = CPLGetXMLValue(psSourceAttrs, "sensor", "");
8✔
2000
    poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem);
8✔
2001

2002
    /* Get beam mode mnemonic */
2003
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamMode", "UNK");
8✔
2004
    poDS->SetMetadataItem("BEAM_MODE", pszItem);
8✔
2005

2006
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeMnemonic", "UNK");
8✔
2007
    poDS->SetMetadataItem("BEAM_MODE_MNEMONIC", pszItem);
8✔
2008

2009
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeDefinitionId", "UNK");
8✔
2010
    poDS->SetMetadataItem("BEAM_MODE_DEFINITION_ID", pszItem);
8✔
2011

2012
    pszItem = CPLGetXMLValue(psSourceAttrs, "rawDataStartTime", "UNK");
8✔
2013
    poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem);
8✔
2014

2015
    pszItem = CPLGetXMLValue(psSourceAttrs, "inputDatasetFacilityId", "UNK");
8✔
2016
    poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem);
8✔
2017

2018
    pszItem = CPLGetXMLValue(psSourceAttrs,
8✔
2019
                             "orbitAndAttitude.orbitInformation.passDirection",
2020
                             "UNK");
2021
    poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem);
8✔
2022
    pszItem = CPLGetXMLValue(
8✔
2023
        psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataSource",
2024
        "UNK");
2025
    poDS->SetMetadataItem("ORBIT_DATA_SOURCE", pszItem);
8✔
2026
    pszItem = CPLGetXMLValue(
8✔
2027
        psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataFileName",
2028
        "UNK");
2029
    poDS->SetMetadataItem("ORBIT_DATA_FILE", pszItem);
8✔
2030

2031
    /* Get incidence angle information. DONE */
2032
    pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngNearRng",
8✔
2033
                             "UNK");
2034
    poDS->SetMetadataItem("NEAR_RANGE_INCIDENCE_ANGLE", pszItem);
8✔
2035

2036
    pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngFarRng",
8✔
2037
                             "UNK");
2038
    poDS->SetMetadataItem("FAR_RANGE_INCIDENCE_ANGLE", pszItem);
8✔
2039

2040
    pszItem = CPLGetXMLValue(psSceneAttributes,
8✔
2041
                             "imageAttributes.slantRangeNearEdge", "UNK");
2042
    poDS->SetMetadataItem("SLANT_RANGE_NEAR_EDGE", pszItem);
8✔
2043

2044
    pszItem = CPLGetXMLValue(psSceneAttributes,
8✔
2045
                             "imageAttributes.slantRangeFarEdge", "UNK");
2046
    poDS->SetMetadataItem("SLANT_RANGE_FAR_EDGE", pszItem);
8✔
2047

2048
    /*--------------------------------------------------------------------- */
2049
    /*      Collect Map projection/Geotransform information, if present.DONE */
2050
    /*      In RCM, there is no file that indicates                         */
2051
    /* -------------------------------------------------------------------- */
2052
    CPLXMLNode *psMapProjection = CPLGetXMLNode(
8✔
2053
        psImageReferenceAttributes, "geographicInformation.mapProjection");
2054

2055
    if (psMapProjection != nullptr)
8✔
2056
    {
2057
        CPLXMLNode *psPos =
2058
            CPLGetXMLNode(psMapProjection, "positioningInformation");
×
2059

2060
        pszItem =
2061
            CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "UNK");
×
2062
        poDS->SetMetadataItem("MAP_PROJECTION_DESCRIPTOR", pszItem);
×
2063

2064
        pszItem =
2065
            CPLGetXMLValue(psMapProjection, "mapProjectionOrientation", "UNK");
×
2066
        poDS->SetMetadataItem("MAP_PROJECTION_ORIENTATION", pszItem);
×
2067

2068
        pszItem = CPLGetXMLValue(psMapProjection, "resamplingKernel", "UNK");
×
2069
        poDS->SetMetadataItem("RESAMPLING_KERNEL", pszItem);
×
2070

2071
        pszItem = CPLGetXMLValue(psMapProjection, "satelliteHeading", "UNK");
×
2072
        poDS->SetMetadataItem("SATELLITE_HEADING", pszItem);
×
2073

2074
        if (psPos != nullptr)
×
2075
        {
2076
            const double tl_x = CPLStrtod(
×
2077
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.easting",
2078
                               "0.0"),
2079
                nullptr);
2080
            const double tl_y = CPLStrtod(
×
2081
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.northing",
2082
                               "0.0"),
2083
                nullptr);
2084
            const double bl_x = CPLStrtod(
×
2085
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.easting",
2086
                               "0.0"),
2087
                nullptr);
2088
            const double bl_y = CPLStrtod(
×
2089
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.northing",
2090
                               "0.0"),
2091
                nullptr);
2092
            const double tr_x = CPLStrtod(
×
2093
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.easting",
2094
                               "0.0"),
2095
                nullptr);
2096
            const double tr_y = CPLStrtod(
×
2097
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.northing",
2098
                               "0.0"),
2099
                nullptr);
2100
            poDS->m_gt[1] = (tr_x - tl_x) / (poDS->nRasterXSize - 1);
×
2101
            poDS->m_gt[4] = (tr_y - tl_y) / (poDS->nRasterXSize - 1);
×
2102
            poDS->m_gt[2] = (bl_x - tl_x) / (poDS->nRasterYSize - 1);
×
2103
            poDS->m_gt[5] = (bl_y - tl_y) / (poDS->nRasterYSize - 1);
×
2104
            poDS->m_gt[0] = (tl_x - 0.5 * poDS->m_gt[1] - 0.5 * poDS->m_gt[2]);
×
2105
            poDS->m_gt[3] = (tl_y - 0.5 * poDS->m_gt[4] - 0.5 * poDS->m_gt[5]);
×
2106

2107
            /* Use bottom right pixel to test geotransform */
2108
            const double br_x = CPLStrtod(
×
2109
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.easting",
2110
                               "0.0"),
2111
                nullptr);
2112
            const double br_y = CPLStrtod(
×
2113
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.northing",
2114
                               "0.0"),
2115
                nullptr);
2116
            const double testx = poDS->m_gt[0] +
×
2117
                                 poDS->m_gt[1] * (poDS->nRasterXSize - 0.5) +
×
2118
                                 poDS->m_gt[2] * (poDS->nRasterYSize - 0.5);
×
2119
            const double testy = poDS->m_gt[3] +
×
2120
                                 poDS->m_gt[4] * (poDS->nRasterXSize - 0.5) +
×
2121
                                 poDS->m_gt[5] * (poDS->nRasterYSize - 0.5);
×
2122

2123
            /* Give 1/4 pixel numerical error leeway in calculating location
2124
            based on affine transform */
2125
            if ((fabs(testx - br_x) >
×
2126
                 fabs(0.25 * (poDS->m_gt[1] + poDS->m_gt[2]))) ||
×
2127
                (fabs(testy - br_y) >
×
2128
                 fabs(0.25 * (poDS->m_gt[4] + poDS->m_gt[5]))))
×
2129
            {
2130
                CPLError(CE_Warning, CPLE_AppDefined,
×
2131
                         "WARNING: Unexpected error in calculating affine "
2132
                         "transform: corner coordinates inconsistent.");
2133
            }
2134
            else
2135
            {
2136
                poDS->bHaveGeoTransform = TRUE;
×
2137
            }
2138
        }
2139
    }
2140

2141
    /* -------------------------------------------------------------------- */
2142
    /*      Collect Projection String Information.DONE                      */
2143
    /* -------------------------------------------------------------------- */
2144
    CPLXMLNode *psEllipsoid =
2145
        CPLGetXMLNode(psImageReferenceAttributes,
8✔
2146
                      "geographicInformation.ellipsoidParameters");
2147

2148
    if (psEllipsoid != nullptr)
8✔
2149
    {
2150
        OGRSpatialReference oLL, oPrj;
16✔
2151

2152
        const char *pszGeodeticTerrainHeight =
2153
            CPLGetXMLValue(psEllipsoid, "geodeticTerrainHeight", "UNK");
8✔
2154
        poDS->SetMetadataItem("GEODETIC_TERRAIN_HEIGHT",
8✔
2155
                              pszGeodeticTerrainHeight);
8✔
2156

2157
        const char *pszEllipsoidName =
2158
            CPLGetXMLValue(psEllipsoid, "ellipsoidName", "");
8✔
2159
        double minor_axis =
2160
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMinorAxis", "0.0"));
8✔
2161
        double major_axis =
2162
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMajorAxis", "0.0"));
8✔
2163

2164
        if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) ||
8✔
2165
            (major_axis == 0.0))
2166
        {
2167
            oLL.SetWellKnownGeogCS("WGS84");
×
2168
            oPrj.SetWellKnownGeogCS("WGS84");
×
2169

2170
            CPLError(CE_Warning, CPLE_AppDefined,
×
2171
                     "WARNING: Incomplete ellipsoid "
2172
                     "information.  Using wgs-84 parameters.");
2173
        }
2174
        else if (EQUAL(pszEllipsoidName, "WGS84") ||
8✔
2175
                 EQUAL(pszEllipsoidName, "WGS 1984"))
8✔
2176
        {
2177
            oLL.SetWellKnownGeogCS("WGS84");
8✔
2178
            oPrj.SetWellKnownGeogCS("WGS84");
8✔
2179
        }
2180
        else
2181
        {
2182
            const double inv_flattening =
×
2183
                major_axis / (major_axis - minor_axis);
×
2184
            oLL.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening);
×
2185
            oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis,
×
2186
                           inv_flattening);
2187
        }
2188

2189
        if (psMapProjection != nullptr)
8✔
2190
        {
2191
            const char *pszProj =
2192
                CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "");
×
2193
            bool bUseProjInfo = false;
×
2194

2195
            CPLXMLNode *psUtmParams =
2196
                CPLGetXMLNode(psMapProjection, "utmProjectionParameters");
×
2197

2198
            CPLXMLNode *psNspParams =
2199
                CPLGetXMLNode(psMapProjection, "nspProjectionParameters");
×
2200

2201
            if ((psUtmParams != nullptr) && poDS->bHaveGeoTransform)
×
2202
            {
2203
                /* double origEasting, origNorthing; */
2204
                bool bNorth = true;
×
2205

2206
                const int utmZone =
2207
                    atoi(CPLGetXMLValue(psUtmParams, "utmZone", ""));
×
2208
                const char *pszHemisphere =
2209
                    CPLGetXMLValue(psUtmParams, "hemisphere", "");
×
2210
#if 0
2211
                origEasting = CPLStrtod(CPLGetXMLValue(
2212
                    psUtmParams, "mapOriginFalseEasting", "0.0"), nullptr);
2213
                origNorthing = CPLStrtod(CPLGetXMLValue(
2214
                    psUtmParams, "mapOriginFalseNorthing", "0.0"), nullptr);
2215
#endif
2216
                if (STARTS_WITH_CI(pszHemisphere, "southern"))
×
2217
                    bNorth = FALSE;
×
2218

2219
                if (STARTS_WITH_CI(pszProj, "UTM"))
×
2220
                {
2221
                    oPrj.SetUTM(utmZone, bNorth);
×
2222
                    bUseProjInfo = true;
×
2223
                }
2224
            }
2225
            else if ((psNspParams != nullptr) && poDS->bHaveGeoTransform)
×
2226
            {
2227
                const double origEasting = CPLStrtod(
×
2228
                    CPLGetXMLValue(psNspParams, "mapOriginFalseEasting", "0.0"),
2229
                    nullptr);
2230
                const double origNorthing =
2231
                    CPLStrtod(CPLGetXMLValue(psNspParams,
×
2232
                                             "mapOriginFalseNorthing", "0.0"),
2233
                              nullptr);
2234
                const double copLong = CPLStrtod(
×
2235
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLongitude",
2236
                                   "0.0"),
2237
                    nullptr);
2238
                const double copLat = CPLStrtod(
×
2239
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLatitude",
2240
                                   "0.0"),
2241
                    nullptr);
2242
                const double sP1 = CPLStrtod(
×
2243
                    CPLGetXMLValue(psNspParams, "standardParallels1", "0.0"),
2244
                    nullptr);
2245
                const double sP2 = CPLStrtod(
×
2246
                    CPLGetXMLValue(psNspParams, "standardParallels2", "0.0"),
2247
                    nullptr);
2248

2249
                if (STARTS_WITH_CI(pszProj, "ARC"))
×
2250
                {
2251
                    /* Albers Conical Equal Area */
2252
                    oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting,
×
2253
                                 origNorthing);
2254
                    bUseProjInfo = true;
×
2255
                }
2256
                else if (STARTS_WITH_CI(pszProj, "LCC"))
×
2257
                {
2258
                    /* Lambert Conformal Conic */
2259
                    oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting,
×
2260
                                origNorthing);
2261
                    bUseProjInfo = true;
×
2262
                }
2263
                else if (STARTS_WITH_CI(pszProj, "STPL"))
×
2264
                {
2265
                    /* State Plate
2266
                    ASSUMPTIONS: "zone" in product.xml matches USGS
2267
                    definition as expected by ogr spatial reference; NAD83
2268
                    zones (versus NAD27) are assumed. */
2269

2270
                    const int nSPZone =
2271
                        atoi(CPLGetXMLValue(psNspParams, "zone", "1"));
×
2272

2273
                    oPrj.SetStatePlane(nSPZone, TRUE, nullptr, 0.0);
×
2274
                    bUseProjInfo = true;
×
2275
                }
2276
            }
2277

2278
            if (bUseProjInfo)
×
2279
            {
2280
                poDS->m_oSRS = std::move(oPrj);
×
2281
            }
2282
            else
2283
            {
2284
                CPLError(CE_Warning, CPLE_AppDefined,
×
2285
                         "WARNING: Unable to interpret projection information; "
2286
                         "check mapProjection info in product.xml!");
2287
            }
2288
        }
2289

2290
        poDS->m_oGCPSRS = std::move(oLL);
8✔
2291
    }
2292

2293
    /* -------------------------------------------------------------------- */
2294
    /*      Collect GCPs.DONE */
2295
    /* -------------------------------------------------------------------- */
2296
    CPLXMLNode *psGeoGrid = CPLGetXMLNode(
8✔
2297
        psImageReferenceAttributes, "geographicInformation.geolocationGrid");
2298

2299
    if (psGeoGrid != nullptr)
8✔
2300
    {
2301
        /* count GCPs */
2302
        poDS->nGCPCount = 0;
8✔
2303

2304
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
136✔
2305
             psNode = psNode->psNext)
128✔
2306
        {
2307
            if (EQUAL(psNode->pszValue, "imageTiePoint"))
128✔
2308
                poDS->nGCPCount++;
128✔
2309
        }
2310

2311
        poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>(
16✔
2312
            CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount));
8✔
2313

2314
        poDS->nGCPCount = 0;
8✔
2315

2316
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
136✔
2317
             psNode = psNode->psNext)
128✔
2318
        {
2319
            GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount;
128✔
2320

2321
            if (!EQUAL(psNode->pszValue, "imageTiePoint"))
128✔
2322
                continue;
×
2323

2324
            poDS->nGCPCount++;
128✔
2325

2326
            char szID[32];
2327
            snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount);
128✔
2328
            psGCP->pszId = CPLStrdup(szID);
128✔
2329
            psGCP->pszInfo = CPLStrdup("");
128✔
2330
            psGCP->dfGCPPixel =
128✔
2331
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.pixel", "0"));
128✔
2332
            psGCP->dfGCPLine =
128✔
2333
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.line", "0"));
128✔
2334
            psGCP->dfGCPX = CPLAtof(
128✔
2335
                CPLGetXMLValue(psNode, "geodeticCoordinate.longitude", ""));
2336
            psGCP->dfGCPY = CPLAtof(
128✔
2337
                CPLGetXMLValue(psNode, "geodeticCoordinate.latitude", ""));
2338
            psGCP->dfGCPZ = CPLAtof(
128✔
2339
                CPLGetXMLValue(psNode, "geodeticCoordinate.height", ""));
2340
        }
2341
    }
2342

2343
    if (pszBeta0LUT)
8✔
2344
        CPLFree(pszBeta0LUT);
8✔
2345
    if (pszSigma0LUT)
8✔
2346
        CPLFree(pszSigma0LUT);
8✔
2347
    if (pszGammaLUT)
8✔
2348
        CPLFree(pszGammaLUT);
8✔
2349

2350
    /* -------------------------------------------------------------------- */
2351
    /*      Collect RPC.DONE */
2352
    /* -------------------------------------------------------------------- */
2353
    CPLXMLNode *psRationalFunctions = CPLGetXMLNode(
8✔
2354
        psImageReferenceAttributes, "geographicInformation.rationalFunctions");
2355
    if (psRationalFunctions != nullptr)
8✔
2356
    {
2357
        char **papszRPC = nullptr;
8✔
2358
        static const char *const apszXMLToGDALMapping[] = {
2359
            "biasError",
2360
            "ERR_BIAS",
2361
            "randomError",
2362
            "ERR_RAND",
2363
            //"lineFitQuality", "????",
2364
            //"pixelFitQuality", "????",
2365
            "lineOffset",
2366
            "LINE_OFF",
2367
            "pixelOffset",
2368
            "SAMP_OFF",
2369
            "latitudeOffset",
2370
            "LAT_OFF",
2371
            "longitudeOffset",
2372
            "LONG_OFF",
2373
            "heightOffset",
2374
            "HEIGHT_OFF",
2375
            "lineScale",
2376
            "LINE_SCALE",
2377
            "pixelScale",
2378
            "SAMP_SCALE",
2379
            "latitudeScale",
2380
            "LAT_SCALE",
2381
            "longitudeScale",
2382
            "LONG_SCALE",
2383
            "heightScale",
2384
            "HEIGHT_SCALE",
2385
            "lineNumeratorCoefficients",
2386
            "LINE_NUM_COEFF",
2387
            "lineDenominatorCoefficients",
2388
            "LINE_DEN_COEFF",
2389
            "pixelNumeratorCoefficients",
2390
            "SAMP_NUM_COEFF",
2391
            "pixelDenominatorCoefficients",
2392
            "SAMP_DEN_COEFF",
2393
        };
2394
        for (size_t i = 0; i < CPL_ARRAYSIZE(apszXMLToGDALMapping); i += 2)
136✔
2395
        {
2396
            const char *pszValue = CPLGetXMLValue(
256✔
2397
                psRationalFunctions, apszXMLToGDALMapping[i], nullptr);
128✔
2398
            if (pszValue)
128✔
2399
                papszRPC = CSLSetNameValue(
128✔
2400
                    papszRPC, apszXMLToGDALMapping[i + 1], pszValue);
128✔
2401
        }
2402
        poDS->GDALDataset::SetMetadata(papszRPC, "RPC");
8✔
2403
        CSLDestroy(papszRPC);
8✔
2404
    }
2405

2406
    /* -------------------------------------------------------------------- */
2407
    /*      Initialize any PAM information.                                 */
2408
    /* -------------------------------------------------------------------- */
2409
    CPLString osDescription;
16✔
2410
    CPLString osSubdatasetName;
16✔
2411
    bool useSubdatasets = true;
8✔
2412

2413
    switch (eCalib)
8✔
2414
    {
2415
        case Sigma0:
1✔
2416
        {
2417
            osSubdatasetName = szSIGMA0;
1✔
2418
            osDescription = FormatCalibration(szSIGMA0, osMDFilename.c_str());
1✔
2419
        }
2420
        break;
1✔
2421
        case Beta0:
1✔
2422
        {
2423
            osSubdatasetName = szBETA0;
1✔
2424
            osDescription = FormatCalibration(szBETA0, osMDFilename.c_str());
1✔
2425
        }
2426
        break;
1✔
2427
        case Gamma:
1✔
2428
        {
2429
            osSubdatasetName = szGAMMA;
1✔
2430
            osDescription = FormatCalibration(szGAMMA, osMDFilename.c_str());
1✔
2431
        }
2432
        break;
1✔
2433
        case Uncalib:
1✔
2434
        {
2435
            osSubdatasetName = szUNCALIB;
1✔
2436
            osDescription = FormatCalibration(szUNCALIB, osMDFilename.c_str());
1✔
2437
        }
2438
        break;
1✔
2439
        default:
4✔
2440
            osSubdatasetName = szUNCALIB;
4✔
2441
            osDescription = osMDFilename;
4✔
2442
            useSubdatasets = false;
4✔
2443
    }
2444

2445
    CPL_IGNORE_RET_VAL(osSubdatasetName);
8✔
2446

2447
    if (eCalib != None)
8✔
2448
        poDS->papszExtraFiles =
4✔
2449
            CSLAddString(poDS->papszExtraFiles, osMDFilename);
4✔
2450

2451
    /* -------------------------------------------------------------------- */
2452
    /*      Initialize any PAM information.                                 */
2453
    /* -------------------------------------------------------------------- */
2454
    poDS->SetDescription(osDescription);
8✔
2455

2456
    poDS->SetPhysicalFilename(osMDFilename);
8✔
2457
    poDS->SetSubdatasetName(osDescription);
8✔
2458

2459
    poDS->TryLoadXML();
8✔
2460

2461
    /* -------------------------------------------------------------------- */
2462
    /*      Check for overviews.                                            */
2463
    /* -------------------------------------------------------------------- */
2464
    if (useSubdatasets)
8✔
2465
        poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
4✔
2466
    else
2467
        poDS->oOvManager.Initialize(poDS.get(), osMDFilename);
4✔
2468

2469
    return poDS.release();
8✔
2470
}
2471

2472
/************************************************************************/
2473
/*                            GetGCPCount()                             */
2474
/************************************************************************/
2475

2476
int RCMDataset::GetGCPCount()
6✔
2477

2478
{
2479
    return nGCPCount;
6✔
2480
}
2481

2482
/************************************************************************/
2483
/*                          GetGCPSpatialRef()                          */
2484
/************************************************************************/
2485

2486
const OGRSpatialReference *RCMDataset::GetGCPSpatialRef() const
1✔
2487

2488
{
2489
    return m_oGCPSRS.IsEmpty() || nGCPCount == 0 ? nullptr : &m_oGCPSRS;
1✔
2490
}
2491

2492
/************************************************************************/
2493
/*                               GetGCPs()                              */
2494
/************************************************************************/
2495

2496
const GDAL_GCP *RCMDataset::GetGCPs()
5✔
2497

2498
{
2499
    return pasGCPList;
5✔
2500
}
2501

2502
/************************************************************************/
2503
/*                          GetSpatialRef()                             */
2504
/************************************************************************/
2505

2506
const OGRSpatialReference *RCMDataset::GetSpatialRef() const
×
2507

2508
{
2509
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
×
2510
}
2511

2512
/************************************************************************/
2513
/*                          GetGeoTransform()                           */
2514
/************************************************************************/
2515

2516
CPLErr RCMDataset::GetGeoTransform(GDALGeoTransform &gt) const
×
2517

2518
{
2519
    gt = m_gt;
×
2520

2521
    if (bHaveGeoTransform)
×
2522
    {
2523
        return CE_None;
×
2524
    }
2525

2526
    return CE_Failure;
×
2527
}
2528

2529
/************************************************************************/
2530
/*                      GetMetadataDomainList()                         */
2531
/************************************************************************/
2532

2533
char **RCMDataset::GetMetadataDomainList()
×
2534
{
2535
    return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
×
2536
                                   "SUBDATASETS", nullptr);
×
2537
}
2538

2539
/************************************************************************/
2540
/*                            GetMetadata()                             */
2541
/************************************************************************/
2542

2543
char **RCMDataset::GetMetadata(const char *pszDomain)
2✔
2544

2545
{
2546
    if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS") &&
2✔
2547
        papszSubDatasets != nullptr)
×
2548
        return papszSubDatasets;
×
2549

2550
    return GDALDataset::GetMetadata(pszDomain);
2✔
2551
}
2552

2553
/************************************************************************/
2554
/*                         GDALRegister_RCM()                           */
2555
/************************************************************************/
2556

2557
void GDALRegister_RCM()
1,911✔
2558

2559
{
2560
    if (GDALGetDriverByName("RCM") != nullptr)
1,911✔
2561
    {
2562
        return;
282✔
2563
    }
2564

2565
    GDALDriver *poDriver = new GDALDriver();
1,629✔
2566
    RCMDriverSetCommonMetadata(poDriver);
1,629✔
2567

2568
    poDriver->pfnOpen = RCMDataset::Open;
1,629✔
2569

2570
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
2571
}
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