Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/rcm/rcmdataset.cpp
Line
Count
Source
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,
41
                                   const char *pszFilename)
42
0
{
43
0
    CPLString ptr;
44
45
    // Always begin by the layer calibration name
46
0
    ptr.append(szLayerCalibration);
47
48
    // A separator is needed before concat calibration name
49
0
    ptr += chLayerSeparator;
50
    // Add calibration name
51
0
    ptr.append(pszCalibName);
52
53
    // A separator is needed before concat full filename name
54
0
    ptr += chLayerSeparator;
55
    // Add full filename name
56
0
    ptr.append(pszFilename);
57
58
    /* return calibration format */
59
0
    return ptr;
60
0
}
61
62
/*** Function to test for valid LUT files ***/
63
static bool IsValidXMLFile(const char *pszPath)
64
0
{
65
    /* Return true for valid xml file, false otherwise */
66
0
    CPLXMLTreeCloser psLut(CPLParseXMLFile(pszPath));
67
68
0
    if (psLut.get() == nullptr)
69
0
    {
70
0
        CPLError(CE_Failure, CPLE_OpenFailed,
71
0
                 "ERROR: Failed to open the LUT file %s", pszPath);
72
0
    }
73
74
0
    return psLut.get() != nullptr;
75
0
}
76
77
static double *InterpolateValues(CSLConstList papszList, int tableSize,
78
                                 int stepSize, int numberOfValues,
79
                                 int pixelFirstLutValue)
80
0
{
81
    /* Allocate the right LUT size according to the product range pixel */
82
0
    double *table =
83
0
        static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), tableSize));
84
0
    if (!table)
85
0
        return nullptr;
86
87
0
    if (stepSize <= 0)
88
0
    {
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
0
        const int positiveStepSize = abs(stepSize);
93
94
0
        int k = 0;
95
96
0
        if (positiveStepSize == 1)
97
0
        {
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
0
            for (int i = pixelFirstLutValue; i >= 0; i--)
102
0
            {
103
0
                const double value = CPLAtof(papszList[i]);
104
0
                table[k++] = value;
105
0
            }
106
0
        }
107
0
        else
108
0
        {
109
110
            /* Interpolation between 2 numbers */
111
0
            for (int i = numberOfValues - 1; i >= 0; i--)
112
0
            {
113
                // We will consider the same value to cover the case that we
114
                // will hit the last pixel
115
0
                double valueFrom = CPLAtof(papszList[i]);
116
0
                double valueTo = valueFrom;
117
118
0
                if (i > 0)
119
0
                {
120
                    // We have room to pick the previous number to interpolate
121
                    // with
122
0
                    valueTo = CPLAtof(papszList[i - 1]);
123
0
                }
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
0
                double interp = (valueTo - valueFrom) / positiveStepSize;
128
129
                // Always begin with the value FROM found
130
0
                table[k++] = valueFrom;
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
0
                for (int j = 0; j < positiveStepSize - 1; j++)
136
0
                {
137
0
                    valueFrom += interp;
138
0
                    table[k++] = valueFrom;
139
0
                }
140
0
            }
141
0
        }
142
0
    }
143
0
    else
144
0
    {
145
        /* When positive, the range of pixel is calculated from the beginning of
146
         * gains array */
147
0
        if (stepSize == 1)
148
0
        {
149
            // Be fast and just copy the values because all gain values
150
            // represent all image wide pixel
151
0
            for (int i = 0; i < numberOfValues; i++)
152
0
            {
153
0
                const double value = CPLAtof(papszList[i]);
154
0
                table[i] = value;
155
0
            }
156
0
        }
157
0
        else
158
0
        {
159
            /* Interpolation between 2 numbers */
160
0
            int k = 0;
161
0
            for (int i = 0; i < numberOfValues; i++)
162
0
            {
163
                // We will consider the same value to cover the case that we
164
                // will hit the last pixel
165
0
                double valueFrom = CPLAtof(papszList[i]);
166
0
                double valueTo = valueFrom;
167
168
0
                if (i < (numberOfValues)-1)
169
0
                {
170
                    // We have room to pick the next number to interpolate with
171
0
                    valueTo = CPLAtof(papszList[i + 1]);
172
0
                }
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
0
                double interp = (valueTo - valueFrom) / stepSize;
177
178
                // Always begin with the value FROM found
179
0
                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
0
                for (int j = 0; j < stepSize - 1; j++)
185
0
                {
186
0
                    valueFrom += interp;
187
0
                    table[k++] = valueFrom;
188
0
                }
189
0
            }
190
0
        }
191
0
    }
192
193
0
    return table;
194
0
}
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,
210
                                              GDALDataset *poBandFile,
211
                                              bool isNITF)
212
0
{
213
214
0
    GDALRasterBand *band1 = poBandFile->GetRasterBand(1);
215
0
    GDALDataType bandfileType = band1->GetRasterDataType();
216
    // if there is one band and it has the same datatype, the band file gets
217
    // passed straight through
218
0
    if ((poBandFile->GetRasterCount() == 1 ||
219
0
         poBandFile->GetRasterCount() == 4) &&
220
0
        dataType == bandfileType)
221
0
        return STRAIGHT;
222
223
    // if the band file has 2 bands, they should represent I+Q
224
    // and be a compatible data type
225
0
    if (poBandFile->GetRasterCount() == 2 && GDALDataTypeIsComplex(dataType))
226
0
    {
227
0
        GDALRasterBand *band2 = poBandFile->GetRasterBand(2);
228
229
0
        if (bandfileType != band2->GetRasterDataType())
230
0
            return BANDERROR;  // both bands must be same datatype
231
232
        // check compatible types - there are 4 complex types in GDAL
233
0
        if ((dataType == GDT_CInt16 && bandfileType == GDT_Int16) ||
234
0
            (dataType == GDT_CInt32 && bandfileType == GDT_Int32) ||
235
0
            (dataType == GDT_CFloat32 && bandfileType == GDT_Float32) ||
236
0
            (dataType == GDT_CFloat64 && bandfileType == GDT_Float64))
237
0
            return TWOBANDCOMPLEX;
238
239
0
        if ((dataType == GDT_CInt16 && bandfileType == GDT_CInt16) ||
240
0
            (dataType == GDT_CInt32 && bandfileType == GDT_CInt32) ||
241
0
            (dataType == GDT_CFloat32 && bandfileType == GDT_CFloat32) ||
242
0
            (dataType == GDT_CFloat64 && bandfileType == GDT_CFloat64))
243
0
            return TWOBANDCOMPLEX;
244
0
    }
245
246
0
    if (isNITF)
247
0
    {
248
0
        return STRAIGHT;
249
0
    }
250
251
0
    return BANDERROR;  // don't accept any other combinations
252
0
}
253
254
/************************************************************************/
255
/*                            RCMRasterBand                             */
256
/************************************************************************/
257
258
RCMRasterBand::RCMRasterBand(RCMDataset *poDSIn, int nBandIn,
259
                             GDALDataType eDataTypeIn, const char *pszPole,
260
                             GDALDataset *poBandFileIn, bool bTwoBandComplex,
261
                             bool isOneFilePerPolIn, bool isNITFIn)
262
0
    : poBandFile(poBandFileIn), poRCMDataset(poDSIn),
263
0
      twoBandComplex(bTwoBandComplex), isOneFilePerPol(isOneFilePerPolIn),
264
0
      isNITF(isNITFIn)
265
0
{
266
0
    poDS = poDSIn;
267
0
    this->nBand = nBandIn;
268
0
    eDataType = eDataTypeIn;
269
270
    /*Check image type, whether there is one file per polarization or
271
     *one file containing all polarizations*/
272
0
    if (this->isOneFilePerPol)
273
0
    {
274
0
        poBand = poBandFile->GetRasterBand(1);
275
0
    }
276
0
    else
277
0
    {
278
0
        poBand = poBandFile->GetRasterBand(this->nBand);
279
0
    }
280
281
0
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
282
283
0
    if (pszPole != nullptr && strlen(pszPole) != 0)
284
0
    {
285
0
        SetMetadataItem("POLARIMETRIC_INTERP", pszPole);
286
0
    }
287
0
}
288
289
/************************************************************************/
290
/*                           RCMRasterBand()                            */
291
/************************************************************************/
292
293
RCMRasterBand::~RCMRasterBand()
294
295
0
{
296
0
    if (poBandFile != nullptr)
297
0
        GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
298
0
}
299
300
/************************************************************************/
301
/*                             IReadBlock()                             */
302
/************************************************************************/
303
304
CPLErr RCMRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
305
306
0
{
307
0
    int nRequestXSize = 0;
308
0
    int nRequestYSize = 0;
309
0
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nRequestXSize, &nRequestYSize);
310
311
    // Zero initial partial right-most and bottom-most blocks
312
0
    if (nRequestXSize < nBlockXSize || nRequestYSize < nBlockYSize)
313
0
    {
314
0
        memset(pImage, 0,
315
0
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
316
0
                   nBlockXSize * nBlockYSize);
317
0
    }
318
319
0
    int dataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
320
0
    GDALDataType bandFileType =
321
0
        poBandFile->GetRasterBand(1)->GetRasterDataType();
322
0
    int bandFileSize = GDALGetDataTypeSizeBytes(bandFileType);
323
324
    // case: 2 bands representing I+Q -> one complex band
325
0
    if (twoBandComplex && !this->isNITF)
326
0
    {
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
0
        return
333
            // I and Q from each band are pixel-interleaved into this complex
334
            // band
335
0
            poBandFile->RasterIO(
336
0
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
337
0
                nRequestXSize, nRequestYSize, pImage, nRequestXSize,
338
0
                nRequestYSize, bandFileType, 2, nullptr, dataTypeSize,
339
0
                static_cast<GSpacing>(dataTypeSize) * nBlockXSize, bandFileSize,
340
0
                nullptr);
341
0
    }
342
0
    else if (twoBandComplex && this->isNITF)
343
0
    {
344
0
        return poBand->RasterIO(
345
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
346
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
347
0
            eDataType, 0, static_cast<GSpacing>(dataTypeSize) * nBlockXSize,
348
0
            nullptr);
349
0
    }
350
351
0
    if (poRCMDataset->IsComplexData())
352
0
    {
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
0
        return
357
            // I and Q from each band are pixel-interleaved into this complex
358
            // band
359
0
            poBandFile->RasterIO(
360
0
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
361
0
                nRequestXSize, nRequestYSize, pImage, nRequestXSize,
362
0
                nRequestYSize, bandFileType, 2, nullptr, dataTypeSize,
363
0
                static_cast<GSpacing>(dataTypeSize) * nBlockXSize, bandFileSize,
364
0
                nullptr);
365
0
    }
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
0
    else if (poBandFile->GetRasterBand(1)->GetRasterDataType() == eDataType)
371
0
    {
372
0
        return poBand->RasterIO(
373
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
374
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
375
0
            eDataType, 0, static_cast<GSpacing>(dataTypeSize) * nBlockXSize,
376
0
            nullptr);
377
0
    }
378
0
    else
379
0
    {
380
0
        CPLAssert(FALSE);
381
0
        return CE_Failure;
382
0
    }
383
0
}
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()
398
0
{
399
400
0
    char bandNumber[12];
401
0
    snprintf(bandNumber, sizeof(bandNumber), "%d", poDS->GetRasterCount() + 1);
402
403
0
    CPLXMLTreeCloser psLUT(CPLParseXMLFile(m_pszLUTFile));
404
0
    if (!psLUT)
405
0
        return;
406
407
0
    this->m_nfOffset =
408
0
        CPLAtof(CPLGetXMLValue(psLUT.get(), "=lut.offset", "0.0"));
409
410
0
    this->pixelFirstLutValue =
411
0
        atoi(CPLGetXMLValue(psLUT.get(), "=lut.pixelFirstLutValue", "0"));
412
413
0
    this->stepSize = atoi(CPLGetXMLValue(psLUT.get(), "=lut.stepSize", "0"));
414
415
0
    this->numberOfValues =
416
0
        atoi(CPLGetXMLValue(psLUT.get(), "=lut.numberOfValues", "0"));
417
418
0
    if (this->numberOfValues <= 0)
419
0
    {
420
0
        CPLError(
421
0
            CE_Failure, CPLE_NotSupported,
422
0
            "ERROR: The RCM driver does not support the LUT Number Of Values  "
423
0
            "equal or lower than zero.");
424
0
        return;
425
0
    }
426
427
0
    const CPLStringList aosLUTList(
428
0
        CSLTokenizeString2(CPLGetXMLValue(psLUT.get(), "=lut.gains", ""), " ",
429
0
                           CSLT_HONOURSTRINGS));
430
431
0
    if (this->stepSize <= 0)
432
0
    {
433
0
        if (this->pixelFirstLutValue <= 0)
434
0
        {
435
0
            CPLError(
436
0
                CE_Failure, CPLE_NotSupported,
437
0
                "ERROR: The RCM driver does not support LUT Pixel First Lut "
438
0
                "Value equal or lower than zero when the product is "
439
0
                "descending.");
440
0
            return;
441
0
        }
442
0
    }
443
444
    /* Get the Pixel Per range */
445
0
    if (this->stepSize == 0 || this->stepSize == INT_MIN ||
446
0
        this->numberOfValues == INT_MIN ||
447
0
        abs(this->stepSize) > INT_MAX / abs(this->numberOfValues))
448
0
    {
449
0
        CPLError(CE_Failure, CPLE_AppDefined,
450
0
                 "Bad values of stepSize / numberOfValues");
451
0
        return;
452
0
    }
453
454
0
    this->m_nTableSize = abs(this->stepSize) * abs(this->numberOfValues);
455
456
0
    if (this->m_nTableSize < this->m_poBandDataset->GetRasterXSize())
457
0
    {
458
0
        CPLError(
459
0
            CE_Failure, CPLE_NotSupported,
460
0
            "ERROR: The RCM driver does not support range of LUT gain values "
461
0
            "lower than the full image pixel range.");
462
0
        return;
463
0
    }
464
465
    // Avoid excessive memory allocation
466
0
    if (this->m_nTableSize > 1000 * 1000)
467
0
    {
468
0
        CPLError(CE_Failure, CPLE_NotSupported, "Too many elements in LUT: %d",
469
0
                 this->m_nTableSize);
470
0
        return;
471
0
    }
472
473
    /* Allocate the right LUT size according to the product range pixel */
474
0
    this->m_nfTable =
475
0
        InterpolateValues(aosLUTList.List(), this->m_nTableSize, this->stepSize,
476
0
                          this->numberOfValues, this->pixelFirstLutValue);
477
0
    if (!this->m_nfTable)
478
0
        return;
479
480
    // 32 max + space
481
0
    char *lut_gains = static_cast<char *>(
482
0
        VSI_CALLOC_VERBOSE(this->m_nTableSize, max_space_for_string));
483
0
    if (!lut_gains)
484
0
        return;
485
486
0
    for (int i = 0; i < this->m_nTableSize; i++)
487
0
    {
488
0
        char lut[max_space_for_string];
489
        // 6.123004711900930e+04  %e Scientific annotation
490
0
        snprintf(lut, sizeof(lut), "%e ", this->m_nfTable[i]);
491
0
        strcat(lut_gains, lut);
492
0
    }
493
494
0
    poDS->SetMetadataItem(CPLString("LUT_GAINS_").append(bandNumber).c_str(),
495
0
                          lut_gains);
496
    // Can free this because the function SetMetadataItem takes a copy
497
0
    CPLFree(lut_gains);
498
499
0
    char snum[256];
500
0
    if (this->m_eCalib == eCalibration::Sigma0)
501
0
    {
502
0
        poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(),
503
0
                              "SIGMA0");
504
0
    }
