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

OSGeo / gdal / 15797738239

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

Pull #12622

github

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

574181 of 807840 relevant lines covered (71.08%)

249831.39 hits per line

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

63.78
/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,
49✔
41
                                   const char *pszFilename)
42
{
43
    CPLString ptr;
49✔
44

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

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

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

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

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

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

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

77
static double *InterpolateValues(CSLConstList papszList, int tableSize,
13✔
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));
13✔
84
    if (!table)
13✔
85
        return nullptr;
×
86

87
    if (stepSize <= 0)
13✔
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);
13✔
93

94
        int k = 0;
13✔
95

96
        if (positiveStepSize == 1)
13✔
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--)
26✔
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]);
13✔
116
                double valueTo = valueFrom;
13✔
117

118
                if (i > 0)
13✔
119
                {
120
                    // We have room to pick the previous number to interpolate
121
                    // with
122
                    valueTo = CPLAtof(papszList[i - 1]);
×
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;
13✔
128

129
                // Always begin with the value FROM found
130
                table[k++] = valueFrom;
13✔
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++)
107,651✔
136
                {
137
                    valueFrom += interp;
107,638✔
138
                    table[k++] = valueFrom;
107,638✔
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;
13✔
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,
14✔
210
                                              GDALDataset *poBandFile,
211
                                              bool isNITF)
212
{
213

214
    GDALRasterBand *band1 = poBandFile->GetRasterBand(1);
14✔
215
    GDALDataType bandfileType = band1->GetRasterDataType();
14✔
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 ||
14✔
219
         poBandFile->GetRasterCount() == 4) &&
14✔
220
        dataType == bandfileType)
221
        return STRAIGHT;
14✔
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,
8✔
259
                             GDALDataType eDataTypeIn, const char *pszPole,
260
                             GDALDataset *poBandFileIn, bool bTwoBandComplex,
261
                             bool isOneFilePerPolIn, bool isNITFIn)
8✔
262
    : poBandFile(poBandFileIn), poRCMDataset(poDSIn),
263
      twoBandComplex(bTwoBandComplex), isOneFilePerPol(isOneFilePerPolIn),
264
      isNITF(isNITFIn)
8✔
265
{
266
    poDS = poDSIn;
8✔
267
    this->nBand = nBandIn;
8✔
268
    eDataType = eDataTypeIn;
8✔
269

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

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

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

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

293
RCMRasterBand::~RCMRasterBand()
16✔
294

295
{
296
    if (poBandFile != nullptr)
8✔
297
        GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
8✔
298
}
16✔
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()
7✔
966
{
967
    adfGeoTransform[0] = 0.0;
7✔
968
    adfGeoTransform[1] = 1.0;
7✔
969
    adfGeoTransform[2] = 0.0;
7✔
970
    adfGeoTransform[3] = 0.0;
7✔
971
    adfGeoTransform[4] = 0.0;
7✔
972
    adfGeoTransform[5] = 1.0;
7✔
973
}
7✔
974

975
/************************************************************************/
976
/*                            ~RCMDataset()                             */
977
/************************************************************************/
978

979
RCMDataset::~RCMDataset()
14✔
980

981
{
982
    RCMDataset::FlushCache(true);
7✔
983

984
    if (nGCPCount > 0)
7✔
985
    {
986
        GDALDeinitGCPs(nGCPCount, pasGCPList);
7✔
987
        CPLFree(pasGCPList);
7✔
988
    }
989

990
    RCMDataset::CloseDependentDatasets();
7✔
991

992
    if (papszSubDatasets != nullptr)
7✔
993
        CSLDestroy(papszSubDatasets);
3✔
994

995
    if (papszExtraFiles != nullptr)
7✔
996
        CSLDestroy(papszExtraFiles);
7✔
997

998
    if (m_nfIncidenceAngleTable != nullptr)
7✔
999
        CPLFree(m_nfIncidenceAngleTable);
7✔
1000
}
14✔
1001

1002
/************************************************************************/
1003
/*                      CloseDependentDatasets()                        */
1004
/************************************************************************/
1005

1006
int RCMDataset::CloseDependentDatasets()
7✔
1007
{
1008
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
7✔
1009

1010
    if (nBands != 0)
7✔
1011
        bHasDroppedRef = TRUE;
7✔
1012

1013
    for (int iBand = 0; iBand < nBands; iBand++)
21✔
1014
    {
1015
        delete papoBands[iBand];
14✔
1016
    }
1017
    nBands = 0;
7✔
1018

1019
    return bHasDroppedRef;
7✔
1020
}
1021

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

1026
char **RCMDataset::GetFileList()
×
1027

1028
{
1029
    char **papszFileList = GDALPamDataset::GetFileList();
×
1030

1031
    papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
×
1032

1033
    return papszFileList;
×
1034
}
1035

1036
/************************************************************************/
1037
/*                                Open()                                */
1038
/************************************************************************/
1039

1040
GDALDataset *RCMDataset::Open(GDALOpenInfo *poOpenInfo)
9✔
1041

1042
{
1043
    /* -------------------------------------------------------------------- */
1044
    /*      Is this a RCM Product.xml definition?                           */
1045
    /* -------------------------------------------------------------------- */
1046
    if (!RCMDatasetIdentify(poOpenInfo))
9✔
1047
    {
1048
        return nullptr;
×
1049
    }
1050

1051
    /* -------------------------------------------------------------------- */
1052
    /*        Get subdataset information, if relevant                       */
1053
    /* -------------------------------------------------------------------- */
1054
    const char *pszFilename = poOpenInfo->pszFilename;
9✔
1055
    eCalibration eCalib = None;
9✔
1056

1057
    if (STARTS_WITH_CI(pszFilename, szLayerCalibration) &&
9✔
1058
        pszFilename[strlen(szLayerCalibration)] == chLayerSeparator)
6✔
1059
    {
1060
        // The calibration name and filename begins after the hard coded layer
1061
        // name
1062
        pszFilename += strlen(szLayerCalibration) + 1;
6✔
1063

1064
        if (STARTS_WITH_CI(pszFilename, szBETA0))
6✔
1065
        {
1066
            eCalib = Beta0;
1✔
1067
        }
1068
        else if (STARTS_WITH_CI(pszFilename, szSIGMA0))
5✔
1069
        {
1070
            eCalib = Sigma0;
1✔
1071
        }
1072
        else if (STARTS_WITH_CI(pszFilename, szGAMMA) ||
4✔
1073
                 STARTS_WITH_CI(pszFilename, "GAMMA0"))  // Cover both situation
3✔
1074
        {
1075
            eCalib = Gamma;
1✔
1076
        }
1077
        else if (STARTS_WITH_CI(pszFilename, szUNCALIB))
3✔
1078
        {
1079
            eCalib = Uncalib;
2✔
1080
        }
1081
        else
1082
        {
1083
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
1084
                     "Unsupported calibration type");
1085
            return nullptr;
1✔
1086
        }
1087

1088
        /* advance the pointer to the actual filename */
1089
        while (*pszFilename != '\0' && *pszFilename != ':')
35✔
1090
            pszFilename++;
30✔
1091

1092
        if (*pszFilename == ':')
5✔
1093
            pszFilename++;
5✔
1094

1095
        // need to redo the directory check:
1096
        // the GDALOpenInfo check would have failed because of the calibration
1097
        // string on the filename
1098
        VSIStatBufL sStat;
1099
        if (VSIStatL(pszFilename, &sStat) == 0)
5✔
1100
            poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode);
4✔
1101
    }
1102

1103
    CPLString osMDFilename;
16✔
1104
    if (poOpenInfo->bIsDirectory)
8✔
1105
    {
1106
        /* Check for directory access when there is a product.xml file in the
1107
        directory. */
1108
        osMDFilename =
1109
            CPLFormCIFilenameSafe(pszFilename, "product.xml", nullptr);
2✔
1110

1111
        VSIStatBufL sStat;
1112
        if (VSIStatL(osMDFilename, &sStat) != 0)
2✔
1113
        {
1114
            /* If not, check for directory extra 'metadata' access when there is
1115
            a product.xml file in the directory. */
1116
            osMDFilename = CPLFormCIFilenameSafe(pszFilename,
1✔
1117
                                                 GetMetadataProduct(), nullptr);
2✔
1118
        }
1119
    }
1120
    else
1121
        osMDFilename = pszFilename;
6✔
1122

1123
    /* -------------------------------------------------------------------- */
1124
    /*      Ingest the Product.xml file.                                    */
1125
    /* -------------------------------------------------------------------- */
1126
    CPLXMLTreeCloser psProduct(CPLParseXMLFile(osMDFilename));
16✔
1127
    if (!psProduct)
8✔
1128
        return nullptr;
1✔
1129

1130
    /* -------------------------------------------------------------------- */
1131
    /*      Confirm the requested access is supported.                      */
1132
    /* -------------------------------------------------------------------- */
1133
    if (poOpenInfo->eAccess == GA_Update)
7✔
1134
    {
1135
        ReportUpdateNotSupportedByDriver("RCM");
×
1136
        return nullptr;
×
1137
    }
1138

1139
    CPLXMLNode *psSceneAttributes =
1140
        CPLGetXMLNode(psProduct.get(), "=product.sceneAttributes");
7✔
1141
    if (psSceneAttributes == nullptr)
7✔
1142
    {
1143
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1144
                 "ERROR: Failed to find <sceneAttributes> in document.");
1145
        return nullptr;
×
1146
    }
1147

