LCOV - code coverage report
Current view: top level - frmts/rs2 - rs2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 389 625 62.2 %
Date: 2026-02-12 23:49:34 Functions: 18 24 75.0 %

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

Generated by: LCOV version 1.14