505
0
    else if (this->m_eCalib == eCalibration::Beta0)
506
0
    {
507
0
        poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(),
508
0
                              "BETA0");
509
0
    }
510
0
    else if (this->m_eCalib == eCalibration::Gamma)
511
0
    {
512
0
        poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(),
513
0
                              "GAMMA");
514
0
    }
515
0
    snprintf(snum, sizeof(snum), "%d", this->m_nTableSize);
516
0
    poDS->SetMetadataItem(CPLString("LUT_SIZE_").append(bandNumber).c_str(),
517
0
                          snum);
518
0
    snprintf(snum, sizeof(snum), "%f", this->m_nfOffset);
519
0
    poDS->SetMetadataItem(CPLString("LUT_OFFSET_").append(bandNumber).c_str(),
520
0
                          snum);
521
0
}
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()
536
0
{
537
538
0
    this->m_nfTableNoiseLevels = nullptr;
539
540
0
    if (this->m_pszNoiseLevelsFile == nullptr)
541
0
    {
542
0
        return;
543
0
    }
544
545
0
    char bandNumber[12];
546
0
    snprintf(bandNumber, sizeof(bandNumber), "%d", poDS->GetRasterCount() + 1);
547
548
0
    CPLXMLTreeCloser psNoiseLevels(CPLParseXMLFile(this->m_pszNoiseLevelsFile));
549
0
    if (!psNoiseLevels)
550
0
        return;
551
552
    // Load Beta Nought, Sigma Nought, Gamma noise levels
553
    // Loop through all nodes with spaces
554
0
    const CPLXMLNode *psReferenceNoiseLevelNode =
555
0
        CPLGetXMLNode(psNoiseLevels.get(), "=noiseLevels");
556
0
    if (!psReferenceNoiseLevelNode)
557
0
        return;
558
559
0
    for (const CPLXMLNode *psNodeInc = psReferenceNoiseLevelNode->psChild;
560
0
         psNodeInc != nullptr; psNodeInc = psNodeInc->psNext)
561
0
    {
562
0
        if (EQUAL(psNodeInc->pszValue, "referenceNoiseLevel"))
563
0
        {
564
0
            const CPLXMLNode *psCalibType =
565
0
                CPLGetXMLNode(psNodeInc, "sarCalibrationType");
566
0
            const CPLXMLNode *psPixelFirstNoiseValue =
567
0
                CPLGetXMLNode(psNodeInc, "pixelFirstNoiseValue");
568
0
            const CPLXMLNode *psStepSize = CPLGetXMLNode(psNodeInc, "stepSize");
569
0
            const CPLXMLNode *psNumberOfValues =
570
0
                CPLGetXMLNode(psNodeInc, "numberOfValues");
571
0
            const CPLXMLNode *psNoiseLevelValues =
572
0
                CPLGetXMLNode(psNodeInc, "noiseLevelValues");
573
574
0
            if (psCalibType != nullptr && psPixelFirstNoiseValue != nullptr &&
575
0
                psStepSize != nullptr && psNumberOfValues != nullptr &&
576
0
                psNoiseLevelValues != nullptr)
577
0
            {
578
0
                const char *calibType = CPLGetXMLValue(psCalibType, "", "");
579
0
                this->pixelFirstLutValueNoiseLevels =
580
0
                    atoi(CPLGetXMLValue(psPixelFirstNoiseValue, "", "0"));
581
0
                this->stepSizeNoiseLevels =
582
0
                    atoi(CPLGetXMLValue(psStepSize, "", "0"));
583
0
                this->numberOfValuesNoiseLevels =
584
0
                    atoi(CPLGetXMLValue(psNumberOfValues, "", "0"));
585
0
                const char *noiseLevelValues =
586
0
                    CPLGetXMLValue(psNoiseLevelValues, "", "");
587
0
                if (this->stepSizeNoiseLevels > 0 &&
588
0
                    this->numberOfValuesNoiseLevels != INT_MIN &&
589
0
                    abs(this->numberOfValuesNoiseLevels) <
590
0
                        INT_MAX / this->stepSizeNoiseLevels)
591
0
                {
592
0
                    char **papszNoiseLevelList = CSLTokenizeString2(
593
0
                        noiseLevelValues, " ", CSLT_HONOURSTRINGS);
594
                    /* Get the Pixel Per range */
595
0
                    this->m_nTableNoiseLevelsSize =
596
0
                        abs(this->stepSizeNoiseLevels) *
597
0
                        abs(this->numberOfValuesNoiseLevels);
598
599
0
                    if ((EQUAL(calibType, "Beta Nought") &&
600
0
                         this->m_eCalib == Beta0) ||
601
0
                        (EQUAL(calibType, "Sigma Nought") &&
602
0
                         this->m_eCalib == Sigma0) ||
603
0
                        (EQUAL(calibType, "Gamma") && this->m_eCalib == Gamma))
604
0
                    {
605
                        /* Allocate the right Noise Levels size according to the
606
                         * product range pixel */
607
0
                        this->m_nfTableNoiseLevels = InterpolateValues(
608
0
                            papszNoiseLevelList, this->m_nTableNoiseLevelsSize,
609
0
                            this->stepSizeNoiseLevels,
610
0
                            this->numberOfValuesNoiseLevels,
611
0
                            this->pixelFirstLutValueNoiseLevels);
612
0
                    }
613
614
0
                    CSLDestroy(papszNoiseLevelList);
615
0
                }
616
617
0
                if (this->m_nfTableNoiseLevels != nullptr)
618
0
                {
619
0
                    break;  // We are done
620
0
                }
621
0
            }
622
0
        }
623
0
    }
624
0
}
625
626
/************************************************************************/
627
/*                         RCMCalibRasterBand()                         */
628
/************************************************************************/
629
630
RCMCalibRasterBand::RCMCalibRasterBand(
631
    RCMDataset *poDataset, const char *pszPolarization, GDALDataType eType,
632
    GDALDataset *poBandDataset, eCalibration eCalib, const char *pszLUT,
633
    const char *pszNoiseLevels, GDALDataType eOriginalType)
634
0
    : m_eCalib(eCalib), m_poBandDataset(poBandDataset),
635
0
      m_eOriginalType(eOriginalType), m_pszLUTFile(VSIStrdup(pszLUT)),
636
0
      m_pszNoiseLevelsFile(VSIStrdup(pszNoiseLevels))
637
0
{
638
0
    this->poDS = poDataset;
639
640
0
    if (pszPolarization != nullptr && strlen(pszPolarization) != 0)
641
0
    {
642
0
        SetMetadataItem("POLARIMETRIC_INTERP", pszPolarization);
643
0
    }
644
645
0
    if ((eType == GDT_CInt16) || (eType == GDT_CFloat32))
646
0
        this->eDataType = GDT_CFloat32;
647
0
    else
648
0
        this->eDataType = GDT_Float32;
649
650
0
    GDALRasterBand *poRasterBand = poBandDataset->GetRasterBand(1);
651
0
    poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
652
653
0
    ReadLUT();
654
0
    ReadNoiseLevels();
655
0
}
656
657
/************************************************************************/
658
/*                        ~RCMCalibRasterBand()                         */
659
/************************************************************************/
660
661
RCMCalibRasterBand::~RCMCalibRasterBand()
662
0
{
663
0
    CPLFree(m_nfTable);
664
0
    CPLFree(m_nfTableNoiseLevels);
665
0
    CPLFree(m_pszLUTFile);
666
0
    CPLFree(m_pszNoiseLevelsFile);
667
0
    GDALClose(m_poBandDataset);
668
0
}
669
670
/************************************************************************/
671
/*                             IReadBlock()                             */
672
/************************************************************************/
673
674
CPLErr RCMCalibRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
675
                                      void *pImage)
676
0
{
677
0
    CPLErr eErr;
678
0
    int nRequestXSize = 0;
679
0
    int nRequestYSize = 0;
680
0
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nRequestXSize, &nRequestYSize);
681
682
    // Zero initial partial right-most and bottom-most blocks
683
0
    if (nRequestXSize < nBlockXSize || nRequestYSize < nBlockYSize)
684
0
    {
685
0
        memset(pImage, 0,
686
0
               static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
687
0
                   nBlockXSize * nBlockYSize);
688
0
    }
689
690
0
    if (this->m_eOriginalType == GDT_CInt16)
691
0
    {
692
        /* read in complex values */
693
0
        GInt16 *panImageTmp = static_cast<GInt16 *>(
694
0
            VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize,
695
0
                                GDALGetDataTypeSizeBytes(m_eOriginalType)));
696
0
        if (!panImageTmp)
697
0
            return CE_Failure;
698
699
0
        if (m_poBandDataset->GetRasterCount() == 2)
700
0
        {
701
0
            eErr = m_poBandDataset->RasterIO(
702
0
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
703
0
                nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize,
704
0
                nRequestYSize, this->m_eOriginalType, 2, nullptr, 4,
705
0
                nBlockXSize * 4, 4, nullptr);
706
707
            /*
708
            eErr = m_poBandDataset->RasterIO(GF_Read,
709
                nBlockXOff * nBlockXSize,
710
                nBlockYOff * nBlockYSize,
711
                nRequestXSize, nRequestYSize,
712
                panImageTmp, nRequestXSize, nRequestYSize,
713
                GDT_Int32,
714
                2, nullptr, 4, nBlockXSize * 4, 2, nullptr);
715
            */
716
0
        }
717
0
        else
718
0
        {
719
0
            eErr = m_poBandDataset->RasterIO(
720
0
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
721
0
                nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize,
722
0
                nRequestYSize, this->m_eOriginalType, 1, nullptr, 4,
723
0
                nBlockXSize * 4, 0, nullptr);
724
725
            /*
726
            eErr =
727
                m_poBandDataset->RasterIO(GF_Read,
728
                    nBlockXOff * nBlockXSize,
729
                    nBlockYOff * nBlockYSize,
730
                    nRequestXSize, nRequestYSize,
731
                    panImageTmp, nRequestXSize, nRequestYSize,
732
                    GDT_UInt32,
733
                    1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
734
            */
735
736
0
#ifdef CPL_LSB
737
            /* First, undo the 32bit swap. */
738
0
            GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
739
740
            /* Then apply 16 bit swap. */
741
0
            GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
742
0
#endif
743
0
        }
744
745
        /* calibrate the complex values */
746
0
        for (int i = 0; i < nRequestYSize; i++)
747
0
        {
748
0
            for (int j = 0; j < nRequestXSize; j++)
749
0
            {
750
                /* calculate pixel offset in memory*/
751
0
                const int nPixOff = 2 * (i * nBlockXSize + j);
752
0
                const int nTruePixOff = (i * nBlockXSize) + j;
753
754
                // Formula for Complex Q+J
755
0
                const float real = static_cast<float>(panImageTmp[nPixOff]);
756
0
                const float img = static_cast<float>(panImageTmp[nPixOff + 1]);
757
0
                const float digitalValue = (real * real) + (img * img);
758
0
                const float lutValue =
759
0
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
760
0
                const float calibValue = digitalValue / (lutValue * lutValue);
761
762
0
                reinterpret_cast<float *>(pImage)[nTruePixOff] = calibValue;
763
0
            }
764
0
        }
765
766
0
        CPLFree(panImageTmp);