1148
    CPLXMLNode *psImageAttributes = CPLGetXMLNode(
7✔
1149
        psProduct.get(), "=product.sceneAttributes.imageAttributes");
1150
    if (psImageAttributes == nullptr)
7✔
1151
    {
1152
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1153
                 "ERROR: Failed to find <sceneAttributes.imageAttributes> in "
1154
                 "document.");
1155
        return nullptr;
×
1156
    }
1157

1158
    int numberOfEntries =
1159
        atoi(CPLGetXMLValue(psSceneAttributes, "numberOfEntries", "0"));
7✔
1160
    if (numberOfEntries != 1)
7✔
1161
    {
1162
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1163
                 "ERROR: Only RCM with Complex Single-beam is supported.");
1164
        return nullptr;
×
1165
    }
1166

1167
    CPLXMLNode *psImageReferenceAttributes =
1168
        CPLGetXMLNode(psProduct.get(), "=product.imageReferenceAttributes");
7✔
1169
    if (psImageReferenceAttributes == nullptr)
7✔
1170
    {
1171
        CPLError(
×
1172
            CE_Failure, CPLE_OpenFailed,
1173
            "ERROR: Failed to find <imageReferenceAttributes> in document.");
1174
        return nullptr;
×
1175
    }
1176

1177
    CPLXMLNode *psImageGenerationParameters =
1178
        CPLGetXMLNode(psProduct.get(), "=product.imageGenerationParameters");
7✔
1179
    if (psImageGenerationParameters == nullptr)
7✔
1180
    {
1181
        CPLError(
×
1182
            CE_Failure, CPLE_OpenFailed,
1183
            "ERROR: Failed to find <imageGenerationParameters> in document.");
1184
        return nullptr;
×
1185
    }
1186

1187
    /* -------------------------------------------------------------------- */
1188
    /*      Create the dataset.                                             */
1189
    /* -------------------------------------------------------------------- */
1190
    auto poDS = std::make_unique<RCMDataset>();
14✔
1191

1192
    poDS->psProduct = std::move(psProduct);
7✔
1193

1194
    /* -------------------------------------------------------------------- */
1195
    /*      Get overall image information.                                  */
1196
    /* -------------------------------------------------------------------- */
1197
    poDS->nRasterXSize = atoi(CPLGetXMLValue(
7✔
1198
        psSceneAttributes, "imageAttributes.samplesPerLine", "-1"));
1199
    poDS->nRasterYSize = atoi(
7✔
1200
        CPLGetXMLValue(psSceneAttributes, "imageAttributes.numLines", "-1"));
1201
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
7✔
1202
    {
1203
        return nullptr;
×
1204
    }
1205

1206
    /* -------------------------------------------------------------------- */
1207
    /*      Check product type, as to determine if there are LUTs for       */
1208
    /*      calibration purposes.                                           */
1209
    /* -------------------------------------------------------------------- */
1210

1211
    const char *pszItem =
1212
        CPLGetXMLValue(psImageGenerationParameters,
7✔
1213
                       "generalProcessingInformation.productType", "UNK");
1214
    poDS->SetMetadataItem("PRODUCT_TYPE", pszItem);
7✔
1215
    const char *pszProductType = pszItem;
7✔
1216

1217
    pszItem =
1218
        CPLGetXMLValue(poDS->psProduct.get(), "=product.productId", "UNK");
7✔
1219
    poDS->SetMetadataItem("PRODUCT_ID", pszItem);
7✔
1220

1221
    pszItem = CPLGetXMLValue(
7✔
1222
        poDS->psProduct.get(),
7✔
1223
        "=product.securityAttributes.securityClassification", "UNK");
1224
    poDS->SetMetadataItem("SECURITY_CLASSIFICATION", pszItem);
7✔
1225

1226
    pszItem =
1227
        CPLGetXMLValue(poDS->psProduct.get(),
7✔
1228
                       "=product.sourceAttributes.polarizationDataMode", "UNK");
1229
    poDS->SetMetadataItem("POLARIZATION_DATA_MODE", pszItem);
7✔
1230

1231
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
7✔
1232
                             "generalProcessingInformation.processingFacility",
1233
                             "UNK");
1234
    poDS->SetMetadataItem("PROCESSING_FACILITY", pszItem);
7✔
1235

1236
    pszItem =
1237
        CPLGetXMLValue(psImageGenerationParameters,
7✔
1238
                       "generalProcessingInformation.processingTime", "UNK");
1239
    poDS->SetMetadataItem("PROCESSING_TIME", pszItem);
7✔
1240

1241
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
7✔
1242
                             "sarProcessingInformation.satelliteHeight", "UNK");
1243
    poDS->SetMetadataItem("SATELLITE_HEIGHT", pszItem);
7✔
1244

1245
    pszItem = CPLGetXMLValue(
7✔
1246
        psImageGenerationParameters,
1247
        "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK");
1248
    poDS->SetMetadataItem("FIRST_LINE_TIME", pszItem);
7✔
1249

1250
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
7✔
1251
                             "sarProcessingInformation.zeroDopplerTimeLastLine",
1252
                             "UNK");
1253
    poDS->SetMetadataItem("LAST_LINE_TIME", pszItem);
7✔
1254

1255
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
7✔
1256
                             "sarProcessingInformation.lutApplied", "");
1257
    poDS->SetMetadataItem("LUT_APPLIED", pszItem);
7✔
1258

1259
    /*---------------------------------------------------------------------
1260
           If true, a polarization dependent application LUT has been applied
1261
           for each polarization channel. Otherwise the same application LUT
1262
           has been applied for all polarization channels. Not applicable to
1263
           lookupTable = "Unity*" or if dataType = "Floating-Point".
1264
      --------------------------------------------------------------------- */
1265
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
7✔
1266
                             "sarProcessingInformation.perPolarizationScaling",
1267
                             "false");
1268
    poDS->SetMetadataItem("PER_POLARIZATION_SCALING", pszItem);
7✔
1269
    if (EQUAL(pszItem, "true") || EQUAL(pszItem, "TRUE"))
7✔
1270
    {
1271
        poDS->bPerPolarizationScaling = TRUE;
7✔
1272
    }
1273

1274
    /* the following cases can be assumed to have no LUTs, as per
1275
     * RN-RP-51-2713, but also common sense
1276
     * SLC represents a SLant range georeferenced Complex product
1277
     * (i.e., equivalent to a Single-Look Complex product for RADARSAT-1 or
1278
     * RADARSAT-2). • GRD or GRC represent GRound range georeferenced Detected
1279
     * or Complex products (GRD is equivalent to an SGX, SCN or SCW product for
1280
     * RADARSAT1 or RADARSAT-2). • GCD or GCC represent GeoCoded Detected or
1281
     * Complex products (GCD is equivalent to an SSG or SPG product for
1282
     * RADARSAT-1 or RADARSAT-2).
1283
     */
1284
    bool bCanCalib = false;
7✔
1285
    if (!(STARTS_WITH_CI(pszProductType, "UNK") ||
7✔
1286
          STARTS_WITH_CI(pszProductType, "GCD") ||
7✔
1287
          STARTS_WITH_CI(pszProductType, "GCC")))
7✔
1288
    {
1289
        bCanCalib = true;
7✔
1290
    }
1291

1292
    /* -------------------------------------------------------------------- */
1293
    /*      Get dataType (so we can recognise complex data), and the        */
1294
    /*      bitsPerSample.                                                  */
1295
    /* -------------------------------------------------------------------- */
1296
    const char *pszSampleDataType = CPLGetXMLValue(
7✔
1297
        psImageReferenceAttributes, "rasterAttributes.sampleType", "");
1298
    poDS->SetMetadataItem("SAMPLE_TYPE", pszSampleDataType);
7✔
1299

1300
    /* Either Integer (16 bits) or Floating-Point (32 bits) */
1301
    const char *pszDataType = CPLGetXMLValue(psImageReferenceAttributes,
7✔
1302
                                             "rasterAttributes.dataType", "");
1303
    poDS->SetMetadataItem("DATA_TYPE", pszDataType);
7✔
1304

1305
    /* 2 entries for complex data 1 entry for magnitude detected data */
1306
    const char *pszBitsPerSample = CPLGetXMLValue(
7✔
1307
        psImageReferenceAttributes, "rasterAttributes.bitsPerSample", "");
1308
    const int nBitsPerSample = atoi(pszBitsPerSample);
7✔
1309
    poDS->SetMetadataItem("BITS_PER_SAMPLE", pszBitsPerSample);
7✔
1310

1311
    const char *pszSampledPixelSpacingTime =
1312
        CPLGetXMLValue(psImageReferenceAttributes,
7✔
1313
                       "rasterAttributes.sampledPixelSpacingTime", "UNK");
1314
    poDS->SetMetadataItem("SAMPLED_PIXEL_SPACING_TIME",
7✔
1315
                          pszSampledPixelSpacingTime);
7✔
1316

1317
    const char *pszSampledLineSpacingTime =
1318
        CPLGetXMLValue(psImageReferenceAttributes,
7✔
1319
                       "rasterAttributes.sampledLineSpacingTime", "UNK");
1320
    poDS->SetMetadataItem("SAMPLED_LINE_SPACING_TIME",
7✔
1321
                          pszSampledLineSpacingTime);
7✔
1322

1323
    GDALDataType eDataType;
1324

1325
    if (EQUAL(pszSampleDataType, "Mixed")) /* RCM MLC has Mixed sampleType */
