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

Generated by: LCOV version 1.14