767
0
    }
768
769
    // If the underlying file is NITF CFloat32
770
0
    else if (this->m_eOriginalType == GDT_CFloat32 ||
771
0
             this->m_eOriginalType == GDT_CFloat64)
772
0
    {
773
        /* read in complex values */
774
0
        const int dataTypeSize =
775
0
            GDALGetDataTypeSizeBytes(this->m_eOriginalType);
776
0
        const GDALDataType bandFileType = this->m_eOriginalType;
777
0
        const int bandFileDataTypeSize = GDALGetDataTypeSizeBytes(bandFileType);
778
779
        /* read the original image complex values in a temporary image space */
780
0
        float *pafImageTmp = static_cast<float *>(VSI_MALLOC3_VERBOSE(
781
0
            nBlockXSize, nBlockYSize, 2 * bandFileDataTypeSize));
782
0
        if (!pafImageTmp)
783
0
            return CE_Failure;
784
785
0
        eErr =
786
            // I and Q from each band are pixel-interleaved into this complex
787
            // band
788
0
            m_poBandDataset->RasterIO(
789
0
                GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
790
0
                nRequestXSize, nRequestYSize, pafImageTmp, nRequestXSize,
791
0
                nRequestYSize, bandFileType, 2, nullptr, dataTypeSize,
792
0
                static_cast<GSpacing>(dataTypeSize) * nBlockXSize,
793
0
                bandFileDataTypeSize, nullptr);
794
795
        /* calibrate the complex values */
796
0
        for (int i = 0; i < nRequestYSize; i++)
797
0
        {
798
0
            for (int j = 0; j < nRequestXSize; j++)
799
0
            {
800
                /* calculate pixel offset in memory*/
801
0
                const int nPixOff = 2 * (i * nBlockXSize + j);
802
0
                const int nTruePixOff = (i * nBlockXSize) + j;
803
804
                // Formula for Complex Q+J
805
0
                const float real = pafImageTmp[nPixOff];
806
0
                const float img = pafImageTmp[nPixOff + 1];
807
0
                const float digitalValue = (real * real) + (img * img);
808
0
                const float lutValue =
809
0
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
810
0
                const float calibValue = digitalValue / (lutValue * lutValue);
811
812
0
                reinterpret_cast<float *>(pImage)[nTruePixOff] = calibValue;
813
0
            }
814
0
        }
815
816
0
        CPLFree(pafImageTmp);
817
0
    }
818
819
0
    else if (this->m_eOriginalType == GDT_Float32)
820
0
    {
821
        /* A Float32 is actual 4 bytes */
822
0
        eErr = m_poBandDataset->RasterIO(
823
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
824
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
825
0
            GDT_Float32, 1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
826
827
        /* iterate over detected values */
828
0
        for (int i = 0; i < nRequestYSize; i++)
829
0
        {
830
0
            for (int j = 0; j < nRequestXSize; j++)
831
0
            {
832
0
                int nPixOff = (i * nBlockXSize) + j;
833
834
                /* For detected products, in order to convert the digital number
835
                of a given range sample to a calibrated value, the digital value
836
                is first squared, then the offset(B) is added and the result is
837
                divided by the gains value(A) corresponding to the range sample.
838
                RCM-SP-53-0419  Issue 2/5:  January 2, 2018  Page 7-56 */
839
0
                const float digitalValue =
840
0
                    reinterpret_cast<float *>(pImage)[nPixOff];
841
0
                const float A =
842
0
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
843
0
                reinterpret_cast<float *>(pImage)[nPixOff] =
844
0
                    ((digitalValue * digitalValue) +
845
0
                     static_cast<float>(this->m_nfOffset)) /
846
0
                    A;
847
0
            }
848
0
        }
849
0
    }
850
851
0
    else if (this->m_eOriginalType == GDT_Float64)
852
0
    {
853
        /* A Float64 is actual 8 bytes */
854
0
        eErr = m_poBandDataset->RasterIO(
855
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
856
0
            nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
857
0
            GDT_Float64, 1, nullptr, 8, nBlockXSize * 8, 0, nullptr);
858
859
        /* iterate over detected values */
860
0
        for (int i = 0; i < nRequestYSize; i++)
861
0
        {
862
0
            for (int j = 0; j < nRequestXSize; j++)
863
0
            {
864
0
                int nPixOff = (i * nBlockXSize) + j;
865
866
                /* For detected products, in order to convert the digital number
867
                of a given range sample to a calibrated value, the digital value
868
                is first squared, then the offset(B) is added and the result is
869
                divided by the gains value(A) corresponding to the range sample.
870
                RCM-SP-53-0419  Issue 2/5:  January 2, 2018  Page 7-56 */
871
0
                const float digitalValue =
872
0
                    reinterpret_cast<float *>(pImage)[nPixOff];
873
0
                const float A =
874
0
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
875
0
                reinterpret_cast<float *>(pImage)[nPixOff] =
876
0
                    ((digitalValue * digitalValue) +
877
0
                     static_cast<float>(this->m_nfOffset)) /
878
0
                    A;
879
0
            }
880
0
        }
881
0
    }
882
883
0
    else if (this->m_eOriginalType == GDT_UInt16)
884
0
    {
885
        /* read in detected values */
886
0
        GUInt16 *panImageTmp = static_cast<GUInt16 *>(VSI_MALLOC3_VERBOSE(
887
0
            nBlockXSize, nBlockYSize, GDALGetDataTypeSizeBytes(GDT_UInt16)));
888
0
        if (!panImageTmp)
889
0
            return CE_Failure;
890
0
        eErr = m_poBandDataset->RasterIO(
891
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
892
0
            nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize,
893
0
            nRequestYSize, GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0,
894
0
            nullptr);
895
896
        /* iterate over detected values */
897
0
        for (int i = 0; i < nRequestYSize; i++)
898
0
        {
899
0
            for (int j = 0; j < nRequestXSize; j++)
900
0
            {
901
0
                const int nPixOff = (i * nBlockXSize) + j;
902
903
0
                const float digitalValue =
904
0
                    static_cast<float>(panImageTmp[nPixOff]);
905
0
                const float A =
906
0
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
907
0
                reinterpret_cast<float *>(pImage)[nPixOff] =
908
0
                    ((digitalValue * digitalValue) +
909
0
                     static_cast<float>(this->m_nfOffset)) /
910
0
                    A;
911
0
            }
912
0
        }
913
0
        CPLFree(panImageTmp);
914
0
    } /* Ticket #2104: Support for ScanSAR products */
915
916
0
    else if (this->m_eOriginalType == GDT_UInt8)
917
0
    {
918
0
        GByte *pabyImageTmp =
919
0
            static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nBlockXSize, nBlockYSize));
920
0
        if (!pabyImageTmp)
921
0
            return CE_Failure;
922
0
        eErr = m_poBandDataset->RasterIO(
923
0
            GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
924
0
            nRequestXSize, nRequestYSize, pabyImageTmp, nRequestXSize,
925
0
            nRequestYSize, GDT_UInt8, 1, nullptr, 1, nBlockXSize, 0, nullptr);
926
927
        /* iterate over detected values */
928
0
        for (int i = 0; i < nRequestYSize; i++)
929
0
        {
930
0
            for (int j = 0; j < nRequestXSize; j++)
931
0
            {
932
0
                const int nPixOff = (i * nBlockXSize) + j;
933
934
0
                const float digitalValue =
935
0
                    static_cast<float>(pabyImageTmp[nPixOff]);
936
0
                const float A =
937
0
                    static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]);
938
0
                reinterpret_cast<float *>(pImage)[nPixOff] =
939
0
                    ((digitalValue * digitalValue) +
940
0
                     static_cast<float>(this->m_nfOffset)) /
941
0
                    A;
942
0
            }
943
0
        }
944
0
        CPLFree(pabyImageTmp);
945
0
    }
946
0
    else
947
0
    {
948
0
        CPLAssert(FALSE);
949
0
        return CE_Failure;
950
0
    }
951
0
    return eErr;
952
0
}
953
954
/************************************************************************/
955
/* ==================================================================== */
956
/*                              RCMDataset                              */
957
/* ==================================================================== */
958
/************************************************************************/
959
960
/************************************************************************/
961
/*                            ~RCMDataset()                             */
962
/************************************************************************/
963
964
RCMDataset::~RCMDataset()
965
966
0
{
967
0
    RCMDataset::FlushCache(true);
968
969
0
    if (nGCPCount > 0)
970
0
    {
971
0
        GDALDeinitGCPs(nGCPCount, pasGCPList);
972
0
        CPLFree(pasGCPList);
973
0
    }
974
975
0
    RCMDataset::CloseDependentDatasets();
976
977
0
    if (papszSubDatasets != nullptr)
978
0
        CSLDestroy(papszSubDatasets);
979
980
0
    if (papszExtraFiles != nullptr)
981
0
        CSLDestroy(papszExtraFiles);
982
983
0
    if (m_nfIncidenceAngleTable != nullptr)
984
0
        CPLFree(m_nfIncidenceAngleTable);
985
0
}
986
987
/************************************************************************/
988
/*                       CloseDependentDatasets()                       */
989
/************************************************************************/
990
991
int RCMDataset::CloseDependentDatasets()
992
0
{
993
0
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
994
995
0
    if (nBands != 0)
996
0
        bHasDroppedRef = TRUE;
997
998
0
    for (int iBand = 0; iBand < nBands; iBand++)
999
0
    {
1000
0
        delete papoBands[iBand];
1001
0
    }
1002
0
    nBands = 0;
1003
1004
0
    return bHasDroppedRef;
1005
0
}
1006
1007
/************************************************************************/
1008
/*                            GetFileList()                             */
1009
/************************************************************************/
1010
1011
char **RCMDataset::GetFileList()
1012
1013
0
{
1014
0
    char **papszFileList = GDALPamDataset::GetFileList();
1015
1016
0
    papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
1017
1018
0
    return papszFileList;
1019
0
}
1020
1021
/************************************************************************/
1022
/*                                Open()                                */
1023
/************************************************************************/
1024
1025
GDALDataset *RCMDataset::Open(GDALOpenInfo *poOpenInfo)
1026
1027
0
{
1028
    /* -------------------------------------------------------------------- */
1029
    /*      Is this a RCM Product.xml definition?                           */
1030
    /* -------------------------------------------------------------------- */
1031
0
    if (!RCMDatasetIdentify(poOpenInfo))
1032
0
    {
1033
0
        return nullptr;
1034
0
    }
1035
1036
    /* -------------------------------------------------------------------- */
1037
    /*        Get subdataset information, if relevant                       */
1038
    /* -------------------------------------------------------------------- */
1039
0
    const char *pszFilename = poOpenInfo->pszFilename;
1040
0
    eCalibration eCalib = None;
1041
1042
0
    if (STARTS_WITH_CI(pszFilename, szLayerCalibration) &&
1043
0
        pszFilename[strlen(szLayerCalibration)] == chLayerSeparator)
1044
0
    {
1045
        // The calibration name and filename begins after the hard coded layer
1046
        // name
1047
0
        pszFilename += strlen(szLayerCalibration) + 1;
1048
1049
0
        if (STARTS_WITH_CI(pszFilename, szBETA0))
1050
0
        {
1051
0
            eCalib = Beta0;
1052
0
        }
1053
0
        else if (STARTS_WITH_CI(pszFilename, szSIGMA0))
1054
0
        {
1055
0
            eCalib = Sigma0;
1056
0
        }
1057
0
        else if (STARTS_WITH_CI(pszFilename, szGAMMA) ||
1058
0
                 STARTS_WITH_CI(pszFilename, "GAMMA0"))  // Cover both situation
1059
0
        {
1060
0
            eCalib = Gamma;
1061
0
        }
1062
0
        else if (STARTS_WITH_CI(pszFilename, szUNCALIB))
1063
0
        {
1064
0
            eCalib = Uncalib;
1065
0
        }
1066
0
        else
1067
0
        {
1068
0
            CPLError(CE_Failure, CPLE_NotSupported,
1069
0
                     "Unsupported calibration type");
1070
0
            return nullptr;
1071
0
        }
1072
1073
        /* advance the pointer to the actual filename */
1074
0
        while (*pszFilename != '\0' && *pszFilename != ':')
1075
0
            pszFilename++;
1076
1077
0
        if (*pszFilename == ':')
1078
0
            pszFilename++;
1079
1080
        // need to redo the directory check:
1081
        // the GDALOpenInfo check would have failed because of the calibration
1082
        // string on the filename
1083
0
        VSIStatBufL sStat;
1084
0
        if (VSIStatL(pszFilename, &sStat) == 0)
1085
0
            poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode);
1086
0
    }
1087
1088
0
    CPLString osMDFilename;
1089
0
    if (poOpenInfo->bIsDirectory)
1090
0
    {
1091
        /* Check for directory access when there is a product.xml file in the
1092
        directory. */
1093
0
        osMDFilename =
1094
0
            CPLFormCIFilenameSafe(pszFilename, "product.xml", nullptr);
1095
1096
0
        VSIStatBufL sStat;
1097
0
        if (VSIStatL(osMDFilename, &sStat) != 0)
1098
0
        {
1099
            /* If not, check for directory extra 'metadata' access when there is
1100
            a product.xml file in the directory. */
1101
0
            osMDFilename = CPLFormCIFilenameSafe(pszFilename,
1102
0
                                                 GetMetadataProduct(), nullptr);
1103
0
        }
1104
0
    }
1105
0
    else