7✔
1326
    {
1327
        poDS->isComplexData = false; /* RCM MLC is detected, non-complex */
×
1328
        if (nBitsPerSample == 32)
×
1329
        {
1330
            eDataType = GDT_Float32; /* 32 bits, check read block */
×
1331
            poDS->magnitudeBits = 32;
×
1332
        }
1333
        else
1334
        {
1335
            eDataType = GDT_UInt16; /* 16 bits, check read block */
×
1336
            poDS->magnitudeBits = 16;
×
1337
        }
1338
    }
1339
    else if (EQUAL(pszSampleDataType, "Complex"))
7✔
1340
    {
1341
        poDS->isComplexData = true;
×
1342
        /* Usually this is the same bits for both */
1343
        poDS->realBitsComplexData = nBitsPerSample;
×
1344
        poDS->imaginaryBitsComplexData = nBitsPerSample;
×
1345

1346
        if (nBitsPerSample == 32)
×
1347
        {
1348
            eDataType = GDT_CFloat32; /* 32 bits, check read block */
×
1349
        }
1350
        else
1351
        {
1352
            eDataType = GDT_CInt16; /* 16 bits, check read block */
×
1353
        }
1354
    }
1355
    else if (nBitsPerSample == 32 &&
7✔
1356
             EQUAL(pszSampleDataType, "Magnitude Detected"))
×
1357
    {
1358
        /* Actually, I don't need to test the 'dataType'=' Floating-Point', we
1359
         * know that a 32 bits per sample */
1360
        eDataType = GDT_Float32;
×
1361
        poDS->isComplexData = false;
×
1362
        poDS->magnitudeBits = 32;
×
1363
    }
1364
    else if (nBitsPerSample == 16 &&
7✔
1365
             EQUAL(pszSampleDataType, "Magnitude Detected"))
7✔
1366
    {
1367
        /* Actually, I don't need to test the 'dataType'=' Integer', we know
1368
         * that a 16 bits per sample */
1369
        eDataType = GDT_UInt16;
7✔
1370
        poDS->isComplexData = false;
7✔
1371
        poDS->magnitudeBits = 16;
7✔
1372
    }
1373
    else
1374
    {
1375
        CPLError(CE_Failure, CPLE_AppDefined,
×
1376
                 "ERROR: dataType=%s and bitsPerSample=%d are not a supported "
1377
                 "configuration.",
1378
                 pszDataType, nBitsPerSample);
1379
        return nullptr;
×
1380
    }
1381

1382
    /* Indicates whether pixel number (i.e., range) increases or decreases with
1383
    range time. For GCD and GCC products, this applies to intermediate ground
1384
    range image data prior to geocoding. */
1385
    const char *pszPixelTimeOrdering =
1386
        CPLGetXMLValue(psImageReferenceAttributes,
7✔
1387
                       "rasterAttributes.pixelTimeOrdering", "UNK");
1388
    poDS->SetMetadataItem("PIXEL_TIME_ORDERING", pszPixelTimeOrdering);
7✔
1389

1390
    /* Indicates whether line numbers (i.e., azimuth) increase or decrease with
1391
    azimuth time. For GCD and GCC products, this applies to intermediate ground
1392
    range image data prior to geocoding. */
1393
    const char *pszLineTimeOrdering = CPLGetXMLValue(
7✔
1394
        psImageReferenceAttributes, "rasterAttributes.lineTimeOrdering", "UNK");
1395
    poDS->SetMetadataItem("LINE_TIME_ORDERING", pszLineTimeOrdering);
7✔
1396

1397
    /* while we're at it, extract the pixel spacing information */
1398
    const char *pszPixelSpacing =
1399
        CPLGetXMLValue(psImageReferenceAttributes,
7✔
1400
                       "rasterAttributes.sampledPixelSpacing", "UNK");
1401
    poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing);
7✔
1402

1403
    const char *pszLineSpacing =
1404
        CPLGetXMLValue(psImageReferenceAttributes,
7✔
1405
                       "rasterAttributes.sampledLineSpacing", "UNK");
1406
    poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing);
7✔
1407

1408
    /* -------------------------------------------------------------------- */
1409
    /*      Open each of the data files as a complex band.                  */
1410
    /* -------------------------------------------------------------------- */
1411
    char *pszBeta0LUT = nullptr;
7✔
1412
    char *pszGammaLUT = nullptr;
7✔
1413
    char *pszSigma0LUT = nullptr;
7✔
1414
    // Same file for all calibrations except the calibration is plit inside the
1415
    // XML
1416
    std::string osNoiseLevelsValues;
14✔
1417

1418
    const CPLString osPath = CPLGetPathSafe(osMDFilename);
14✔
1419

1420
    /* Get a list of all polarizations */
1421
    CPLXMLNode *psSourceAttrs =
1422
        CPLGetXMLNode(poDS->psProduct.get(), "=product.sourceAttributes");
7✔
1423
    if (psSourceAttrs == nullptr)
7✔
1424
    {
1425
        CPLError(
×
1426
            CE_Failure, CPLE_OpenFailed,
1427
            "ERROR: RCM source attributes is missing. Please contact your data "
1428
            "provider for a corrected dataset.");
1429
        return nullptr;
×
1430
    }
1431

1432
    CPLXMLNode *psRadarParameters = CPLGetXMLNode(
7✔
1433
        poDS->psProduct.get(), "=product.sourceAttributes.radarParameters");
7✔
1434
    if (psRadarParameters == nullptr)
7✔
1435
    {
1436
        CPLError(
×
1437
            CE_Failure, CPLE_OpenFailed,
1438
            "ERROR: RCM radar parameters is missing. Please contact your data "
1439
            "provider for a corrected dataset.");
1440
        return nullptr;
×
1441
    }
1442

1443
    const char *pszPolarizations =
1444
        CPLGetXMLValue(psRadarParameters, "polarizations", "");
7✔
1445
    if (pszPolarizations == nullptr || strlen(pszPolarizations) == 0)
7✔
1446
    {
1447
        CPLError(
×
1448
            CE_Failure, CPLE_OpenFailed,
1449
            "ERROR: RCM polarizations list is missing. Please contact your "
1450
            "data provider for a corrected dataset.");
1451
        return nullptr;
×
1452
    }
1453
    poDS->SetMetadataItem("POLARIZATIONS", pszPolarizations);
7✔
1454

1455
    const char *psAcquisitionType =
1456
        CPLGetXMLValue(psRadarParameters, "acquisitionType", "UNK");
7✔
1457
    poDS->SetMetadataItem("ACQUISITION_TYPE", psAcquisitionType);
7✔
1458

1459
    const char *psBeams = CPLGetXMLValue(psRadarParameters, "beams", "UNK");
7✔
1460
    poDS->SetMetadataItem("BEAMS", psBeams);
7✔
1461

1462
    const CPLStringList aosPolarizationsGrids(
1463
        CSLTokenizeString2(pszPolarizations, " ", 0));
14✔
1464
    CPLStringList imageBandList;
14✔
1465
    CPLStringList imageBandFileList;
14✔
1466
    const int nPolarizationsGridCount = aosPolarizationsGrids.size();
7✔
1467

1468
    /* File names for full resolution IPDFs. For GeoTIFF format, one entry per
1469
    pole; For NITF 2.1 format, only one entry. */
1470
    bool bIsNITF = false;
7✔
1471
    const char *pszNITF_Filename = nullptr;
7✔
1472
    int imageBandFileCount = 0;
7✔
1473
    int imageBandCount = 0;
7✔
1474

1475
    /* Split the polarization string*/
1476
    auto iss = std::istringstream((CPLString(pszPolarizations)).c_str());
21✔
1477
    auto pol = std::string{};
14✔
1478
    /* Count number of polarizations*/
1479
    while (iss >> pol)
21✔
1480
        imageBandCount++;
14✔
1481

1482
    CPLXMLNode *psNode = psImageAttributes->psChild;
7✔
1483
    for (; psNode != nullptr; psNode = psNode->psNext)
112✔
1484
    {
1485
        /* Find the tif or ntf filename */
1486
        if (psNode->eType != CXT_Element || !(EQUAL(psNode->pszValue, "ipdf")))
105✔
1487
            continue;
91✔
1488

1489
        /* --------------------------------------------------------------------
1490
         */
1491
        /*      Fetch ipdf image. Could be either tif or ntf. */
1492
        /*      Replace / by \\ */
1493
        /* --------------------------------------------------------------------
1494
         */
1495
        const char *pszBasedFilename = CPLGetXMLValue(psNode, "", "");
14✔
1496
        if (*pszBasedFilename == '\0')
14✔
1497
            continue;
×
1498

1499
        /* Count number of image names within ipdf tag*/
1500
        imageBandFileCount++;
14✔
1501

1502
        CPLString pszUpperBasedFilename(CPLString(pszBasedFilename).toupper());
14✔
1503

1504
        const bool bEndsWithNTF =
1505
            strlen(pszUpperBasedFilename.c_str()) > 4 &&
28✔
1506
            EQUAL(pszUpperBasedFilename.c_str() +
14✔
1507
                      strlen(pszUpperBasedFilename.c_str()) - 4,
1508
                  ".NTF");
1509

1510
        if (bEndsWithNTF)
14✔
1511
        {
1512
            /* Found it! It would not exist one more */
1513
            bIsNITF = true;
×
1514
            pszNITF_Filename = pszBasedFilename;
×
1515
            break;
×
1516
        }
1517
        else
1518
        {
1519
            /* Keep adding polarizations filename according to the pole */
1520
            const char *pszPole = CPLGetXMLValue(psNode, "pole", "");
14✔
1521
            if (*pszPole == '\0')
14✔
1522
            {
1523
                /* Guard against case when pole is a null string, skip it */
1524
                imageBandFileCount--;
×
1525
                continue;
×
1526
            }
1527

1528
            if (EQUAL(pszPole, "XC"))
14✔
1529
            {
1530
                /* Skip RCM MLC's 3rd band file ##XC.tif */
1531
                imageBandFileCount--;
×
1532
                continue;
×
1533
            }
1534

1535
            imageBandList.AddString(CPLString(pszPole).toupper());
14✔
1536
            imageBandFileList.AddString(pszBasedFilename);
14✔
1537
        }
1538
    }
