LCOV - code coverage report
Current view: top level - frmts/rs2 - rs2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 399 637 62.6 %
Date: 2025-10-27 00:14:23 Functions: 20 26 76.9 %

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

Generated by: LCOV version 1.14