1106
0
        osMDFilename = pszFilename;
1107
1108
    /* -------------------------------------------------------------------- */
1109
    /*      Ingest the Product.xml file.                                    */
1110
    /* -------------------------------------------------------------------- */
1111
0
    CPLXMLTreeCloser psProduct(CPLParseXMLFile(osMDFilename));
1112
0
    if (!psProduct)
1113
0
        return nullptr;
1114
1115
    /* -------------------------------------------------------------------- */
1116
    /*      Confirm the requested access is supported.                      */
1117
    /* -------------------------------------------------------------------- */
1118
0
    if (poOpenInfo->eAccess == GA_Update)
1119
0
    {
1120
0
        ReportUpdateNotSupportedByDriver("RCM");
1121
0
        return nullptr;
1122
0
    }
1123
1124
0
    const CPLXMLNode *psSceneAttributes =
1125
0
        CPLGetXMLNode(psProduct.get(), "=product.sceneAttributes");
1126
0
    if (psSceneAttributes == nullptr)
1127
0
    {
1128
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1129
0
                 "ERROR: Failed to find <sceneAttributes> in document.");
1130
0
        return nullptr;
1131
0
    }
1132
1133
0
    const CPLXMLNode *psImageAttributes = CPLGetXMLNode(
1134
0
        psProduct.get(), "=product.sceneAttributes.imageAttributes");
1135
0
    if (psImageAttributes == nullptr)
1136
0
    {
1137
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1138
0
                 "ERROR: Failed to find <sceneAttributes.imageAttributes> in "
1139
0
                 "document.");
1140
0
        return nullptr;
1141
0
    }
1142
1143
0
    int numberOfEntries =
1144
0
        atoi(CPLGetXMLValue(psSceneAttributes, "numberOfEntries", "0"));
1145
0
    if (numberOfEntries != 1)
1146
0
    {
1147
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1148
0
                 "ERROR: Only RCM with Complex Single-beam is supported.");
1149
0
        return nullptr;
1150
0
    }
1151
1152
0
    const CPLXMLNode *psImageReferenceAttributes =
1153
0
        CPLGetXMLNode(psProduct.get(), "=product.imageReferenceAttributes");
1154
0
    if (psImageReferenceAttributes == nullptr)
1155
0
    {
1156
0
        CPLError(
1157
0
            CE_Failure, CPLE_OpenFailed,
1158
0
            "ERROR: Failed to find <imageReferenceAttributes> in document.");
1159
0
        return nullptr;
1160
0
    }
1161
1162
0
    const CPLXMLNode *psImageGenerationParameters =
1163
0
        CPLGetXMLNode(psProduct.get(), "=product.imageGenerationParameters");
1164
0
    if (psImageGenerationParameters == nullptr)
1165
0
    {
1166
0
        CPLError(
1167
0
            CE_Failure, CPLE_OpenFailed,
1168
0
            "ERROR: Failed to find <imageGenerationParameters> in document.");
1169
0
        return nullptr;
1170
0
    }
1171
1172
    /* -------------------------------------------------------------------- */
1173
    /*      Create the dataset.                                             */
1174
    /* -------------------------------------------------------------------- */
1175
0
    auto poDS = std::make_unique<RCMDataset>();
1176
1177
0
    poDS->psProduct = std::move(psProduct);
1178
1179
    /* -------------------------------------------------------------------- */
1180
    /*      Get overall image information.                                  */
1181
    /* -------------------------------------------------------------------- */
1182
0
    poDS->nRasterXSize = atoi(CPLGetXMLValue(
1183
0
        psSceneAttributes, "imageAttributes.samplesPerLine", "-1"));
1184
0
    poDS->nRasterYSize = atoi(
1185
0
        CPLGetXMLValue(psSceneAttributes, "imageAttributes.numLines", "-1"));
1186
0
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
1187
0
    {
1188
0
        return nullptr;
1189
0
    }
1190
1191
    /* -------------------------------------------------------------------- */
1192
    /*      Check product type, as to determine if there are LUTs for       */
1193
    /*      calibration purposes.                                           */
1194
    /* -------------------------------------------------------------------- */
1195
1196
0
    const char *pszItem =
1197
0
        CPLGetXMLValue(psImageGenerationParameters,
1198
0
                       "generalProcessingInformation.productType", "UNK");
1199
0
    poDS->SetMetadataItem("PRODUCT_TYPE", pszItem);
1200
0
    const char *pszProductType = pszItem;
1201
1202
0
    pszItem =
1203
0
        CPLGetXMLValue(poDS->psProduct.get(), "=product.productId", "UNK");
1204
0
    poDS->SetMetadataItem("PRODUCT_ID", pszItem);
1205
1206
0
    pszItem = CPLGetXMLValue(
1207
0
        poDS->psProduct.get(),
1208
0
        "=product.securityAttributes.securityClassification", "UNK");
1209
0
    poDS->SetMetadataItem("SECURITY_CLASSIFICATION", pszItem);
1210
1211
0
    pszItem =
1212
0
        CPLGetXMLValue(poDS->psProduct.get(),
1213
0
                       "=product.sourceAttributes.polarizationDataMode", "UNK");
1214
0
    poDS->SetMetadataItem("POLARIZATION_DATA_MODE", pszItem);
1215
1216
0
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
1217
0
                             "generalProcessingInformation.processingFacility",
1218
0
                             "UNK");
1219
0
    poDS->SetMetadataItem("PROCESSING_FACILITY", pszItem);
1220
1221
0
    pszItem =
1222
0
        CPLGetXMLValue(psImageGenerationParameters,
1223
0
                       "generalProcessingInformation.processingTime", "UNK");
1224
0
    poDS->SetMetadataItem("PROCESSING_TIME", pszItem);
1225
1226
0
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
1227
0
                             "sarProcessingInformation.satelliteHeight", "UNK");
1228
0
    poDS->SetMetadataItem("SATELLITE_HEIGHT", pszItem);
1229
1230
0
    pszItem = CPLGetXMLValue(
1231
0
        psImageGenerationParameters,
1232
0
        "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK");
1233
0
    poDS->SetMetadataItem("FIRST_LINE_TIME", pszItem);
1234
1235
0
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
1236
0
                             "sarProcessingInformation.zeroDopplerTimeLastLine",
1237
0
                             "UNK");
1238
0
    poDS->SetMetadataItem("LAST_LINE_TIME", pszItem);
1239
1240
0
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
1241
0
                             "sarProcessingInformation.lutApplied", "");
1242
0
    poDS->SetMetadataItem("LUT_APPLIED", pszItem);
1243
1244
    /*---------------------------------------------------------------------
1245
           If true, a polarization dependent application LUT has been applied
1246
           for each polarization channel. Otherwise the same application LUT
1247
           has been applied for all polarization channels. Not applicable to
1248
           lookupTable = "Unity*" or if dataType = "Floating-Point".
1249
      --------------------------------------------------------------------- */
1250
0
    pszItem = CPLGetXMLValue(psImageGenerationParameters,
1251
0
                             "sarProcessingInformation.perPolarizationScaling",
1252
0
                             "false");
1253
0
    poDS->SetMetadataItem("PER_POLARIZATION_SCALING", pszItem);
1254
0
    if (EQUAL(pszItem, "true") || EQUAL(pszItem, "TRUE"))
1255
0
    {
1256
0
        poDS->bPerPolarizationScaling = TRUE;
1257
0
    }
1258
1259
    /* the following cases can be assumed to have no LUTs, as per
1260
     * RN-RP-51-2713, but also common sense
1261
     * SLC represents a SLant range georeferenced Complex product
1262
     * (i.e., equivalent to a Single-Look Complex product for RADARSAT-1 or
1263
     * RADARSAT-2). • GRD or GRC represent GRound range georeferenced Detected
1264
     * or Complex products (GRD is equivalent to an SGX, SCN or SCW product for
1265
     * RADARSAT1 or RADARSAT-2). • GCD or GCC represent GeoCoded Detected or
1266
     * Complex products (GCD is equivalent to an SSG or SPG product for
1267
     * RADARSAT-1 or RADARSAT-2).
1268
     */
1269
0
    bool bCanCalib = false;
1270
0
    if (!(STARTS_WITH_CI(pszProductType, "UNK") ||
1271
0
          STARTS_WITH_CI(pszProductType, "GCD") ||
1272
0
          STARTS_WITH_CI(pszProductType, "GCC")))
1273
0
    {
1274
0
        bCanCalib = true;
1275
0
    }
1276
1277
    /* -------------------------------------------------------------------- */
1278
    /*      Get dataType (so we can recognise complex data), and the        */
1279
    /*      bitsPerSample.                                                  */
1280
    /* -------------------------------------------------------------------- */
1281
0
    const char *pszSampleDataType = CPLGetXMLValue(
1282
0
        psImageReferenceAttributes, "rasterAttributes.sampleType", "");
1283
0
    poDS->SetMetadataItem("SAMPLE_TYPE", pszSampleDataType);
1284
1285
    /* Either Integer (16 bits) or Floating-Point (32 bits) */
1286
0
    const char *pszDataType = CPLGetXMLValue(psImageReferenceAttributes,
1287
0
                                             "rasterAttributes.dataType", "");
1288
0
    poDS->SetMetadataItem("DATA_TYPE", pszDataType);
1289
1290
    /* 2 entries for complex data 1 entry for magnitude detected data */
1291
0
    const char *pszBitsPerSample = CPLGetXMLValue(
1292
0
        psImageReferenceAttributes, "rasterAttributes.bitsPerSample", "");
1293
0
    const int nBitsPerSample = atoi(pszBitsPerSample);
1294
0
    poDS->SetMetadataItem("BITS_PER_SAMPLE", pszBitsPerSample);
1295
1296
0
    const char *pszSampledPixelSpacingTime =
1297
0
        CPLGetXMLValue(psImageReferenceAttributes,
1298
0
                       "rasterAttributes.sampledPixelSpacingTime", "UNK");
1299
0
    poDS->SetMetadataItem("SAMPLED_PIXEL_SPACING_TIME",
1300
0
                          pszSampledPixelSpacingTime);
1301
1302
0
    const char *pszSampledLineSpacingTime =
1303
0
        CPLGetXMLValue(psImageReferenceAttributes,
1304
0
                       "rasterAttributes.sampledLineSpacingTime", "UNK");
1305
0
    poDS->SetMetadataItem("SAMPLED_LINE_SPACING_TIME",
1306
0
                          pszSampledLineSpacingTime);
1307
1308
0
    GDALDataType eDataType;
1309
1310
0
    if (EQUAL(pszSampleDataType, "Mixed")) /* RCM MLC has Mixed sampleType */
1311
0
    {
1312
0
        poDS->isComplexData = false; /* RCM MLC is detected, non-complex */
1313
0
        if (nBitsPerSample == 32)
1314
0
        {
1315
0
            eDataType = GDT_Float32; /* 32 bits, check read block */
1316
0
            poDS->magnitudeBits = 32;
1317
0
        }
1318
0
        else
1319
0
        {
1320
0
            eDataType = GDT_UInt16; /* 16 bits, check read block */
1321
0
            poDS->magnitudeBits = 16;
1322
0
        }
1323
0
    }
1324
0
    else if (EQUAL(pszSampleDataType, "Complex"))
1325
0
    {
1326
0
        poDS->isComplexData = true;
1327
        /* Usually this is the same bits for both */
1328
0
        poDS->realBitsComplexData = nBitsPerSample;
1329
0
        poDS->imaginaryBitsComplexData = nBitsPerSample;
1330
1331
0
        if (nBitsPerSample == 32)
1332
0
        {
1333
0
            eDataType = GDT_CFloat32; /* 32 bits, check read block */
1334
0
        }
1335
0
        else
1336
0
        {
1337
0
            eDataType = GDT_CInt16; /* 16 bits, check read block */
1338
0
        }
1339
0
    }
1340
0
    else if (nBitsPerSample == 32 &&
1341
0
             EQUAL(pszSampleDataType, "Magnitude Detected"))
1342
0
    {
1343
        /* Actually, I don't need to test the 'dataType'=' Floating-Point', we
1344
         * know that a 32 bits per sample */
1345
0
        eDataType = GDT_Float32;
1346
0
        poDS->isComplexData = false;
1347
0
        poDS->magnitudeBits = 32;
1348
0
    }
1349
0
    else if (nBitsPerSample == 16 &&
1350
0
             EQUAL(pszSampleDataType, "Magnitude Detected"))
1351
0
    {
1352
        /* Actually, I don't need to test the 'dataType'=' Integer', we know
1353
         * that a 16 bits per sample */
1354
0
        eDataType = GDT_UInt16;
1355
0
        poDS->isComplexData = false;
1356
0
        poDS->magnitudeBits = 16;
1357
0
    }
1358
0
    else
1359
0
    {
1360
0
        CPLError(CE_Failure, CPLE_AppDefined,
1361
0
                 "ERROR: dataType=%s and bitsPerSample=%d are not a supported "
1362
0
                 "configuration.",
1363
0
                 pszDataType, nBitsPerSample);
1364
0
        return nullptr;
1365
0
    }
1366
1367
    /* Indicates whether pixel number (i.e., range) increases or decreases with
1368
    range time. For GCD and GCC products, this applies to intermediate ground
1369
    range image data prior to geocoding. */
1370
0
    const char *pszPixelTimeOrdering =
1371
0
        CPLGetXMLValue(psImageReferenceAttributes,
1372
0
                       "rasterAttributes.pixelTimeOrdering", "UNK");