1539

1540
    /* -------------------------------------------------------------------- */
1541
    /*      Incidence Angle in a sub-folder                                 */
1542
    /*      called 'calibration' from the 'metadata' folder                 */
1543
    /* -------------------------------------------------------------------- */
1544

1545
    const char *pszIncidenceAngleFileName = CPLGetXMLValue(
7✔
1546
        psImageReferenceAttributes, "incidenceAngleFileName", "");
1547

1548
    if (pszIncidenceAngleFileName != nullptr)
7✔
1549
    {
1550
        CPLString osIncidenceAngleFilePath = CPLFormFilenameSafe(
14✔
1551
            CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr).c_str(),
7✔
1552
            pszIncidenceAngleFileName, nullptr);
14✔
1553

1554
        /* Check if the file exist */
1555
        if (IsValidXMLFile(osIncidenceAngleFilePath))
7✔
1556
        {
1557
            CPLXMLTreeCloser psIncidenceAngle(
1558
                CPLParseXMLFile(osIncidenceAngleFilePath));
14✔
1559

1560
            int pixelFirstLutValue = atoi(
7✔
1561
                CPLGetXMLValue(psIncidenceAngle.get(),
7✔
1562
                               "=incidenceAngles.pixelFirstAnglesValue", "0"));
1563

1564
            const int stepSize = atoi(CPLGetXMLValue(
7✔
1565
                psIncidenceAngle.get(), "=incidenceAngles.stepSize", "0"));
7✔
1566
            const int numberOfValues =
1567
                atoi(CPLGetXMLValue(psIncidenceAngle.get(),
7✔
1568
                                    "=incidenceAngles.numberOfValues", "0"));
1569

1570
            if (!(stepSize == 0 || stepSize == INT_MIN ||
7✔
1571
                  numberOfValues == INT_MIN ||
1572
                  abs(numberOfValues) > INT_MAX / abs(stepSize)))
7✔
1573
            {
1574
                /* Get the Pixel Per range */
1575
                const int tableSize = abs(stepSize) * abs(numberOfValues);
7✔
1576

1577
                CPLString angles;
14✔
1578
                // Loop through all nodes with spaces
1579
                CPLXMLNode *psNextNode =
1580
                    CPLGetXMLNode(psIncidenceAngle.get(), "=incidenceAngles");
7✔
1581

1582
                CPLXMLNode *psNodeInc;
1583
                for (psNodeInc = psNextNode->psChild; psNodeInc != nullptr;
56✔
1584
                     psNodeInc = psNodeInc->psNext)
49✔
1585
                {
1586
                    if (EQUAL(psNodeInc->pszValue, "angles"))
49✔
1587
                    {
1588
                        if (angles.length() > 0)
7✔
1589
                        {
1590
                            angles.append(" "); /* separator */
×
1591
                        }
1592
                        const char *valAngle =
1593
                            CPLGetXMLValue(psNodeInc, "", "");
7✔
1594
                        angles.append(valAngle);
7✔
1595
                    }
1596
                }
1597

1598
                char **papszAngleList =
1599
                    CSLTokenizeString2(angles, " ", CSLT_HONOURSTRINGS);
7✔
1600

1601
                /* Allocate the right LUT size according to the product range pixel
1602
                 */
1603
                poDS->m_IncidenceAngleTableSize = tableSize;
7✔
1604
                poDS->m_nfIncidenceAngleTable =
14✔
1605
                    InterpolateValues(papszAngleList, tableSize, stepSize,
7✔
1606
                                      numberOfValues, pixelFirstLutValue);
1607

1608
                CSLDestroy(papszAngleList);
7✔
1609
            }
1610
        }
1611
    }
1612

1613
    for (int iPoleInx = 0; iPoleInx < nPolarizationsGridCount; iPoleInx++)
21✔
1614
    {
1615
        // Search for a specific band name
1616
        const CPLString pszPole =
1617
            CPLString(aosPolarizationsGrids[iPoleInx]).toupper();
14✔
1618

1619
        // Look if the NoiseLevel file xml exist for the
1620
        CPLXMLNode *psRefNode = psImageReferenceAttributes->psChild;
14✔
1621
        for (; psRefNode != nullptr; psRefNode = psRefNode->psNext)
196✔
1622
        {
1623
            if (EQUAL(psRefNode->pszValue, "noiseLevelFileName") && bCanCalib)
182✔
1624
            {
1625
                /* Determine which incidence angle correction this is */
1626
                const char *pszPoleToMatch =
1627
                    CPLGetXMLValue(psRefNode, "pole", "");
28✔
1628
                const char *pszNoiseLevelFile =
1629
                    CPLGetXMLValue(psRefNode, "", "");
28✔
1630

1631
                if (*pszPoleToMatch == '\0')
28✔
1632
                    continue;
14✔
1633

1634
                if (EQUAL(pszPoleToMatch, "XC"))
28✔
1635
                    /* Skip noise for RCM MLC's 3rd band XC */
1636
                    continue;
×
1637

1638
                if (EQUAL(pszNoiseLevelFile, ""))
28✔
1639
                    continue;
×
1640

1641
                /* --------------------------------------------------------------------
1642
                 */
1643
                /*      With RCM, LUT file is different per polarizarion (pole)
1644
                 */
1645
                /*      The following code make sure to loop through all
1646
                 * possible       */
1647
                /*      'noiseLevelFileName' and match the <ipdf 'pole' name */
1648
                /* --------------------------------------------------------------------
1649
                 */
1650
                if (pszPole.compare(pszPoleToMatch) != 0)
28✔
1651
                {
1652
                    continue;
14✔
1653
                }
1654

1655
                /* --------------------------------------------------------------------
1656
                 */
1657
                /*      LUT calibration is unique per pole in a sub-folder */
1658
                /*      called 'calibration' from the 'metadata' folder */
1659
                /* --------------------------------------------------------------------
1660
                 */
1661

1662
                CPLString oNoiseLevelPath = CPLFormFilenameSafe(
28✔
1663
                    CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr)
14✔
1664
                        .c_str(),
1665
                    pszNoiseLevelFile, nullptr);
28✔
1666
                if (IsValidXMLFile(oNoiseLevelPath))
14✔
1667
                {
1668
                    osNoiseLevelsValues = std::move(oNoiseLevelPath);
14✔
1669
                }
1670
            }
1671
        }
1672

1673
        // Search again with different file
1674
        psRefNode = psImageReferenceAttributes->psChild;
14✔
1675
        for (; psRefNode != nullptr; psRefNode = psRefNode->psNext)