1373
0
    poDS->SetMetadataItem("PIXEL_TIME_ORDERING", pszPixelTimeOrdering);
1374
1375
    /* Indicates whether line numbers (i.e., azimuth) increase or decrease with
1376
    azimuth time. For GCD and GCC products, this applies to intermediate ground
1377
    range image data prior to geocoding. */
1378
0
    const char *pszLineTimeOrdering = CPLGetXMLValue(
1379
0
        psImageReferenceAttributes, "rasterAttributes.lineTimeOrdering", "UNK");
1380
0
    poDS->SetMetadataItem("LINE_TIME_ORDERING", pszLineTimeOrdering);
1381
1382
    /* while we're at it, extract the pixel spacing information */
1383
0
    const char *pszPixelSpacing =
1384
0
        CPLGetXMLValue(psImageReferenceAttributes,
1385
0
                       "rasterAttributes.sampledPixelSpacing", "UNK");
1386
0
    poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing);
1387
1388
0
    const char *pszLineSpacing =
1389
0
        CPLGetXMLValue(psImageReferenceAttributes,
1390
0
                       "rasterAttributes.sampledLineSpacing", "UNK");
1391
0
    poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing);
1392
1393
    /* -------------------------------------------------------------------- */
1394
    /*      Open each of the data files as a complex band.                  */
1395
    /* -------------------------------------------------------------------- */
1396
0
    std::string osBeta0LUT;
1397
0
    std::string osGammaLUT;
1398
0
    std::string osSigma0LUT;
1399
    // Same file for all calibrations except the calibration is plit inside the
1400
    // XML
1401
0
    std::string osNoiseLevelsValues;
1402
1403
0
    const CPLString osPath = CPLGetPathSafe(osMDFilename);
1404
1405
    /* Get a list of all polarizations */
1406
0
    const CPLXMLNode *psSourceAttrs =
1407
0
        CPLGetXMLNode(poDS->psProduct.get(), "=product.sourceAttributes");
1408
0
    if (psSourceAttrs == nullptr)
1409
0
    {
1410
0
        CPLError(
1411
0
            CE_Failure, CPLE_OpenFailed,
1412
0
            "ERROR: RCM source attributes is missing. Please contact your data "
1413
0
            "provider for a corrected dataset.");
1414
0
        return nullptr;
1415
0
    }
1416
1417
0
    const CPLXMLNode *psRadarParameters = CPLGetXMLNode(
1418
0
        poDS->psProduct.get(), "=product.sourceAttributes.radarParameters");
1419
0
    if (psRadarParameters == nullptr)
1420
0
    {
1421
0
        CPLError(
1422
0
            CE_Failure, CPLE_OpenFailed,
1423
0
            "ERROR: RCM radar parameters is missing. Please contact your data "
1424
0
            "provider for a corrected dataset.");
1425
0
        return nullptr;
1426
0
    }
1427
1428
0
    const char *pszPolarizations =
1429
0
        CPLGetXMLValue(psRadarParameters, "polarizations", "");
1430
0
    if (pszPolarizations == nullptr || strlen(pszPolarizations) == 0)
1431
0
    {
1432
0
        CPLError(
1433
0
            CE_Failure, CPLE_OpenFailed,
1434
0
            "ERROR: RCM polarizations list is missing. Please contact your "
1435
0
            "data provider for a corrected dataset.");
1436
0
        return nullptr;
1437
0
    }
1438
0
    poDS->SetMetadataItem("POLARIZATIONS", pszPolarizations);
1439
1440
0
    const char *psAcquisitionType =
1441
0
        CPLGetXMLValue(psRadarParameters, "acquisitionType", "UNK");
1442
0
    poDS->SetMetadataItem("ACQUISITION_TYPE", psAcquisitionType);
1443
1444
0
    const char *psBeams = CPLGetXMLValue(psRadarParameters, "beams", "UNK");
1445
0
    poDS->SetMetadataItem("BEAMS", psBeams);
1446
1447
0
    const CPLStringList aosPolarizationsGrids(
1448
0
        CSLTokenizeString2(pszPolarizations, " ", 0));
1449
0
    CPLStringList imageBandList;
1450
0
    CPLStringList imageBandFileList;
1451
0
    const int nPolarizationsGridCount = aosPolarizationsGrids.size();
1452
1453
    /* File names for full resolution IPDFs. For GeoTIFF format, one entry per
1454
    pole; For NITF 2.1 format, only one entry. */
1455
0
    bool bIsNITF = false;
1456
0
    const char *pszNITF_Filename = nullptr;
1457
0
    int imageBandFileCount = 0;
1458
0
    int imageBandCount = 0;
1459
1460
    /* Split the polarization string*/
1461
0
    auto iss = std::istringstream((CPLString(pszPolarizations)).c_str());
1462
0
    auto pol = std::string{};
1463
    /* Count number of polarizations*/
1464
0
    while (iss >> pol)
1465
0
        imageBandCount++;
1466
1467
0
    for (const CPLXMLNode *psNode = psImageAttributes->psChild;
1468
0
         psNode != nullptr; psNode = psNode->psNext)
1469
0
    {
1470
        /* Find the tif or ntf filename */
1471
0
        if (psNode->eType != CXT_Element || !(EQUAL(psNode->pszValue, "ipdf")))
1472
0
            continue;
1473
1474
        /* --------------------------------------------------------------------
1475
         */
1476
        /*      Fetch ipdf image. Could be either tif or ntf. */
1477
        /*      Replace / by \\ */
1478
        /* --------------------------------------------------------------------
1479
         */
1480
0
        const char *pszBasedFilename = CPLGetXMLValue(psNode, "", "");
1481
0
        if (*pszBasedFilename == '\0')
1482
0
            continue;
1483
1484
        /* Count number of image names within ipdf tag*/
1485
0
        imageBandFileCount++;
1486
1487
0
        CPLString pszUpperBasedFilename(CPLString(pszBasedFilename).toupper());
1488
1489
0
        const bool bEndsWithNTF =
1490
0
            strlen(pszUpperBasedFilename.c_str()) > 4 &&
1491
0
            EQUAL(pszUpperBasedFilename.c_str() +
1492
0
                      strlen(pszUpperBasedFilename.c_str()) - 4,
1493
0
                  ".NTF");
1494
1495
0
        if (bEndsWithNTF)
1496
0
        {
1497
            /* Found it! It would not exist one more */
1498
0
            bIsNITF = true;
1499
0
            pszNITF_Filename = pszBasedFilename;
1500
0
            break;
1501
0
        }
1502
0
        else
1503
0
        {
1504
            /* Keep adding polarizations filename according to the pole */
1505
0
            const char *pszPole = CPLGetXMLValue(psNode, "pole", "");
1506
0
            if (*pszPole == '\0')
1507
0
            {
1508
                /* Guard against case when pole is a null string, skip it */
1509
0
                imageBandFileCount--;
1510
0
                continue;
1511
0
            }
1512
1513
0
            if (EQUAL(pszPole, "XC"))
1514
0
            {
1515
                /* Skip RCM MLC's 3rd band file ##XC.tif */
1516
0
                imageBandFileCount--;
1517
0
                continue;
1518
0
            }
1519
1520
0
            imageBandList.AddString(CPLString(pszPole).toupper());
1521
0
            imageBandFileList.AddString(pszBasedFilename);
1522
0
        }
1523
0
    }
1524
1525
    /* -------------------------------------------------------------------- */
1526
    /*      Incidence Angle in a sub-folder                                 */
1527
    /*      called 'calibration' from the 'metadata' folder                 */
1528
    /* -------------------------------------------------------------------- */
1529
1530
0
    const char *pszIncidenceAngleFileName = CPLGetXMLValue(
1531
0
        psImageReferenceAttributes, "incidenceAngleFileName", "");
1532
1533
0
    if (pszIncidenceAngleFileName != nullptr)
1534
0
    {
1535
0
        if (CPLHasPathTraversal(pszIncidenceAngleFileName))
1536
0
        {
1537
0
            CPLError(CE_Failure, CPLE_NotSupported,
1538
0
                     "Path traversal detected in %s",
1539
0
                     pszIncidenceAngleFileName);
1540
0
            return nullptr;
1541
0
        }
1542
0
        const CPLString osIncidenceAngleFilePath = CPLFormFilenameSafe(
1543
0
            CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr).c_str(),
1544
0
            pszIncidenceAngleFileName, nullptr);
1545
        /* Check if the file exist */
1546
0
        if (IsValidXMLFile(osIncidenceAngleFilePath))
1547
0
        {
1548
0
            CPLXMLTreeCloser psIncidenceAngle(
1549
0
                CPLParseXMLFile(osIncidenceAngleFilePath));
1550
1551
0
            int pixelFirstLutValue = atoi(
1552
0
                CPLGetXMLValue(psIncidenceAngle.get(),
1553
0
                               "=incidenceAngles.pixelFirstAnglesValue", "0"));
1554
1555
0
            const int stepSize = atoi(CPLGetXMLValue(
1556
0
                psIncidenceAngle.get(), "=incidenceAngles.stepSize", "0"));
1557
0
            const int numberOfValues =
1558
0
                atoi(CPLGetXMLValue(psIncidenceAngle.get(),
1559
0
                                    "=incidenceAngles.numberOfValues", "0"));
1560
1561
0
            if (!(stepSize == 0 || stepSize == INT_MIN ||
1562
0
                  numberOfValues == INT_MIN ||
1563
0
                  abs(numberOfValues) > INT_MAX / abs(stepSize)))
1564
0
            {
1565
                /* Get the Pixel Per range */
1566
0
                const int tableSize = abs(stepSize) * abs(numberOfValues);
1567
1568
0
                CPLString angles;
1569
                // Loop through all nodes with spaces
1570
0
                CPLXMLNode *psNextNode =
1571
0
                    CPLGetXMLNode(psIncidenceAngle.get(), "=incidenceAngles");
1572
1573
0
                CPLXMLNode *psNodeInc;
1574
0
                for (psNodeInc = psNextNode->psChild; psNodeInc != nullptr;
1575
0
                     psNodeInc = psNodeInc->psNext)
1576
0
                {
1577
0
                    if (EQUAL(psNodeInc->pszValue, "angles"))
1578
0
                    {
1579
0
                        if (angles.length() > 0)
1580
0
                        {
1581
0
                            angles.append(" "); /* separator */
1582
0
                        }
1583
0
                        const char *valAngle =
1584
0
                            CPLGetXMLValue(psNodeInc, "", "");
1585
0
                        angles.append(valAngle);
1586
0
                    }
1587
0
                }
1588
1589
0
                char **papszAngleList =
1590
0
                    CSLTokenizeString2(angles, " ", CSLT_HONOURSTRINGS);
1591
1592
                /* Allocate the right LUT size according to the product range pixel
1593
                 */
1594
0
                poDS->m_IncidenceAngleTableSize = tableSize;
1595
0
                poDS->m_nfIncidenceAngleTable =
1596
0
                    InterpolateValues(papszAngleList, tableSize, stepSize,
1597
0
                                      numberOfValues, pixelFirstLutValue);
1598
1599
0
                CSLDestroy(papszAngleList);
1600
0
            }
1601
0
        }
1602
0
    }
1603
1604
0
    for (int iPoleInx = 0; iPoleInx < nPolarizationsGridCount; iPoleInx++)
1605
0
    {
1606
        // Search for a specific band name
1607
0
        const CPLString pszPole =
1608
0
            CPLString(aosPolarizationsGrids[iPoleInx]).toupper();
1609
1610
        // Look if the NoiseLevel file xml exist for the
1611
0
        const CPLXMLNode *psRefNode = psImageReferenceAttributes->psChild;
1612
0
        for (; psRefNode != nullptr; psRefNode = psRefNode->psNext)
1613
0
        {
1614
0
            if (EQUAL(psRefNode->pszValue, "noiseLevelFileName") && bCanCalib)
1615
0
            {
1616
                /* Determine which incidence angle correction this is */
1617
0
                const char *pszPoleToMatch =
1618
0
                    CPLGetXMLValue(psRefNode, "pole", "");
1619
0
                const char *pszNoiseLevelFile =
1620
0
                    CPLGetXMLValue(psRefNode, "", "");
1621
1622
0
                if (*pszPoleToMatch == '\0')
1623
0
                    continue;
1624
1625
0
                if (EQUAL(pszPoleToMatch, "XC"))
1626
                    /* Skip noise for RCM MLC's 3rd band XC */
1627
0
                    continue;
1628
1629
0
                if (EQUAL(pszNoiseLevelFile, ""))
1630
0
                    continue;
1631
1632
                /* --------------------------------------------------------------------
1633
                 */
1634
                /*      With RCM, LUT file is different per polarizarion (pole)
1635
                 */
1636
                /*      The following code make sure to loop through all
1637
                 * possible       */
1638
                /*      'noiseLevelFileName' and match the <ipdf 'pole' name */
1639
                /* --------------------------------------------------------------------
1640
                 */
1641
0
                if (pszPole.compare(pszPoleToMatch) != 0)
1642
0
                {
1643
0
                    continue;
1644
0
                }
1645
1646
                /* --------------------------------------------------------------------
1647
                 */
1648
                /*      LUT calibration is unique per pole in a sub-folder */
1649
                /*      called 'calibration' from the 'metadata' folder */
1650
                /* --------------------------------------------------------------------
1651
                 */
1652
1653
0
                if (CPLHasPathTraversal(pszNoiseLevelFile))
1654
0
                {
1655
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1656
0
                             "Path traversal detected in %s",
1657
0
                             pszNoiseLevelFile);
1658
0
                    return nullptr;
1659
0
                }
1660
0
                CPLString oNoiseLevelPath = CPLFormFilenameSafe(
1661
0
                    CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr)
1662
0
                        .c_str(),
1663
0
                    pszNoiseLevelFile, nullptr);
1664
0
                if (IsValidXMLFile(oNoiseLevelPath))
1665
0
                {
1666
0
                    osNoiseLevelsValues = std::move(oNoiseLevelPath);
1667
0
                }
1668
0
            }
1669
0
        }
1670
1671
        // Search again with different file
1672
0
        psRefNode = psImageReferenceAttributes->psChild;
1673
0
        for (; psRefNode != nullptr; psRefNode = psRefNode->psNext)
1674
0
        {
1675
0
            if (EQUAL(psRefNode->pszValue, "lookupTableFileName") && bCanCalib)
1676
0
            {
1677
                /* Determine which incidence angle correction this is */
1678
0
                const char *pszLUTType =
1679
0
                    CPLGetXMLValue(psRefNode, "sarCalibrationType", "");
1680
0
                const char *pszPoleToMatch =
1681
0
                    CPLGetXMLValue(psRefNode, "pole", "");
1682
0
                const char *pszLUTFile = CPLGetXMLValue(psRefNode, "", "");
1683
1684
0
                if (*pszPoleToMatch == '\0')
1685
0
                    continue;
1686
1687
0
                if (EQUAL(pszPoleToMatch, "XC"))
1688
                    /* Skip Calib for RCM MLC's 3rd band XC */
1689
0
                    continue;
1690
1691
0
                if (*pszLUTType == '\0')
1692
0
                    continue;
1693
1694
0
                if (EQUAL(pszLUTType, ""))
1695
0
                    continue;
1696
1697
                /* --------------------------------------------------------------------
1698
                 */
1699
                /*      With RCM, LUT file is different per polarizarion (pole)
1700
                 */
1701
                /*      The following code make sure to loop through all
1702
                 * possible       */
1703
                /*      'lookupTableFileName' and match the <ipdf 'pole' name */
1704
                /* --------------------------------------------------------------------
1705
                 */
1706
0
                if (pszPole.compare(pszPoleToMatch) != 0)
1707
0
                {
1708
0
                    continue;
1709
0
                }
1710
1711
                /* --------------------------------------------------------------------
1712
                 */
1713
                /*      LUT calibration is unique per pole in a sub-folder */
1714
                /*      called 'calibration' from the 'metadata' folder */
1715
                /* --------------------------------------------------------------------
1716
                 */
1717
0
                if (CPLHasPathTraversal(pszLUTFile))
1718
0
                {
1719
0
                    CPLError(CE_Failure, CPLE_NotSupported,
1720
0
                             "Path traversal detected in %s", pszLUTFile);
1721
0
                    return nullptr;
1722
0
                }
1723
0
                const CPLString osLUTFilePath = CPLFormFilenameSafe(
1724
0
                    CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr)
1725
0
                        .c_str(),
1726
0
                    pszLUTFile, nullptr);
1727
1728
0
                if (EQUAL(pszLUTType, "Beta Nought") &&
1729
0
                    IsValidXMLFile(osLUTFilePath))
1730
0
                {
1731
0
                    poDS->papszExtraFiles =
1732
0
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
1733
1734
0
                    CPLString pszBuf(
1735
0
                        FormatCalibration(szBETA0, osMDFilename.c_str()));
1736
0
                    osBeta0LUT = osLUTFilePath;
1737
1738
0
                    const char *oldLut =
1739
0
                        poDS->GetMetadataItem("BETA_NOUGHT_LUT");
1740
0
                    if (oldLut == nullptr)
1741
0
                    {
1742
0
                        poDS->SetMetadataItem("BETA_NOUGHT_LUT", osLUTFilePath);
1743
0
                    }
1744
0
                    else
1745
0
                    {
1746
                        /* Keep adding LUT file for all bands, should be planty
1747
                         * of space */
1748
0
                        char *ptrConcatLut =
1749
0
                            static_cast<char *>(CPLMalloc(2048));
1750
0
                        ptrConcatLut[0] =
1751
0
                            '\0'; /* Just initialize the first byte */
1752
0
                        strcat(ptrConcatLut, oldLut);
1753
0
                        strcat(ptrConcatLut, ",");
1754
0
                        strcat(ptrConcatLut, osLUTFilePath);
1755
0
                        poDS->SetMetadataItem("BETA_NOUGHT_LUT", ptrConcatLut);
1756
0
                        CPLFree(ptrConcatLut);
1757
0
                    }
1758
1759
0
                    poDS->papszSubDatasets = CSLSetNameValue(
1760
0
                        poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf);
1761
0
                    poDS->papszSubDatasets = CSLSetNameValue(
1762
0
                        poDS->papszSubDatasets, "SUBDATASET_3_DESC",
1763
0
                        "Beta Nought calibrated");
1764
0
                }
1765
0
                else if (EQUAL(pszLUTType, "Sigma Nought") &&
1766
0
                         IsValidXMLFile(osLUTFilePath))
1767
0
                {
1768
0
                    poDS->papszExtraFiles =
1769
0
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
1770
1771
0
                    CPLString pszBuf(
1772
0
                        FormatCalibration(szSIGMA0, osMDFilename.c_str()));
1773
0
                    osSigma0LUT = osLUTFilePath;
1774
1775
0
                    const char *oldLut =
1776
0
                        poDS->GetMetadataItem("SIGMA_NOUGHT_LUT");
1777
0
                    if (oldLut == nullptr)
1778
0
                    {
1779
0
                        poDS->SetMetadataItem("SIGMA_NOUGHT_LUT",
1780
0
                                              osLUTFilePath);
1781
0
                    }
1782
0
                    else
1783
0
                    {
1784
                        /* Keep adding LUT file for all bands, should be planty
1785
                         * of space */
1786
0
                        char *ptrConcatLut =
1787
0
                            static_cast<char *>(CPLMalloc(2048));
1788
0
                        ptrConcatLut[0] =
1789
0
                            '\0'; /* Just initialize the first byte */
1790
0
                        strcat(ptrConcatLut, oldLut);
1791
0
                        strcat(ptrConcatLut, ",");
1792
0
                        strcat(ptrConcatLut, osLUTFilePath);
1793
0
                        poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", ptrConcatLut);
1794
0
                        CPLFree(ptrConcatLut);
1795
0
                    }
1796
1797
0
                    poDS->papszSubDatasets = CSLSetNameValue(
1798
0
                        poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf);
1799
0
                    poDS->papszSubDatasets = CSLSetNameValue(
1800
0
                        poDS->papszSubDatasets, "SUBDATASET_2_DESC",
1801
0
                        "Sigma Nought calibrated");
1802
0
                }
1803
0
                else if (EQUAL(pszLUTType, "Gamma") &&
1804
0
                         IsValidXMLFile(osLUTFilePath))
1805
0
                {
1806
0
                    poDS->papszExtraFiles =
1807
0
                        CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
1808
1809
0
                    CPLString pszBuf(
1810
0
                        FormatCalibration(szGAMMA, osMDFilename.c_str()));
1811
0
                    osGammaLUT = osLUTFilePath;
1812
1813
0
                    const char *oldLut = poDS->GetMetadataItem("GAMMA_LUT");
1814
0
                    if (oldLut == nullptr)
1815
0
                    {
1816
0
                        poDS->SetMetadataItem("GAMMA_LUT", osLUTFilePath);
1817
0
                    }
1818
0
                    else
1819
0
                    {
1820
                        /* Keep adding LUT file for all bands, should be planty
1821
                         * of space */
1822
0
                        char *ptrConcatLut =
1823
0
                            static_cast<char *>(CPLMalloc(2048));
1824
0
                        ptrConcatLut[0] =
1825
0
                            '\0'; /* Just initialize the first byte */
1826
0
                        strcat(ptrConcatLut, oldLut);
1827
0
                        strcat(ptrConcatLut, ",");
1828
0
                        strcat(ptrConcatLut, osLUTFilePath);
1829
0
                        poDS->SetMetadataItem("GAMMA_LUT", ptrConcatLut);
1830
0
                        CPLFree(ptrConcatLut);
1831
0
                    }
1832
1833
0
                    poDS->papszSubDatasets = CSLSetNameValue(
1834
0
                        poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf);
1835
0
                    poDS->papszSubDatasets = CSLSetNameValue(
1836
0
                        poDS->papszSubDatasets, "SUBDATASET_4_DESC",
1837
0
                        "Gamma calibrated");
1838
0
                }
1839
0
            }
1840
0
        }
1841
1842
        /* --------------------------------------------------------------------
1843
         */
1844
        /*      Fetch ipdf image. Could be either tif or ntf. */
1845
        /*      Replace / by \\ */
1846
        /* --------------------------------------------------------------------
1847
         */
1848
0
        const char *pszBasedFilename;
1849
0
        if (bIsNITF)
1850
0
        {
1851
0
            pszBasedFilename = pszNITF_Filename;
1852
0
        }
1853
0
        else
1854
0
        {
1855
0
            const int bandPositionIndex = imageBandList.FindString(pszPole);
1856
0
            if (bandPositionIndex < 0)
1857
0
            {
1858
0
                CPLError(CE_Failure, CPLE_OpenFailed,
1859
0
                         "ERROR: RCM cannot find the polarization %s. Please "
1860
0
                         "contact your data provider for a corrected dataset",
1861
0
                         pszPole.c_str());
1862
0
                return nullptr;
1863
0
            }
1864
1865
0
            pszBasedFilename = imageBandFileList[bandPositionIndex];
1866
0
        }
1867
1868
        /* --------------------------------------------------------------------
1869
         */
1870
        /*      Form full filename (path of product.xml + basename). */
1871
        /* --------------------------------------------------------------------
1872
         */
1873
0
        std::string osPathImage = osPath;
1874
0
        std::string osBasedImage = pszBasedFilename;
1875
0
        if (STARTS_WITH(osBasedImage.c_str(), "../") ||
1876
0
            STARTS_WITH(osBasedImage.c_str(), "..\\"))
1877
0
        {
1878
0
            osPathImage = CPLGetPathSafe(osPath);
1879
0
            osBasedImage = osBasedImage.substr(strlen("../"));
1880
0
        }
1881
0
        if (CPLHasPathTraversal(osBasedImage.c_str()))
1882
0
        {
1883
0
            CPLError(CE_Failure, CPLE_NotSupported,
1884
0
                     "Path traversal detected in %s", osBasedImage.c_str());
1885
0
            return nullptr;
1886
0
        }
1887
0
        const std::string osFullname = CPLFormFilenameSafe(
1888
0
            osPathImage.c_str(), osBasedImage.c_str(), nullptr);
1889
1890
        /* --------------------------------------------------------------------
1891
         */
1892
        /*      Try and open the file. */
1893
        /* --------------------------------------------------------------------
1894
         */
1895
0
        auto poBandFile = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1896
0
            osFullname.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
1897
0
        if (poBandFile == nullptr || poBandFile->GetRasterCount() == 0)
1898
0
        {
1899
0
            continue;
1900
0
        }
1901
1902
0
        poDS->papszExtraFiles =
1903
0
            CSLAddString(poDS->papszExtraFiles, osFullname.c_str());
1904
1905
        /* Some CFloat32 NITF files have nBitsPerSample incorrectly reported */
1906
        /* as 16, and get misinterpreted as CInt16.  Check the underlying NITF
1907
         */
1908
        /* and override if this is the case. */
1909
0
        if (poBandFile->GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
1910
0
            eDataType = GDT_CFloat32;
1911
1912
0
        BandMappingRCM b =
1913
0
            checkBandFileMappingRCM(eDataType, poBandFile.get(), bIsNITF);
1914
0
        if (b == BANDERROR)
1915
0
        {
1916
0
            CPLError(CE_Failure, CPLE_AppDefined,
1917
0
                     "The underlying band files do not have an appropriate "
1918
0
                     "data type.");
1919
0
            return nullptr;
1920
0
        }
1921
0
        bool twoBandComplex = b == TWOBANDCOMPLEX;
1922
0
        bool isOneFilePerPol = (imageBandCount == imageBandFileCount);
1923
1924
        /* --------------------------------------------------------------------
1925
         */
1926
        /*      Create the band. */
1927
        /* --------------------------------------------------------------------
1928
         */
1929
0
        int bandNum = poDS->GetRasterCount() + 1;
1930
0
        if (eCalib == None || eCalib == Uncalib)
1931
0
        {
1932
0
            RCMRasterBand *poBand = new RCMRasterBand(
1933
0
                poDS.get(), bandNum, eDataType, pszPole, poBandFile.release(),
1934
0
                twoBandComplex, isOneFilePerPol, bIsNITF);
1935
1936
0
            poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
1937
0
        }
1938
0
        else
1939
0
        {
1940
0
            const char *pszLUT = osSigma0LUT.c_str();
1941
0
            switch (eCalib)
1942
0
            {
1943
0
                case Sigma0:
1944
0
                    pszLUT = osSigma0LUT.c_str();
1945
0
                    break;
1946
0
                case Beta0:
1947
0
                    pszLUT = osBeta0LUT.c_str();
1948
0
                    break;
1949
0
                case Gamma:
1950
0
                    pszLUT = osGammaLUT.c_str();
1951
0
                    break;
1952
0
                default:
1953
                    /* we should bomb gracefully... */
1954
0
                    break;
1955
0
            }
1956
0
            if (pszLUT[0] == '\0')
1957
0
            {
1958
0
                CPLError(CE_Failure, CPLE_AppDefined, "LUT missing.");
1959
0
                return nullptr;
1960
0
            }
1961
1962
            // The variable 'osNoiseLevelsValues' is always the same for a ban
1963
            // name except the XML contains different calibration name
1964
0
            if (poDS->isComplexData)
1965
0
            {
1966
                // If Complex, always 32 bits
1967
0
                RCMCalibRasterBand *poBand = new RCMCalibRasterBand(
1968
0
                    poDS.get(), pszPole, GDT_Float32, poBandFile.release(),
1969
0
                    eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType);
1970
0
                poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
1971
0
            }
1972
0
            else
1973
0
            {
1974
                // Whatever the datatype was previously set
1975
0
                RCMCalibRasterBand *poBand = new RCMCalibRasterBand(
1976
0
                    poDS.get(), pszPole, eDataType, poBandFile.release(),
1977
0
                    eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType);
1978
0
                poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
1979
0
            }
1980
0
        }
1981
0
    }