196✔
1676
        {
1677
            if (EQUAL(psRefNode->pszValue, "lookupTableFileName") && bCanCalib)
182✔
1678
            {
1679
                /* Determine which incidence angle correction this is */
1680
                const char *pszLUTType =
1681
                    CPLGetXMLValue(psRefNode, "sarCalibrationType", "");
84✔
1682
                const char *pszPoleToMatch =
1683
                    CPLGetXMLValue(psRefNode, "pole", "");
84✔
1684
                const char *pszLUTFile = CPLGetXMLValue(psRefNode, "", "");
84✔
1685

1686
                if (*pszPoleToMatch == '\0')
84✔
1687
                    continue;
42✔
1688

1689
                if (EQUAL(pszPoleToMatch, "XC"))
84✔
1690
                    /* Skip Calib for RCM MLC's 3rd band XC */
1691
                    continue;
×
1692

1693
                if (*pszLUTType == '\0')
84✔
1694
                    continue;
×
1695

1696
                if (EQUAL(pszLUTType, ""))
84✔
1697
                    continue;
×
1698

1699
                /* --------------------------------------------------------------------
1700
                 */
1701
                /*      With RCM, LUT file is different per polarizarion (pole)
1702
                 */
1703
                /*      The following code make sure to loop through all
1704
                 * possible       */
1705
                /*      'lookupTableFileName' and match the <ipdf 'pole' name */
1706
                /* --------------------------------------------------------------------
1707
                 */
1708
                if (pszPole.compare(pszPoleToMatch) != 0)
84✔
1709
                {
1710
                    continue;
42✔
1711
                }
1712

1713
                /* --------------------------------------------------------------------
1714
                 */
1715
                /*      LUT calibration is unique per pole in a sub-folder */
1716
                /*      called 'calibration' from the 'metadata' folder */
1717
                /* --------------------------------------------------------------------
1718
                 */
1719
                const CPLString osLUTFilePath = CPLFormFilenameSafe(
84✔
1720
                    CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr)
42✔
1721
                        .c_str(),
1722
                    pszLUTFile, nullptr);
84✔
1723

1724
                if (EQUAL(pszLUTType, "Beta Nought") &&
56✔
1725
                    IsValidXMLFile(osLUTFilePath))
14✔
1726
                {
1727
                    poDS->papszExtraFiles =
28✔
1728
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
14✔
1729

1730
                    CPLString pszBuf(
1731
                        FormatCalibration(szBETA0, osMDFilename.c_str()));
14✔
1732
                    CPLFree(pszBeta0LUT);
14✔
1733
                    pszBeta0LUT = VSIStrdup(osLUTFilePath);
14✔
1734

1735
                    const char *oldLut =
1736
                        poDS->GetMetadataItem("BETA_NOUGHT_LUT");
14✔
1737
                    if (oldLut == nullptr)
14✔
1738
                    {
1739
                        poDS->SetMetadataItem("BETA_NOUGHT_LUT", osLUTFilePath);
7✔
1740
                    }
1741
                    else
1742
                    {
1743
                        /* Keep adding LUT file for all bands, should be planty
1744
                         * of space */
1745
                        char *ptrConcatLut =
1746
                            static_cast<char *>(CPLMalloc(2048));
7✔
1747
                        ptrConcatLut[0] =
7✔
1748
                            '\0'; /* Just initialize the first byte */
1749
                        strcat(ptrConcatLut, oldLut);
7✔
1750
                        strcat(ptrConcatLut, ",");
7✔
1751
                        strcat(ptrConcatLut, osLUTFilePath);
7✔
1752
                        poDS->SetMetadataItem("BETA_NOUGHT_LUT", ptrConcatLut);
7✔
1753
                        CPLFree(ptrConcatLut);
7✔
1754
                    }
1755

1756
                    poDS->papszSubDatasets = CSLSetNameValue(
28✔
1757
                        poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf);
14✔
1758
                    poDS->papszSubDatasets = CSLSetNameValue(
14✔
1759
                        poDS->papszSubDatasets, "SUBDATASET_3_DESC",
14✔
1760
                        "Beta Nought calibrated");
1761
                }
1762
                else if (EQUAL(pszLUTType, "Sigma Nought") &&
42✔
1763
                         IsValidXMLFile(osLUTFilePath))
14✔
1764
                {
1765
                    poDS->papszExtraFiles =
28✔
1766
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
14✔
1767

1768
                    CPLString pszBuf(
1769
                        FormatCalibration(szSIGMA0, osMDFilename.c_str()));
14✔
1770
                    CPLFree(pszSigma0LUT);
14✔
1771
                    pszSigma0LUT = VSIStrdup(osLUTFilePath);
14✔
1772

1773
                    const char *oldLut =
1774
                        poDS->GetMetadataItem("SIGMA_NOUGHT_LUT");
14✔
1775
                    if (oldLut == nullptr)
14✔
1776
                    {
1777
                        poDS->SetMetadataItem("SIGMA_NOUGHT_LUT",
14✔
1778
                                              osLUTFilePath);
7✔
1779
                    }
1780
                    else
1781
                    {
1782
                        /* Keep adding LUT file for all bands, should be planty
1783
                         * of space */
1784
                        char *ptrConcatLut =
1785
                            static_cast<char *>(CPLMalloc(2048));
7✔
1786
                        ptrConcatLut[0] =
7✔
1787
                            '\0'; /* Just initialize the first byte */
1788
                        strcat(ptrConcatLut, oldLut);
7✔
1789
                        strcat(ptrConcatLut, ",");
7✔
1790
                        strcat(ptrConcatLut, osLUTFilePath);
7✔
1791
                        poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", ptrConcatLut);
7✔
1792
                        CPLFree(ptrConcatLut);
7✔
1793
                    }
1794

1795
                    poDS->papszSubDatasets = CSLSetNameValue(
28✔
1796
                        poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf);
14✔
1797
                    poDS->papszSubDatasets = CSLSetNameValue(
14✔
1798
                        poDS->papszSubDatasets, "SUBDATASET_2_DESC",
14✔
1799
                        "Sigma Nought calibrated");
1800
                }
1801
                else if (EQUAL(pszLUTType, "Gamma") &&
28✔
1802
                         IsValidXMLFile(osLUTFilePath))
14✔
1803
                {
1804
                    poDS->papszExtraFiles =
28✔
1805
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
14✔
1806

1807
                    CPLString pszBuf(
1808
                        FormatCalibration(szGAMMA, osMDFilename.c_str()));
14✔
1809
                    CPLFree(pszGammaLUT);
14✔
1810
                    pszGammaLUT = VSIStrdup(osLUTFilePath);
14✔
1811

1812
                    const char *oldLut = poDS->GetMetadataItem("GAMMA_LUT");
14✔
1813
                    if (oldLut == nullptr)
14✔
1814
                    {
1815
                        poDS->SetMetadataItem("GAMMA_LUT", osLUTFilePath);
7✔
1816
                    }
1817
                    else
1818
                    {
1819
                        /* Keep adding LUT file for all bands, should be planty
1820
                         * of space */
1821
                        char *ptrConcatLut =
1822
                            static_cast<char *>(CPLMalloc(2048));
7✔
1823
                        ptrConcatLut[0] =
7✔
1824
                            '\0'; /* Just initialize the first byte */
1825
                        strcat(ptrConcatLut, oldLut);
7✔
1826
                        strcat(ptrConcatLut, ",");
7✔
1827
                        strcat(ptrConcatLut, osLUTFilePath);
7✔
1828
                        poDS->SetMetadataItem("GAMMA_LUT", ptrConcatLut);
7✔
1829
                        CPLFree(ptrConcatLut);
7✔
1830
                    }
1831

1832
                    poDS->papszSubDatasets = CSLSetNameValue(
28✔
1833
                        poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf);
14✔
1834
                    poDS->papszSubDatasets = CSLSetNameValue(
14✔
1835
                        poDS->papszSubDatasets, "SUBDATASET_4_DESC",
14✔
1836
                        "Gamma calibrated");
1837
                }
1838
            }
1839
        }
1840

1841
        /* --------------------------------------------------------------------
1842
         */
1843
        /*      Fetch ipdf image. Could be either tif or ntf. */
1844
        /*      Replace / by \\ */
1845
        /* --------------------------------------------------------------------
1846
         */
1847
        const char *pszBasedFilename;
1848
        if (bIsNITF)
14✔
1849
        {
1850
            pszBasedFilename = pszNITF_Filename;
×
1851
        }
1852
        else
1853
        {
1854
            const int bandPositionIndex = imageBandList.FindString(pszPole);
14✔
1855
            if (bandPositionIndex < 0)
14✔
1856
            {
1857
                CPLFree(imageBandList);
×
1858
                CPLFree(imageBandFileList);
×
1859

1860
                CPLError(CE_Failure, CPLE_OpenFailed,
×
1861
                         "ERROR: RCM cannot find the polarization %s. Please "
1862
                         "contact your data provider for a corrected dataset",
1863
                         pszPole.c_str());
1864
                return nullptr;
×
1865
            }
1866

1867
            pszBasedFilename = imageBandFileList[bandPositionIndex];
14✔
1868
        }
1869

1870
        /* --------------------------------------------------------------------
1871
         */
1872
        /*      Form full filename (path of product.xml + basename). */
1873
        /* --------------------------------------------------------------------
1874
         */
1875
        char *pszFullname = CPLStrdup(
14✔
1876
            CPLFormFilenameSafe(osPath, pszBasedFilename, nullptr).c_str());
28✔
1877

1878
        /* --------------------------------------------------------------------
1879
         */
1880
        /*      Try and open the file. */
1881
        /* --------------------------------------------------------------------
1882
         */
1883
        auto poBandFile = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1884
            pszFullname, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
14✔
1885
        if (poBandFile == nullptr)
14✔
1886
        {
1887
            CPLFree(pszFullname);
×
1888
            continue;
×
1889
        }
1890
        if (poBandFile->GetRasterCount() == 0)
14✔
1891
        {
1892
            CPLFree(pszFullname);
×
1893
            continue;
×
1894
        }
1895

1896
        poDS->papszExtraFiles =
28✔
1897
            CSLAddString(poDS->papszExtraFiles, pszFullname);
14✔
1898

1899
        /* Some CFloat32 NITF files have nBitsPerSample incorrectly reported */
1900
        /* as 16, and get misinterpreted as CInt16.  Check the underlying NITF
1901
         */
1902
        /* and override if this is the case. */