1982
1983
0
    if (poDS->papszSubDatasets != nullptr && eCalib == None)
1984
0
    {
1985
0
        CPLString pszBuf = FormatCalibration(szUNCALIB, osMDFilename.c_str());
1986
0
        poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
1987
0
                                                 "SUBDATASET_1_NAME", pszBuf);
1988
0
        poDS->papszSubDatasets =
1989
0
            CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_1_DESC",
1990
0
                            "Uncalibrated digital numbers");
1991
0
    }
1992
0
    else if (poDS->papszSubDatasets != nullptr)
1993
0
    {
1994
0
        CSLDestroy(poDS->papszSubDatasets);
1995
0
        poDS->papszSubDatasets = nullptr;
1996
0
    }
1997
1998
    /* -------------------------------------------------------------------- */
1999
    /*      Set the appropriate MATRIX_REPRESENTATION.                      */
2000
    /* -------------------------------------------------------------------- */
2001
2002
0
    if (poDS->GetRasterCount() == 4 &&
2003
0
        (eDataType == GDT_CInt16 || eDataType == GDT_CFloat32))
2004
0
    {
2005
0
        poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
2006
0
    }
2007
2008
    /* -------------------------------------------------------------------- */
2009
    /*      Collect a few useful metadata items.                            */
2010
    /* -------------------------------------------------------------------- */
2011
2012
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "satellite", "");
2013
0
    poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem);
2014
2015
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "sensor", "");
2016
0
    poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem);
2017
2018
    /* Get beam mode mnemonic */
2019
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamMode", "UNK");
2020
0
    poDS->SetMetadataItem("BEAM_MODE", pszItem);
2021
2022
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeMnemonic", "UNK");
2023
0
    poDS->SetMetadataItem("BEAM_MODE_MNEMONIC", pszItem);
2024
2025
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeDefinitionId", "UNK");
2026
0
    poDS->SetMetadataItem("BEAM_MODE_DEFINITION_ID", pszItem);
2027
2028
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "rawDataStartTime", "UNK");
2029
0
    poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem);
2030
2031
0
    pszItem = CPLGetXMLValue(psSourceAttrs, "inputDatasetFacilityId", "UNK");
2032
0
    poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem);
2033
2034
0
    pszItem = CPLGetXMLValue(psSourceAttrs,
2035
0
                             "orbitAndAttitude.orbitInformation.passDirection",
2036
0
                             "UNK");
2037
0
    poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem);
2038
0
    pszItem = CPLGetXMLValue(
2039
0
        psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataSource",
2040
0
        "UNK");
2041
0
    poDS->SetMetadataItem("ORBIT_DATA_SOURCE", pszItem);
2042
0
    pszItem = CPLGetXMLValue(
2043
0
        psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataFileName",
2044
0
        "UNK");
2045
0
    poDS->SetMetadataItem("ORBIT_DATA_FILE", pszItem);
2046
2047
    /* Get incidence angle information. DONE */
2048
0
    pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngNearRng",
2049
0
                             "UNK");
2050
0
    poDS->SetMetadataItem("NEAR_RANGE_INCIDENCE_ANGLE", pszItem);
2051
2052
0
    pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngFarRng",
2053
0
                             "UNK");
2054
0
    poDS->SetMetadataItem("FAR_RANGE_INCIDENCE_ANGLE", pszItem);
2055
2056
0
    pszItem = CPLGetXMLValue(psSceneAttributes,
2057
0
                             "imageAttributes.slantRangeNearEdge", "UNK");
2058
0
    poDS->SetMetadataItem("SLANT_RANGE_NEAR_EDGE", pszItem);
2059
2060
0
    pszItem = CPLGetXMLValue(psSceneAttributes,
2061
0
                             "imageAttributes.slantRangeFarEdge", "UNK");
2062
0
    poDS->SetMetadataItem("SLANT_RANGE_FAR_EDGE", pszItem);
2063
2064
    /*--------------------------------------------------------------------- */
2065
    /*      Collect Map projection/Geotransform information, if present.DONE */
2066
    /*      In RCM, there is no file that indicates                         */
2067
    /* -------------------------------------------------------------------- */
2068
0
    const CPLXMLNode *psMapProjection = CPLGetXMLNode(
2069
0
        psImageReferenceAttributes, "geographicInformation.mapProjection");
2070
2071
0
    if (psMapProjection != nullptr)
2072
0
    {
2073
0
        const CPLXMLNode *psPos =
2074
0
            CPLGetXMLNode(psMapProjection, "positioningInformation");
2075
2076
0
        pszItem =
2077
0
            CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "UNK");
2078
0
        poDS->SetMetadataItem("MAP_PROJECTION_DESCRIPTOR", pszItem);
2079
2080
0
        pszItem =
2081
0
            CPLGetXMLValue(psMapProjection, "mapProjectionOrientation", "UNK");
2082
0
        poDS->SetMetadataItem("MAP_PROJECTION_ORIENTATION", pszItem);
2083
2084
0
        pszItem = CPLGetXMLValue(psMapProjection, "resamplingKernel", "UNK");
2085
0
        poDS->SetMetadataItem("RESAMPLING_KERNEL", pszItem);
2086
2087
0
        pszItem = CPLGetXMLValue(psMapProjection, "satelliteHeading", "UNK");
2088
0
        poDS->SetMetadataItem("SATELLITE_HEADING", pszItem);
2089
2090
0
        if (psPos != nullptr)
2091
0
        {
2092
0
            const double tl_x = CPLStrtod(
2093
0
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.easting",
2094
0
                               "0.0"),
2095
0
                nullptr);
2096
0
            const double tl_y = CPLStrtod(
2097
0
                CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.northing",
2098
0
                               "0.0"),
2099
0
                nullptr);
2100
0
            const double bl_x = CPLStrtod(
2101
0
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.easting",
2102
0
                               "0.0"),
2103
0
                nullptr);
2104
0
            const double bl_y = CPLStrtod(
2105
0
                CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.northing",
2106
0
                               "0.0"),
2107
0
                nullptr);
2108
0
            const double tr_x = CPLStrtod(
2109
0
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.easting",
2110
0
                               "0.0"),
2111
0
                nullptr);
2112
0
            const double tr_y = CPLStrtod(
2113
0
                CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.northing",
2114
0
                               "0.0"),
2115
0
                nullptr);
2116
0
            poDS->m_gt.xscale = (tr_x - tl_x) / (poDS->nRasterXSize - 1);
2117
0
            poDS->m_gt.yrot = (tr_y - tl_y) / (poDS->nRasterXSize - 1);
2118
0
            poDS->m_gt.xrot = (bl_x - tl_x) / (poDS->nRasterYSize - 1);
2119
0
            poDS->m_gt.yscale = (bl_y - tl_y) / (poDS->nRasterYSize - 1);
2120
0
            poDS->m_gt.xorig =
2121
0
                (tl_x - 0.5 * poDS->m_gt.xscale - 0.5 * poDS->m_gt.xrot);
2122
0
            poDS->m_gt.yorig =
2123
0
                (tl_y - 0.5 * poDS->m_gt.yrot - 0.5 * poDS->m_gt.yscale);
2124
2125
            /* Use bottom right pixel to test geotransform */
2126
0
            const double br_x = CPLStrtod(
2127
0
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.easting",
2128
0
                               "0.0"),
2129
0
                nullptr);
2130
0
            const double br_y = CPLStrtod(
2131
0
                CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.northing",
2132
0
                               "0.0"),
2133
0
                nullptr);
2134
0
            const double testx =
2135
0
                poDS->m_gt.xorig +
2136
0
                poDS->m_gt.xscale * (poDS->nRasterXSize - 0.5) +
2137
0
                poDS->m_gt.xrot * (poDS->nRasterYSize - 0.5);
2138
0
            const double testy = poDS->m_gt.yorig +
2139
0
                                 poDS->m_gt.yrot * (poDS->nRasterXSize - 0.5) +
2140
0
                                 poDS->m_gt.yscale * (poDS->nRasterYSize - 0.5);
2141
2142
            /* Give 1/4 pixel numerical error leeway in calculating location
2143
            based on affine transform */
2144
0
            if ((fabs(testx - br_x) >
2145
0
                 fabs(0.25 * (poDS->m_gt.xscale + poDS->m_gt.xrot))) ||
2146
0
                (fabs(testy - br_y) >
2147
0
                 fabs(0.25 * (poDS->m_gt.yrot + poDS->m_gt.yscale))))
2148
0
            {
2149
0
                CPLError(CE_Warning, CPLE_AppDefined,
2150
0
                         "WARNING: Unexpected error in calculating affine "
2151
0
                         "transform: corner coordinates inconsistent.");
2152
0
            }
2153
0
            else
2154
0
            {
2155
0
                poDS->bHaveGeoTransform = TRUE;
2156
0
            }
2157
0
        }
2158
0
    }
2159
2160
    /* -------------------------------------------------------------------- */
2161
    /*      Collect Projection String Information.DONE                      */
2162
    /* -------------------------------------------------------------------- */
2163
0
    const CPLXMLNode *psEllipsoid =
2164
0
        CPLGetXMLNode(psImageReferenceAttributes,
2165
0
                      "geographicInformation.ellipsoidParameters");
2166
2167
0
    if (psEllipsoid != nullptr)
2168
0
    {
2169
0
        OGRSpatialReference oLL, oPrj;
2170
2171
0
        const char *pszGeodeticTerrainHeight =
2172
0
            CPLGetXMLValue(psEllipsoid, "geodeticTerrainHeight", "UNK");
2173
0
        poDS->SetMetadataItem("GEODETIC_TERRAIN_HEIGHT",
2174
0
                              pszGeodeticTerrainHeight);
2175
2176
0
        const char *pszEllipsoidName =
2177
0
            CPLGetXMLValue(psEllipsoid, "ellipsoidName", "");
2178
0
        double minor_axis =
2179
0
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMinorAxis", "0.0"));
2180
0
        double major_axis =
2181
0
            CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMajorAxis", "0.0"));
2182
2183
0
        if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) ||
2184
0
            (major_axis == 0.0))
2185
0
        {
2186
0
            oLL.SetWellKnownGeogCS("WGS84");
2187
0
            oPrj.SetWellKnownGeogCS("WGS84");
2188
2189
0
            CPLError(CE_Warning, CPLE_AppDefined,
2190
0
                     "WARNING: Incomplete ellipsoid "
2191
0
                     "information.  Using wgs-84 parameters.");
2192
0
        }
2193
0
        else if (EQUAL(pszEllipsoidName, "WGS84") ||
2194
0
                 EQUAL(pszEllipsoidName, "WGS 1984"))
2195
0
        {
2196
0
            oLL.SetWellKnownGeogCS("WGS84");
2197
0
            oPrj.SetWellKnownGeogCS("WGS84");
2198
0
        }
2199
0
        else
2200
0
        {
2201
0
            const double inv_flattening =
2202
0
                major_axis / (major_axis - minor_axis);
2203
0
            oLL.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening);
2204
0
            oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis,
2205
0
                           inv_flattening);
2206
0
        }
2207
2208
0
        if (psMapProjection != nullptr)
2209
0
        {
2210
0
            const char *pszProj =
2211
0
                CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "");
2212
0
            bool bUseProjInfo = false;
2213
2214
0
            const CPLXMLNode *psUtmParams =
2215
0
                CPLGetXMLNode(psMapProjection, "utmProjectionParameters");
2216
2217
0
            const CPLXMLNode *psNspParams =
2218
0
                CPLGetXMLNode(psMapProjection, "nspProjectionParameters");
2219
2220
0
            if ((psUtmParams != nullptr) && poDS->bHaveGeoTransform)
2221
0
            {
2222
                /* double origEasting, origNorthing; */
2223
0
                bool bNorth = true;
2224
2225
0
                const int utmZone =
2226
0
                    atoi(CPLGetXMLValue(psUtmParams, "utmZone", ""));
2227
0
                const char *pszHemisphere =
2228
0
                    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
0
                if (STARTS_WITH_CI(pszHemisphere, "southern"))
2236
0
                    bNorth = FALSE;
2237
2238
0
                if (STARTS_WITH_CI(pszProj, "UTM"))
2239
0
                {
2240
0
                    oPrj.SetUTM(utmZone, bNorth);
2241
0
                    bUseProjInfo = true;
2242
0
                }
2243
0
            }
2244
0
            else if ((psNspParams != nullptr) && poDS->bHaveGeoTransform)
2245
0
            {
2246
0
                const double origEasting = CPLStrtod(
2247
0
                    CPLGetXMLValue(psNspParams, "mapOriginFalseEasting", "0.0"),
2248
0
                    nullptr);
2249
0
                const double origNorthing =
2250
0
                    CPLStrtod(CPLGetXMLValue(psNspParams,
2251
0
                                             "mapOriginFalseNorthing", "0.0"),
2252
0
                              nullptr);
2253
0
                const double copLong = CPLStrtod(
2254
0
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLongitude",
2255
0
                                   "0.0"),
2256
0
                    nullptr);
2257
0
                const double copLat = CPLStrtod(
2258
0
                    CPLGetXMLValue(psNspParams, "centerOfProjectionLatitude",
2259
0
                                   "0.0"),
2260
0
                    nullptr);
2261
0
                const double sP1 = CPLStrtod(
2262
0
                    CPLGetXMLValue(psNspParams, "standardParallels1", "0.0"),
2263
0
                    nullptr);
2264
0
                const double sP2 = CPLStrtod(
2265
0
                    CPLGetXMLValue(psNspParams, "standardParallels2", "0.0"),
2266
0
                    nullptr);
2267
2268
0
                if (STARTS_WITH_CI(pszProj, "ARC"))
2269
0
                {
2270
                    /* Albers Conical Equal Area */
2271
0
                    oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting,
2272
0
                                 origNorthing);
2273
0
                    bUseProjInfo = true;
2274
0
                }
2275
0
                else if (STARTS_WITH_CI(pszProj, "LCC"))
2276
0
                {
2277
                    /* Lambert Conformal Conic */
2278
0
                    oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting,
2279
0
                                origNorthing);
2280
0
                    bUseProjInfo = true;
2281
0
                }
2282
0
                else if (STARTS_WITH_CI(pszProj, "STPL"))
2283
0
                {
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
0
                    const int nSPZone =
2290
0
                        atoi(CPLGetXMLValue(psNspParams, "zone", "1"));
2291
2292
0
                    oPrj.SetStatePlane(nSPZone, TRUE, nullptr, 0.0);
2293
0
                    bUseProjInfo = true;
2294
0
                }
2295
0
            }
2296
2297
0
            if (bUseProjInfo)
2298
0
            {
2299
0
                poDS->m_oSRS = std::move(oPrj);
2300
0
            }
2301
0
            else
2302
0
            {
2303
0
                CPLError(CE_Warning, CPLE_AppDefined,
2304
0
                         "WARNING: Unable to interpret projection information; "
2305
0
                         "check mapProjection info in product.xml!");
2306
0
            }
2307
0
        }
2308
2309
0
        poDS->m_oGCPSRS = std::move(oLL);
2310
0
    }
2311
2312
    /* -------------------------------------------------------------------- */
2313
    /*      Collect GCPs.DONE */
2314
    /* -------------------------------------------------------------------- */
2315
0
    const CPLXMLNode *psGeoGrid = CPLGetXMLNode(
2316
0
        psImageReferenceAttributes, "geographicInformation.geolocationGrid");
2317
2318
0
    if (psGeoGrid != nullptr)
2319
0
    {
2320
        /* count GCPs */
2321
0
        poDS->nGCPCount = 0;
2322
2323
0
        for (const CPLXMLNode *psNode = psGeoGrid->psChild; psNode != nullptr;
2324
0
             psNode = psNode->psNext)
2325
0
        {
2326
0
            if (EQUAL(psNode->pszValue, "imageTiePoint"))
2327
0
                poDS->nGCPCount++;
2328
0
        }
2329
2330
0
        poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>(
2331
0
            CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount));
2332
2333
0
        poDS->nGCPCount = 0;
2334
2335
0
        for (const CPLXMLNode *psNode = psGeoGrid->psChild; psNode != nullptr;
2336
0
             psNode = psNode->psNext)
2337
0
        {
2338
0
            GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount;
2339
2340
0
            if (!EQUAL(psNode->pszValue, "imageTiePoint"))
2341
0
                continue;
2342
2343
0
            poDS->nGCPCount++;
2344
2345
0
            char szID[32];
2346
0
            snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount);
2347
0
            psGCP->pszId = CPLStrdup(szID);
2348
0
            psGCP->pszInfo = CPLStrdup("");
2349
0
            psGCP->dfGCPPixel =
2350
0
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.pixel", "0"));
2351
0
            psGCP->dfGCPLine =
2352
0
                CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.line", "0"));
2353
0
            psGCP->dfGCPX = CPLAtof(
2354
0
                CPLGetXMLValue(psNode, "geodeticCoordinate.longitude", ""));
2355
0
            psGCP->dfGCPY = CPLAtof(
2356
0
                CPLGetXMLValue(psNode, "geodeticCoordinate.latitude", ""));
2357
0
            psGCP->dfGCPZ = CPLAtof(
2358
0
                CPLGetXMLValue(psNode, "geodeticCoordinate.height", ""));
2359
0
        }
2360
0
    }
2361
2362
    /* -------------------------------------------------------------------- */
2363
    /*      Collect RPC.DONE */
2364
    /* -------------------------------------------------------------------- */
2365
0
    const CPLXMLNode *psRationalFunctions = CPLGetXMLNode(
2366
0
        psImageReferenceAttributes, "geographicInformation.rationalFunctions");
2367
0
    if (psRationalFunctions != nullptr)
2368
0
    {
2369
0
        char **papszRPC = nullptr;
2370
0
        static const char *const apszXMLToGDALMapping[] = {
2371
0
            "biasError",
2372
0
            "ERR_BIAS",
2373
0
            "randomError",
2374
0
            "ERR_RAND",
2375
            //"lineFitQuality", "????",
2376
            //"pixelFitQuality", "????",
2377
0
            "lineOffset",
2378
0
            "LINE_OFF",
2379
0
            "pixelOffset",
2380
0
            "SAMP_OFF",
2381
0
            "latitudeOffset",
2382
0
            "LAT_OFF",
2383
0
            "longitudeOffset",
2384
0
            "LONG_OFF",
2385
0
            "heightOffset",
2386
0
            "HEIGHT_OFF",
2387
0
            "lineScale",
2388
0
            "LINE_SCALE",
2389
0
            "pixelScale",
2390
0
            "SAMP_SCALE",
2391
0
            "latitudeScale",
2392
0
            "LAT_SCALE",
2393
0
            "longitudeScale",
2394
0
            "LONG_SCALE",
2395
0
            "heightScale",
2396
0
            "HEIGHT_SCALE",
2397
0
            "lineNumeratorCoefficients",
2398
0
            "LINE_NUM_COEFF",
2399
0
            "lineDenominatorCoefficients",
2400
0
            "LINE_DEN_COEFF",
2401
0
            "pixelNumeratorCoefficients",
2402
0
            "SAMP_NUM_COEFF",
2403
0
            "pixelDenominatorCoefficients",
2404
0
            "SAMP_DEN_COEFF",
2405
0
        };
2406
0
        for (size_t i = 0; i < CPL_ARRAYSIZE(apszXMLToGDALMapping); i += 2)
2407
0
        {
2408
0
            const char *pszValue = CPLGetXMLValue(
2409
0
                psRationalFunctions, apszXMLToGDALMapping[i], nullptr);
2410
0
            if (pszValue)
2411
0
                papszRPC = CSLSetNameValue(
2412
0
                    papszRPC, apszXMLToGDALMapping[i + 1], pszValue);
2413
0
        }
2414
0
        poDS->GDALDataset::SetMetadata(papszRPC, GDAL_MDD_RPC);
2415
0
        CSLDestroy(papszRPC);
2416
0
    }
2417
2418
    /* -------------------------------------------------------------------- */
2419
    /*      Initialize any PAM information.                                 */
2420
    /* -------------------------------------------------------------------- */
2421
0
    CPLString osDescription;
2422
0
    CPLString osSubdatasetName;
2423
0
    bool useSubdatasets = true;
2424
2425
0
    switch (eCalib)
2426
0
    {
2427
0
        case Sigma0:
2428
0
        {
2429
0
            osSubdatasetName = szSIGMA0;
2430
0
            osDescription = FormatCalibration(szSIGMA0, osMDFilename.c_str());
2431
0
        }
2432
0
        break;
2433
0
        case Beta0:
2434
0
        {
2435
0
            osSubdatasetName = szBETA0;
2436
0
            osDescription = FormatCalibration(szBETA0, osMDFilename.c_str());
2437
0
        }
2438
0
        break;
2439
0
        case Gamma:
2440
0
        {
2441
0
            osSubdatasetName = szGAMMA;
2442
0
            osDescription = FormatCalibration(szGAMMA, osMDFilename.c_str());
2443
0
        }
2444
0
        break;
2445
0
        case Uncalib:
2446
0
        {
2447
0
            osSubdatasetName = szUNCALIB;
2448
0
            osDescription = FormatCalibration(szUNCALIB, osMDFilename.c_str());
2449
0
        }
2450
0
        break;
2451
0
        default:
2452
0
            osSubdatasetName = szUNCALIB;
2453
0
            osDescription = osMDFilename;
2454
0
            useSubdatasets = false;
2455
0
    }
2456
2457
0
    CPL_IGNORE_RET_VAL(osSubdatasetName);
2458
2459
0
    if (eCalib != None)
2460
0
        poDS->papszExtraFiles =
2461
0
            CSLAddString(poDS->papszExtraFiles, osMDFilename);
2462
2463
    /* -------------------------------------------------------------------- */
2464
    /*      Initialize any PAM information.                                 */
2465
    /* -------------------------------------------------------------------- */
2466
0
    poDS->SetDescription(osDescription);
2467
2468
0
    poDS->SetPhysicalFilename(osMDFilename);
2469
0
    poDS->SetSubdatasetName(osDescription);
2470
2471
0
    poDS->TryLoadXML();
2472
2473
    /* -------------------------------------------------------------------- */
2474
    /*      Check for overviews.                                            */
2475
    /* -------------------------------------------------------------------- */
2476
0
    if (useSubdatasets)
2477
0
        poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
2478
0
    else
2479
0
        poDS->oOvManager.Initialize(poDS.get(), osMDFilename);
2480
2481
0
    return poDS.release();
2482
0
}
2483
2484
/************************************************************************/
2485
/*                            GetGCPCount()                             */
2486
/************************************************************************/
2487
2488
int RCMDataset::GetGCPCount()
2489
2490
0
{
2491
0
    return nGCPCount;
2492
0
}
2493
2494
/************************************************************************/
2495
/*                          GetGCPSpatialRef()                          */
2496
/************************************************************************/
2497
2498
const OGRSpatialReference *RCMDataset::GetGCPSpatialRef() const
2499
2500
0
{
2501
0
    return m_oGCPSRS.IsEmpty() || nGCPCount == 0 ? nullptr : &m_oGCPSRS;
2502
0
}
2503
2504
/************************************************************************/
2505
/*                              GetGCPs()                               */
2506
/************************************************************************/
2507
2508
const GDAL_GCP *RCMDataset::GetGCPs()
2509
2510
0
{
2511
0
    return pasGCPList;
2512
0
}
2513
2514
/************************************************************************/
2515
/*                           GetSpatialRef()                            */
2516
/************************************************************************/
2517
2518
const OGRSpatialReference *RCMDataset::GetSpatialRef() const
2519
2520
0
{
2521
0
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
2522
0
}
2523
2524
/************************************************************************/
2525
/*                          GetGeoTransform()                           */
2526
/************************************************************************/
2527
2528
CPLErr RCMDataset::GetGeoTransform(GDALGeoTransform &gt) const
2529
2530
0
{
2531
0
    gt = m_gt;
2532
2533
0
    if (bHaveGeoTransform)
2534
0
    {
2535
0
        return CE_None;
2536
0
    }
2537
2538
0
    return CE_Failure;
2539
0
}
2540
2541
/************************************************************************/
2542
/*                       GetMetadataDomainList()                        */
2543
/************************************************************************/
2544
2545
char **RCMDataset::GetMetadataDomainList()
2546
0
{
2547
0
    return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
2548
0
                                   GDAL_MDD_SUBDATASETS, nullptr);
2549
0
}
2550
2551
/************************************************************************/
2552
/*                            GetMetadata()                             */
2553
/************************************************************************/
2554
2555
CSLConstList RCMDataset::GetMetadata(const char *pszDomain)
2556
2557
0
{
2558
0
    if (pszDomain != nullptr &&
2559
0
        STARTS_WITH_CI(pszDomain, GDAL_MDD_SUBDATASETS) &&
2560
0
        papszSubDatasets != nullptr)
2561
0
        return papszSubDatasets;
2562
2563
0
    return GDALDataset::GetMetadata(pszDomain);
2564
0
}
2565
2566
/************************************************************************/
2567
/*                          GDALRegister_RCM()                          */
2568
/************************************************************************/
2569
2570
void GDALRegister_RCM()
2571
2572
22
{
2573
22
    if (GDALGetDriverByName("RCM") != nullptr)
2574
0
    {
2575
0
        return;
2576
0
    }
2577
2578
22
    GDALDriver *poDriver = new GDALDriver();
2579
22
    RCMDriverSetCommonMetadata(poDriver);
2580
2581
22
    poDriver->pfnOpen = RCMDataset::Open;
2582
2583
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
2584
22
}