1903
        if (poBandFile->GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
14✔
1904
            eDataType = GDT_CFloat32;
×
1905

1906
        BandMappingRCM b =
1907
            checkBandFileMappingRCM(eDataType, poBandFile.get(), bIsNITF);
14✔
1908
        if (b == BANDERROR)
14✔
1909
        {
1910
            CPLFree(pszFullname);
×
1911
            CPLError(CE_Failure, CPLE_AppDefined,
×
1912
                     "The underlying band files do not have an appropriate "
1913
                     "data type.");
1914
            return nullptr;
×
1915
        }
1916
        bool twoBandComplex = b == TWOBANDCOMPLEX;
14✔
1917
        bool isOneFilePerPol = (imageBandCount == imageBandFileCount);
14✔
1918

1919
        /* --------------------------------------------------------------------
1920
         */
1921
        /*      Create the band. */
1922
        /* --------------------------------------------------------------------
1923
         */
1924
        int bandNum = poDS->GetRasterCount() + 1;
14✔
1925
        if (eCalib == None || eCalib == Uncalib)
14✔
1926
        {
1927
            RCMRasterBand *poBand = new RCMRasterBand(
1928
                poDS.get(), bandNum, eDataType, pszPole, poBandFile.release(),
16✔
1929
                twoBandComplex, isOneFilePerPol, bIsNITF);
16✔
1930

1931
            poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
8✔
1932
        }
1933
        else
1934
        {
1935
            const char *pszLUT = nullptr;
6✔
1936
            switch (eCalib)
6✔
1937
            {
1938
                case Sigma0:
2✔
1939
                    pszLUT = pszSigma0LUT;
2✔
1940
                    break;
2✔
1941
                case Beta0:
2✔
1942
                    pszLUT = pszBeta0LUT;
2✔
1943
                    break;
2✔
1944
                case Gamma:
2✔
1945
                    pszLUT = pszGammaLUT;
2✔
1946
                    break;
2✔
1947
                default:
×
1948
                    /* we should bomb gracefully... */
1949
                    pszLUT = pszSigma0LUT;
×
1950
            }
1951
            if (!pszLUT)
6✔
1952
            {
1953
                CPLFree(pszFullname);
×
1954
                CPLError(CE_Failure, CPLE_AppDefined, "LUT missing.");
×
1955
                return nullptr;
×
1956
            }
1957

1958
            // The variable 'osNoiseLevelsValues' is always the same for a ban
1959
            // name except the XML contains different calibration name
1960
            if (poDS->isComplexData)
6✔
1961
            {
1962
                // If Complex, always 32 bits
1963
                RCMCalibRasterBand *poBand = new RCMCalibRasterBand(
1964
                    poDS.get(), pszPole, GDT_Float32, poBandFile.release(),
×
1965
                    eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType);
×
1966
                poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
×
1967
            }
1968
            else
1969
            {
1970
                // Whatever the datatype was previoulsy set
1971
                RCMCalibRasterBand *poBand = new RCMCalibRasterBand(
1972
                    poDS.get(), pszPole, eDataType, poBandFile.release(),
12✔
1973
                    eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType);
12✔
1974
                poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
6✔
1975
            }
1976
        }
1977

1978
        CPLFree(pszFullname);
14✔
1979
    }
1980

1981
    if (poDS->papszSubDatasets != nullptr && eCalib == None)
7✔
1982
    {
1983
        CPLString pszBuf = FormatCalibration(szUNCALIB, osMDFilename.c_str());
3✔
1984
        poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
3✔
1985
                                                 "SUBDATASET_1_NAME", pszBuf);
1986
        poDS->papszSubDatasets =
3✔
1987
            CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_1_DESC",
3✔
1988
                            "Uncalibrated digital numbers");
1989
    }
1990
    else if (poDS->papszSubDatasets != nullptr)
4✔
1991
    {
1992
        CSLDestroy(poDS->papszSubDatasets);
4✔
1993
        poDS->papszSubDatasets = nullptr;
4✔
1994
    }
1995

1996
    /* -------------------------------------------------------------------- */
1997
    /*      Set the appropriate MATRIX_REPRESENTATION.                      */
1998
    /* -------------------------------------------------------------------- */
1999

2000
    if (poDS->GetRasterCount() == 4 &&
7✔
2001
        (eDataType == GDT_CInt16 || eDataType == GDT_CFloat32))
×
2002
    {
2003
        poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
×
2004
    }
2005

2006
    /* -------------------------------------------------------------------- */
2007
    /*      Collect a few useful metadata items.                            */
2008
    /* -------------------------------------------------------------------- */
2009

2010
    pszItem = CPLGetXMLValue(psSourceAttrs, "satellite", "");
7✔
2011
    poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem);
7✔
2012

2013
    pszItem = CPLGetXMLValue(psSourceAttrs, "sensor", "");
7✔
2014
    poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem);
7✔
2015

2016
    /* Get beam mode mnemonic */
2017
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamMode", "UNK");
7✔
2018
    poDS->SetMetadataItem("BEAM_MODE", pszItem);
7✔
2019

2020
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeMnemonic", "UNK");
7✔
2021
    poDS->SetMetadataItem("BEAM_MODE_MNEMONIC", pszItem);
7✔
2022

2023
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeDefinitionId", "UNK");
7✔
2024
    poDS->SetMetadataItem("BEAM_MODE_DEFINITION_ID", pszItem);
7✔
2025

2026
    pszItem = CPLGetXMLValue(psSourceAttrs, "rawDataStartTime", "UNK");
7✔
2027
    poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem);
7✔
2028

2029
    pszItem = CPLGetXMLValue(psSourceAttrs, "inputDatasetFacilityId", "UNK");
7✔
2030
    poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem);
7✔
2031

2032
    pszItem = CPLGetXMLValue(psSourceAttrs,
7✔
2033
                             "orbitAndAttitude.orbitInformation.passDirection",
2034
                             "UNK");
2035
    poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem);
7✔
2036
    pszItem = CPLGetXMLValue(
7✔
2037
        psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataSource",
2038
        "UNK");
2039
    poDS->SetMetadataItem("ORBIT_DATA_SOURCE", pszItem);
7✔
2040
    pszItem = CPLGetXMLValue(
7✔
2041
        psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataFileName",
2042
        "UNK");
2043
    poDS->SetMetadataItem("ORBIT_DATA_FILE", pszItem);
7✔
2044

2045
    /* Get incidence angle information. DONE */
2046
    pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngNearRng",
7✔
2047
                             "UNK");
2048
    poDS->SetMetadataItem("NEAR_RANGE_INCIDENCE_ANGLE", pszItem);
7✔
2049

2050
    pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngFarRng",
7✔
2051
                             "UNK");
2052
    poDS->SetMetadataItem("FAR_RANGE_INCIDENCE_ANGLE", pszItem);
7✔
2053

2054
    pszItem = CPLGetXMLValue(psSceneAttributes,
7✔
2055
                             "imageAttributes.slantRangeNearEdge", "UNK");
2056
    poDS->SetMetadataItem("SLANT_RANGE_NEAR_EDGE", pszItem);
7✔
2057

2058
    pszItem = CPLGetXMLValue(psSceneAttributes,
7✔
2059
                             "imageAttributes.slantRangeFarEdge", "UNK");
2060
    poDS->SetMetadataItem("SLANT_RANGE_FAR_EDGE", pszItem);
7✔
2061

2062
    /*--------------------------------------------------------------------- */
2063
    /*      Collect Map projection/Geotransform information, if present.DONE */
2064
    /*      In RCM, there is no file that indicates                         */
2065
    /* -------------------------------------------------------------------- */
2066
    CPLXMLNode *psMapProjection = CPLGetXMLNode(
7✔
2067
        psImageReferenceAttributes, "geographicInformation.mapProjection");
2068

2069
    if (psMapProjection != nullptr)
7✔
2070
    {
2071
        CPLXMLNode *psPos =
2072
            CPLGetXMLNode(psMapProjection, "positioningInformation");
×
2073

2074
        pszItem =
2075
            CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "UNK");
×
2076
        poDS->SetMetadataItem("MAP_PROJECTION_DESCRIPTOR", pszItem);
×
2077

2078
        pszItem =
2079
            CPLGetXMLValue(psMapProjection, "mapProjectionOrientation", "UNK");
×
2080
        poDS->SetMetadataItem("MAP_PROJECTION_ORIENTATION", pszItem);
×
2081

2082
        pszItem = CPLGetXMLValue(psMapProjection, "resamplingKernel", "UNK");
×
2083
        poDS->SetMetadataItem("RESAMPLING_KERNEL", pszItem);
×
2084

2085
        pszItem = CPLGetXMLValue(psMapProjection, "satelliteHeading", "UNK");
×
2086
        poDS->SetMetadataItem("SATELLITE_HEADING", pszItem);
×
2087

2088
        if (psPos != nullptr)
×
2089
        {
2090
            const double tl_x = CPLStrtod(
×
2091
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.easting",
2092
                               "0.0"),
2093
                nullptr);
2094
            const double tl_y = CPLStrtod(
×
2095
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.northing",
2096
                               "0.0"),
2097
                nullptr);
2098
            const double bl_x = CPLStrtod(
×
2099
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.easting",
2100
                               "0.0"),
2101
                nullptr);
2102
            const double bl_y = CPLStrtod(
×
2103
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.northing",
2104
                               "0.0"),
2105
                nullptr);
2106
            const double tr_x = CPLStrtod(
×
2107
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.easting",
2108
                               "0.0"),
2109
                nullptr);
2110
            const double tr_y = CPLStrtod(
×
2111
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.northing",
2112
                               "0.0"),
2113
                nullptr);
2114
            poDS->adfGeoTransform[1] = (tr_x - tl_x) / (poDS->nRasterXSize - 1);
×
2115
            poDS->adfGeoTransform[4] = (tr_y - tl_y) / (poDS->nRasterXSize - 1);
×
2116
            poDS->adfGeoTransform[2] = (bl_x - tl_x) / (poDS->nRasterYSize - 1);
×
2117
            poDS->adfGeoTransform[5] = (bl_y - tl_y) / (poDS->nRasterYSize - 1);
×
2118
            poDS->adfGeoTransform[0] = (tl_x - 0.5 * poDS->adfGeoTransform[1] -
×
2119
                                        0.5 * poDS->adfGeoTransform[2]);
×
2120
            poDS->adfGeoTransform[3] = (tl_y - 0.5 * poDS->adfGeoTransform[4] -
×
2121
                                        0.5 * poDS->adfGeoTransform[5]);
×
2122

2123
            /* Use bottom right pixel to test geotransform */
2124
            const double br_x = CPLStrtod(
×
2125
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.easting",
2126
                               "0.0"),
2127
                nullptr);
2128
            const double br_y = CPLStrtod(
×
2129
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.northing",
2130
                               "0.0"),
2131
                nullptr);
2132
            const double testx =
2133
                poDS->adfGeoTransform[0] +
×
2134
                poDS->adfGeoTransform[1] * (poDS->nRasterXSize - 0.5) +
×
2135
                poDS->adfGeoTransform[2] * (poDS->nRasterYSize - 0.5);
×
2136
            const double testy =
2137
                poDS->adfGeoTransform[3] +
×
2138
                poDS->adfGeoTransform[4] * (poDS->nRasterXSize - 0.5) +
×
2139
                poDS->adfGeoTransform[5] * (poDS->nRasterYSize - 0.5);
×
2140

2141
            /* Give 1/4 pixel numerical error leeway in calculating location
2142
            based on affine transform */
2143
            if ((fabs(testx - br_x) >
×
2144
                 fabs(0.25 *
×
2145
                      (poDS->adfGeoTransform[1] + poDS->adfGeoTransform[2]))) ||
×
2146
                (fabs(testy - br_y) > fabs(0.25 * (poDS->adfGeoTransform[4] +
×
2147
                                                   poDS->adfGeoTransform[5]))))
×
2148
            {
2149
                CPLError(CE_Warning, CPLE_AppDefined,
×
2150
                         "WARNING: Unexpected error in calculating affine "
2151
                         "transform: corner coordinates inconsistent.");
2152
            }
2153
            else
2154
            {
2155
                poDS->bHaveGeoTransform = TRUE;
×
2156
            }
2157
        }
2158
    }
2159

2160
    /* -------------------------------------------------------------------- */
2161
    /*      Collect Projection String Information.DONE                      */
2162
    /* -------------------------------------------------------------------- */
2163
    CPLXMLNode *psEllipsoid =
2164
        CPLGetXMLNode(psImageReferenceAttributes,
7✔
2165
                      "geographicInformation.ellipsoidParameters");
2166

2167
    if (psEllipsoid != nullptr)
7✔
2168
    {
2169
        OGRSpatialReference oLL, oPrj;
14✔
2170

2171
        const char *pszGeodeticTerrainHeight =
2172
            CPLGetXMLValue(psEllipsoid, "geodeticTerrainHeight", "UNK");
7✔
2173
        poDS->SetMetadataItem("GEODETIC_TERRAIN_HEIGHT",
7✔
2174
                              pszGeodeticTerrainHeight);
7✔
2175

2176
        const char *pszEllipsoidName =
2177
            CPLGetXMLValue(psEllipsoid, "ellipsoidName", "");
7✔
2178
        double minor_axis =
2179
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMinorAxis", "0.0"));
7✔
2180
        double major_axis =
2181
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMajorAxis", "0.0"));
7✔
2182

2183
        if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) ||
7✔
2184
            (major_axis == 0.0))
2185
        {
2186
            oLL.SetWellKnownGeogCS("WGS84");
×
2187
            oPrj.SetWellKnownGeogCS("WGS84");
×
2188

2189
            CPLError(CE_Warning, CPLE_AppDefined,
×
2190
                     "WARNING: Incomplete ellipsoid "
2191
                     "information.  Using wgs-84 parameters.");
2192
        }
2193
        else if (EQUAL(pszEllipsoidName, "WGS84") ||
7✔
2194
                 EQUAL(pszEllipsoidName, "WGS 1984"))
7✔
2195
        {
2196
            oLL.SetWellKnownGeogCS("WGS84");
7✔
2197
            oPrj.SetWellKnownGeogCS("WGS84");
7✔
2198
        }
2199
        else
2200
        {
2201
            const double inv_flattening =
×
2202
                major_axis / (major_axis - minor_axis);
×
2203
            oLL.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening);
×
2204
            oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis,
×
2205
                           inv_flattening);
2206
        }
2207

2208
        if (psMapProjection != nullptr)
7✔
2209
        {
2210
            const char *pszProj =
2211
                CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "");
×
2212
            bool bUseProjInfo = false;
×
2213

2214
            CPLXMLNode *psUtmParams =
2215
                CPLGetXMLNode(psMapProjection, "utmProjectionParameters");
×
2216

2217
            CPLXMLNode *psNspParams =
2218
                CPLGetXMLNode(psMapProjection, "nspProjectionParameters");
×
2219

2220
            if ((psUtmParams != nullptr) && poDS->bHaveGeoTransform)
×
2221
            {
2222
                /* double origEasting, origNorthing; */
2223
                bool bNorth = true;
×
2224

2225
                const int utmZone =
2226
                    atoi(CPLGetXMLValue(psUtmParams, "utmZone", ""));
×
2227
                const char *pszHemisphere =
2228
                    CPLGetXMLValue(psUtmParams, "hemisphere", "");
×
2229
#if 0
2230
                origEasting = CPLStrtod(CPLGetXMLValue(
2231
                    psUtmParams, "mapOriginFalseEasting", "0.0"), nullptr);
2232
                origNorthing = CPLStrtod(CPLGetXMLValue(
2233
                    psUtmParams, "mapOriginFalseNorthing", "0.0"), nullptr);
2234
#endif
2235
                if (STARTS_WITH_CI(pszHemisphere, "southern"))
×
2236
                    bNorth = FALSE;
×
2237

2238
                if (STARTS_WITH_CI(pszProj, "UTM"))
×
2239
                {
2240
                    oPrj.SetUTM(utmZone, bNorth);
×
2241
                    bUseProjInfo = true;
×
2242
                }
2243
            }
2244
            else if ((psNspParams != nullptr) && poDS->bHaveGeoTransform)
×
2245
            {
2246
                const double origEasting = CPLStrtod(
×
2247
                    CPLGetXMLValue(psNspParams, "mapOriginFalseEasting", "0.0"),
2248
                    nullptr);
2249
                const double origNorthing =
2250
                    CPLStrtod(CPLGetXMLValue(psNspParams,
×
2251
                                             "mapOriginFalseNorthing", "0.0"),
2252
                              nullptr);
2253
                const double copLong = CPLStrtod(
×
2254
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLongitude",
2255
                                   "0.0"),
2256
                    nullptr);
2257
                const double copLat = CPLStrtod(
×
2258
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLatitude",
2259
                                   "0.0"),
2260
                    nullptr);
2261
                const double sP1 = CPLStrtod(
×
2262
                    CPLGetXMLValue(psNspParams, "standardParallels1", "0.0"),
2263
                    nullptr);
2264
                const double sP2 = CPLStrtod(
×
2265
                    CPLGetXMLValue(psNspParams, "standardParallels2", "0.0"),
2266
                    nullptr);
2267

2268
                if (STARTS_WITH_CI(pszProj, "ARC"))
×
2269
                {
2270
                    /* Albers Conical Equal Area */
2271
                    oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting,
×
2272
                                 origNorthing);
2273
                    bUseProjInfo = true;
×
2274
                }
2275
                else if (STARTS_WITH_CI(pszProj, "LCC"))
×
2276
                {
2277
                    /* Lambert Conformal Conic */
2278
                    oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting,
×
2279
                                origNorthing);
2280
                    bUseProjInfo = true;
×
2281
                }
2282
                else if (STARTS_WITH_CI(pszProj, "STPL"))
×
2283
                {
2284
                    /* State Plate
2285
                    ASSUMPTIONS: "zone" in product.xml matches USGS
2286
                    definition as expected by ogr spatial reference; NAD83
2287
                    zones (versus NAD27) are assumed. */
2288

2289
                    const int nSPZone =
2290
                        atoi(CPLGetXMLValue(psNspParams, "zone", "1"));
×
2291

2292
                    oPrj.SetStatePlane(nSPZone, TRUE, nullptr, 0.0);
×
2293
                    bUseProjInfo = true;
×
2294
                }
2295
            }
2296

2297
            if (bUseProjInfo)
×
2298
            {
2299
                poDS->m_oSRS = std::move(oPrj);
×
2300
            }
2301
            else
2302
            {
2303
                CPLError(CE_Warning, CPLE_AppDefined,
×
2304
                         "WARNING: Unable to interpret projection information; "
2305
                         "check mapProjection info in product.xml!");
2306
            }
2307
        }
2308

2309
        poDS->m_oGCPSRS = std::move(oLL);
7✔
2310
    }
2311

2312
    /* -------------------------------------------------------------------- */
2313
    /*      Collect GCPs.DONE */
2314
    /* -------------------------------------------------------------------- */
2315
    CPLXMLNode *psGeoGrid = CPLGetXMLNode(
7✔
2316
        psImageReferenceAttributes, "geographicInformation.geolocationGrid");
2317

2318
    if (psGeoGrid != nullptr)
7✔
2319
    {
2320
        /* count GCPs */
2321
        poDS->nGCPCount = 0;
7✔
2322

2323
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
14✔
2324
             psNode = psNode->psNext)
7✔
2325
        {
2326
            if (EQUAL(psNode->pszValue, "imageTiePoint"))
7✔
2327
                poDS->nGCPCount++;
7✔
2328
        }
2329

2330
        poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>(
14✔
2331
            CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount));
7✔
2332

2333
        poDS->nGCPCount = 0;
7✔
2334

2335
        for (psNode = psGeoGrid->psChild; psNode != nullptr;
14✔
2336
             psNode = psNode->psNext)
7✔
2337
        {
2338
            GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount;
7✔
2339

2340
            if (!EQUAL(psNode->pszValue, "imageTiePoint"))
7✔
2341
                continue;
×
2342

2343
            poDS->nGCPCount++;
7✔
2344

2345
            char szID[32];
2346
            snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount);
7✔
2347
            psGCP->pszId = CPLStrdup(szID);
7✔
2348
            psGCP->pszInfo = CPLStrdup("");
7✔
2349
            psGCP->dfGCPPixel =
7✔
2350
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.pixel", "0"));
7✔
2351
            psGCP->dfGCPLine =
7✔
2352
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.line", "0"));
7✔
2353
            psGCP->dfGCPX = CPLAtof(
7✔
2354
                CPLGetXMLValue(psNode, "geodeticCoordinate.longitude", ""));
2355
            psGCP->dfGCPY = CPLAtof(
7✔
2356
                CPLGetXMLValue(psNode, "geodeticCoordinate.latitude", ""));
2357
            psGCP->dfGCPZ = CPLAtof(
7✔
2358
                CPLGetXMLValue(psNode, "geodeticCoordinate.height", ""));
2359
        }
2360
    }
2361

2362
    if (pszBeta0LUT)
7✔
2363
        CPLFree(pszBeta0LUT);
7✔
2364
    if (pszSigma0LUT)
7✔
2365
        CPLFree(pszSigma0LUT);
7✔
2366
    if (pszGammaLUT)
7✔
2367
        CPLFree(pszGammaLUT);
7✔
2368

2369
    /* -------------------------------------------------------------------- */
2370
    /*      Collect RPC.DONE */
2371
    /* -------------------------------------------------------------------- */
2372
    CPLXMLNode *psRationalFunctions = CPLGetXMLNode(
7✔
2373
        psImageReferenceAttributes, "geographicInformation.rationalFunctions");
2374
    if (psRationalFunctions != nullptr)
7✔
2375
    {
2376
        char **papszRPC = nullptr;
7✔
2377
        static const char *const apszXMLToGDALMapping[] = {
2378
            "biasError",
2379
            "ERR_BIAS",
2380
            "randomError",
2381
            "ERR_RAND",
2382
            //"lineFitQuality", "????",
2383
            //"pixelFitQuality", "????",
2384
            "lineOffset",
2385
            "LINE_OFF",
2386
            "pixelOffset",
2387
            "SAMP_OFF",
2388
            "latitudeOffset",
2389
            "LAT_OFF",
2390
            "longitudeOffset",
2391
            "LONG_OFF",
2392
            "heightOffset",
2393
            "HEIGHT_OFF",
2394
            "lineScale",
2395
            "LINE_SCALE",
2396
            "pixelScale",
2397
            "SAMP_SCALE",
2398
            "latitudeScale",
2399
            "LAT_SCALE",
2400
            "longitudeScale",
2401
            "LONG_SCALE",
2402
            "heightScale",
2403
            "HEIGHT_SCALE",
2404
            "lineNumeratorCoefficients",
2405
            "LINE_NUM_COEFF",
2406
            "lineDenominatorCoefficients",
2407
            "LINE_DEN_COEFF",
2408
            "pixelNumeratorCoefficients",
2409
            "SAMP_NUM_COEFF",
2410
            "pixelDenominatorCoefficients",
2411
            "SAMP_DEN_COEFF",
2412
        };
2413
        for (size_t i = 0; i < CPL_ARRAYSIZE(apszXMLToGDALMapping); i += 2)
119✔
2414
        {
2415
            const char *pszValue = CPLGetXMLValue(
224✔
2416
                psRationalFunctions, apszXMLToGDALMapping[i], nullptr);
112✔
2417
            if (pszValue)
112✔
2418
                papszRPC = CSLSetNameValue(
112✔
2419
                    papszRPC, apszXMLToGDALMapping[i + 1], pszValue);
112✔
2420
        }
2421
        poDS->GDALDataset::SetMetadata(papszRPC, "RPC");
7✔
2422
        CSLDestroy(papszRPC);
7✔
2423
    }
2424

2425
    /* -------------------------------------------------------------------- */
2426
    /*      Initialize any PAM information.                                 */
2427
    /* -------------------------------------------------------------------- */
2428
    CPLString osDescription;
14✔
2429
    CPLString osSubdatasetName;
14✔
2430
    bool useSubdatasets = true;
7✔
2431

2432
    switch (eCalib)
7✔
2433
    {
2434
        case Sigma0:
1✔
2435
        {
2436
            osSubdatasetName = szSIGMA0;
1✔
2437
            osDescription = FormatCalibration(szSIGMA0, osMDFilename.c_str());
1✔
2438
        }
2439
        break;
1✔
2440
        case Beta0:
1✔
2441
        {
2442
            osSubdatasetName = szBETA0;
1✔
2443
            osDescription = FormatCalibration(szBETA0, osMDFilename.c_str());
1✔
2444
        }
2445
        break;
1✔
2446
        case Gamma:
1✔
2447
        {
2448
            osSubdatasetName = szGAMMA;
1✔
2449
            osDescription = FormatCalibration(szGAMMA, osMDFilename.c_str());
1✔
2450
        }
2451
        break;
1✔
2452
        case Uncalib:
1✔
2453
        {
2454
            osSubdatasetName = szUNCALIB;
1✔
2455
            osDescription = FormatCalibration(szUNCALIB, osMDFilename.c_str());
1✔
2456
        }
2457
        break;
1✔
2458
        default:
3✔
2459
            osSubdatasetName = szUNCALIB;
3✔
2460
            osDescription = osMDFilename;
3✔
2461
            useSubdatasets = false;
3✔
2462
    }
2463

2464
    CPL_IGNORE_RET_VAL(osSubdatasetName);
7✔
2465

2466
    if (eCalib != None)
7✔
2467
        poDS->papszExtraFiles =
4✔
2468
            CSLAddString(poDS->papszExtraFiles, osMDFilename);
4✔
2469

2470
    /* -------------------------------------------------------------------- */
2471
    /*      Initialize any PAM information.                                 */
2472
    /* -------------------------------------------------------------------- */
2473
    poDS->SetDescription(osDescription);
7✔
2474

2475
    poDS->SetPhysicalFilename(osMDFilename);
7✔
2476
    poDS->SetSubdatasetName(osDescription);
7✔
2477

2478
    poDS->TryLoadXML();
7✔
2479

2480
    /* -------------------------------------------------------------------- */
2481
    /*      Check for overviews.                                            */
2482
    /* -------------------------------------------------------------------- */
2483
    if (useSubdatasets)
7✔
2484
        poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
4✔
2485
    else
2486
        poDS->oOvManager.Initialize(poDS.get(), osMDFilename);
3✔
2487

2488
    return poDS.release();
7✔
2489
}
2490

2491
/************************************************************************/
2492
/*                            GetGCPCount()                             */
2493
/************************************************************************/
2494

2495
int RCMDataset::GetGCPCount()
6✔
2496

2497
{
2498
    return nGCPCount;
6✔
2499
}
2500

2501
/************************************************************************/
2502
/*                          GetGCPSpatialRef()                          */
2503
/************************************************************************/
2504

2505
const OGRSpatialReference *RCMDataset::GetGCPSpatialRef() const
1✔
2506

2507
{
2508
    return m_oGCPSRS.IsEmpty() || nGCPCount == 0 ? nullptr : &m_oGCPSRS;
1✔
2509
}
2510

2511
/************************************************************************/
2512
/*                               GetGCPs()                              */
2513
/************************************************************************/
2514

2515
const GDAL_GCP *RCMDataset::GetGCPs()
5✔
2516

2517
{
2518
    return pasGCPList;
5✔
2519
}
2520

2521
/************************************************************************/
2522
/*                          GetSpatialRef()                             */
2523
/************************************************************************/
2524

2525
const OGRSpatialReference *RCMDataset::GetSpatialRef() const
×
2526

2527
{
2528
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
×
2529
}
2530

2531
/************************************************************************/
2532
/*                          GetGeoTransform()                           */
2533
/************************************************************************/
2534

2535
CPLErr RCMDataset::GetGeoTransform(double *padfTransform)
×
2536

2537
{
2538
    memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
×
2539

2540
    if (bHaveGeoTransform)
×
2541
    {
2542
        return CE_None;
×
2543
    }
2544

2545
    return CE_Failure;
×
2546
}
2547

2548
/************************************************************************/
2549
/*                      GetMetadataDomainList()                         */
2550
/************************************************************************/
2551

2552
char **RCMDataset::GetMetadataDomainList()
×
2553
{
2554
    return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
×
2555
                                   "SUBDATASETS", nullptr);
×
2556
}
2557

2558
/************************************************************************/
2559
/*                            GetMetadata()                             */
2560
/************************************************************************/
2561

2562
char **RCMDataset::GetMetadata(const char *pszDomain)
2✔
2563

2564
{
2565
    if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS") &&
2✔
2566
        papszSubDatasets != nullptr)
×
2567
        return papszSubDatasets;
×
2568

2569
    return GDALDataset::GetMetadata(pszDomain);
2✔
2570
}
2571

2572
/************************************************************************/
2573
/*                         GDALRegister_RCM()                           */
2574
/************************************************************************/
2575

2576
void GDALRegister_RCM()
1,911✔
2577

2578
{
2579
    if (GDALGetDriverByName("RCM") != nullptr)
1,911✔
2580
    {
2581
        return;
282✔
2582
    }
2583

2584
    GDALDriver *poDriver = new GDALDriver();
1,629✔
2585
    RCMDriverSetCommonMetadata(poDriver);
1,629✔
2586

2587
    poDriver->pfnOpen = RCMDataset::Open;
1,629✔
2588

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