LCOV - code coverage report
Current view: top level - frmts/safe - safedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 474 837 56.6 %
Date: 2026-02-12 23:49:34 Functions: 18 29 62.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Sentinel SAFE products
       4             :  * Purpose:  Sentinel Products (manifest.safe) driver
       5             :  * Author:   Delfim Rego, delfimrego@gmail.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Delfim Rego <delfimrego@gmail.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "safedataset.h"
      14             : 
      15             : #include "gdal_driver.h"
      16             : #include "gdal_drivermanager.h"
      17             : #include "gdal_openinfo.h"
      18             : #include "gdal_cpp_functions.h"
      19             : 
      20             : #include "cpl_time.h"
      21             : 
      22             : #include <algorithm>
      23             : 
      24             : #ifdef USE_OMP
      25             : #include <omp.h>
      26             : #endif
      27             : 
      28             : #ifdef USE_OMP
      29             : static int GetNumThreadsToUse()
      30             : {
      31             :     unsigned int nCores = std::thread::hardware_concurrency();
      32             :     return (nCores / 2 > 1) ? nCores / 2 : 1;
      33             : }
      34             : #endif
      35             : 
      36             : /************************************************************************/
      37             : /*                            SAFERasterBand                            */
      38             : /************************************************************************/
      39             : 
      40          14 : SAFERasterBand::SAFERasterBand(SAFEDataset *poDSIn, GDALDataType eDataTypeIn,
      41             :                                const CPLString &osSwath,
      42             :                                const CPLString &osPolarization,
      43          14 :                                std::unique_ptr<GDALDataset> &&poBandFileIn)
      44          14 :     : poBandFile(std::move(poBandFileIn))
      45             : {
      46          14 :     poDS = poDSIn;
      47          14 :     GDALRasterBand *poSrcBand = poBandFile->GetRasterBand(1);
      48          14 :     poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
      49          14 :     eDataType = eDataTypeIn;
      50             : 
      51          14 :     if (!osSwath.empty())
      52          14 :         SetMetadataItem("SWATH", osSwath.c_str());
      53             : 
      54          14 :     if (!osPolarization.empty())
      55          14 :         SetMetadataItem("POLARIZATION", osPolarization.c_str());
      56          14 : }
      57             : 
      58             : /************************************************************************/
      59             : /*                             IReadBlock()                             */
      60             : /************************************************************************/
      61             : 
      62          55 : CPLErr SAFERasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
      63             : 
      64             : {
      65             :     /* -------------------------------------------------------------------- */
      66             :     /*      If the last strip is partial, we need to avoid                  */
      67             :     /*      over-requesting.  We also need to initialize the extra part     */
      68             :     /*      of the block to zero.                                           */
      69             :     /* -------------------------------------------------------------------- */
      70          55 :     const int nYOff = nBlockYOff * nBlockYSize;
      71          55 :     const int nRequestYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
      72             : 
      73             :     /*-------------------------------------------------------------------- */
      74             :     /*      If the input imagery is tiled, also need to avoid over-        */
      75             :     /*      requesting in the X-direction.                                 */
      76             :     /* ------------------------------------------------------------------- */
      77          55 :     const int nXOff = nBlockXOff * nBlockXSize;
      78          55 :     const int nRequestXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
      79             : 
      80          55 :     if (nRequestXSize < nBlockXSize || nRequestYSize < nBlockYSize)
      81             :     {
      82           5 :         memset(pImage, 0,
      83           5 :                static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
      84           5 :                    nBlockXSize * nBlockYSize);
      85             :     }
      86             : 
      87          55 :     if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 2)
      88           0 :         return poBandFile->RasterIO(GF_Read, nXOff, nYOff, nRequestXSize,
      89             :                                     nRequestYSize, pImage, nRequestXSize,
      90             :                                     nRequestYSize, GDT_Int16, 2, nullptr, 4,
      91           0 :                                     nBlockXSize * 4, 2, nullptr);
      92             : 
      93             :     /* -------------------------------------------------------------------- */
      94             :     /*      File has one sample marked as sample format void, a 32bits.     */
      95             :     /* -------------------------------------------------------------------- */
      96          55 :     else if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 1)
      97             :     {
      98           0 :         CPLErr eErr = poBandFile->RasterIO(
      99             :             GF_Read, nXOff, nYOff, nRequestXSize, nRequestYSize, pImage,
     100             :             nRequestXSize, nRequestYSize, GDT_CInt16, 1, nullptr, 4,
     101           0 :             nBlockXSize * 4, 0, nullptr);
     102             : 
     103           0 :         return eErr;
     104             :     }
     105             : 
     106             :     /* -------------------------------------------------------------------- */
     107             :     /*      The 16bit case is straight forward.  The underlying file        */
     108             :     /*      looks like a 16bit unsigned data too.                           */
     109             :     /* -------------------------------------------------------------------- */
     110          55 :     else if (eDataType == GDT_UInt16)
     111          55 :         return poBandFile->RasterIO(GF_Read, nXOff, nYOff, nRequestXSize,
     112             :                                     nRequestYSize, pImage, nRequestXSize,
     113             :                                     nRequestYSize, GDT_UInt16, 1, nullptr, 2,
     114          55 :                                     nBlockXSize * 2, 0, nullptr);
     115             : 
     116           0 :     else if (eDataType == GDT_UInt8)
     117           0 :         return poBandFile->RasterIO(GF_Read, nXOff, nYOff, nRequestXSize,
     118             :                                     nRequestYSize, pImage, nRequestXSize,
     119             :                                     nRequestYSize, GDT_UInt8, 1, nullptr, 1,
     120           0 :                                     nBlockXSize, 0, nullptr);
     121             : 
     122           0 :     CPLAssert(false);
     123             :     return CE_Failure;
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                          SAFESLCRasterBand                           */
     128             : /************************************************************************/
     129             : 
     130           3 : SAFESLCRasterBand::SAFESLCRasterBand(
     131             :     SAFEDataset *poDSIn, GDALDataType eDataTypeIn, const CPLString &osSwath,
     132             :     const CPLString &osPolarization,
     133           3 :     std::unique_ptr<GDALDataset> &&poBandFileIn, BandType eBandType)
     134           3 :     : poBandFile(std::move(poBandFileIn))
     135             : {
     136           3 :     poDS = poDSIn;
     137           3 :     eDataType = eDataTypeIn;
     138           3 :     m_eInputDataType = eDataTypeIn;
     139           3 :     GDALRasterBand *poSrcBand = poBandFile->GetRasterBand(1);
     140           3 :     poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     141           3 :     m_eBandType = eBandType;
     142             : 
     143           3 :     if (!osSwath.empty())
     144           3 :         SetMetadataItem("SWATH", osSwath.c_str());
     145             : 
     146           3 :     if (!osPolarization.empty())
     147           3 :         SetMetadataItem("POLARIZATION", osPolarization.c_str());
     148             : 
     149             :     // For intensity band
     150           3 :     if (m_eBandType == INTENSITY)
     151           2 :         eDataType = GDT_Float32;
     152             :     else
     153             :         // For complex bands
     154           1 :         eDataType = GDT_CInt16;
     155           3 : }
     156             : 
     157             : /************************************************************************/
     158             : /*                             IReadBlock()                             */
     159             : /************************************************************************/
     160             : 
     161           0 : CPLErr SAFESLCRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     162             :                                      void *pImage)
     163             : 
     164             : {
     165             :     /* -------------------------------------------------------------------- */
     166             :     /*      If the last strip is partial, we need to avoid                  */
     167             :     /*      over-requesting.  We also need to initialize the extra part     */
     168             :     /*      of the block to zero.                                           */
     169             :     /* -------------------------------------------------------------------- */
     170           0 :     const int nXOff = nBlockXOff * nBlockXSize;
     171           0 :     const int nRequestXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
     172           0 :     const int nYOff = nBlockYOff * nBlockYSize;
     173           0 :     const int nRequestYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
     174             : 
     175           0 :     if (nRequestYSize < nBlockYSize)
     176             :     {
     177           0 :         memset(pImage, 0,
     178           0 :                static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
     179           0 :                    nBlockXSize * nBlockYSize);
     180             :     }
     181             : 
     182             :     /*-------------------------------------------------------------------- */
     183             :     /*      If the input imagery is tiled, also need to avoid over-        */
     184             :     /*      requesting in the X-direction.                                 */
     185             :     /* ------------------------------------------------------------------- */
     186           0 :     if (nRequestXSize < nBlockXSize)
     187             :     {
     188           0 :         memset(pImage, 0,
     189           0 :                static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
     190           0 :                    nBlockXSize * nBlockYSize);
     191             :     }
     192             : 
     193           0 :     if (m_eInputDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 2)
     194             :     {
     195           0 :         return poBandFile->RasterIO(GF_Read, nXOff, nYOff, nRequestXSize,
     196             :                                     nRequestYSize, pImage, nRequestXSize,
     197             :                                     nRequestYSize, GDT_Int16, 2, nullptr, 4,
     198           0 :                                     nBlockXSize * 4, 2, nullptr);
     199             :     }
     200             :     // File has one sample marked as sample format void, a 32bits.
     201           0 :     else if (m_eInputDataType == GDT_CInt16 &&
     202           0 :              poBandFile->GetRasterCount() == 1)
     203             :     {
     204           0 :         if (m_eBandType == COMPLEX)
     205             :         {
     206           0 :             CPLErr eErr = poBandFile->RasterIO(
     207             :                 GF_Read, nXOff, nYOff, nRequestXSize, nRequestYSize, pImage,
     208             :                 nRequestXSize, nRequestYSize, GDT_CInt16, 1, nullptr, 4,
     209           0 :                 nBlockXSize * 4, 0, nullptr);
     210           0 :             if (eErr != CE_None)
     211             :             {
     212           0 :                 return eErr;
     213             :             }
     214             :         }
     215           0 :         else if (m_eBandType == INTENSITY)
     216             :         {
     217           0 :             GInt16 *pnImageTmp = static_cast<GInt16 *>(VSI_MALLOC3_VERBOSE(
     218             :                 2 * sizeof(int16_t), nBlockXSize, nBlockYSize));
     219           0 :             if (!pnImageTmp)
     220             :             {
     221           0 :                 return CE_Failure;
     222             :             }
     223             : 
     224           0 :             CPLErr eErr = poBandFile->RasterIO(
     225             :                 GF_Read, nXOff, nYOff, nRequestXSize, nRequestYSize, pnImageTmp,
     226             :                 nRequestXSize, nRequestYSize, GDT_CInt16, 1, nullptr, 4,
     227           0 :                 nBlockXSize * 4, 0, nullptr);
     228           0 :             if (eErr != CE_None)
     229             :             {
     230           0 :                 CPLFree(pnImageTmp);
     231           0 :                 return eErr;
     232             :             }
     233             : 
     234           0 :             float *pfBuffer = static_cast<float *>(pImage);
     235             : #ifdef USE_OMP
     236             :             omp_set_num_threads(GetNumThreadsToUse());
     237             : #pragma omp parallel
     238             : #endif
     239           0 :             for (int i = 0; i < nBlockYSize; i++)
     240             :             {
     241             : #ifdef USE_OMP
     242             : #pragma omp for nowait
     243             : #endif
     244           0 :                 for (int j = 0; j < nBlockXSize; j++)
     245             :                 {
     246           0 :                     int nPixOff = (2 * (i * nBlockXSize)) + (j * 2);
     247           0 :                     int nOutPixOff = (i * nBlockXSize) + j;
     248           0 :                     pfBuffer[nOutPixOff] = static_cast<float>(
     249           0 :                         static_cast<double>(pnImageTmp[nPixOff] *
     250           0 :                                             pnImageTmp[nPixOff]) +
     251           0 :                         static_cast<double>(pnImageTmp[nPixOff + 1] *
     252           0 :                                             pnImageTmp[nPixOff + 1]));
     253             :                 }
     254             :             }
     255           0 :             CPLFree(pnImageTmp);
     256             :         }
     257           0 :         return CE_None;
     258             :     }
     259             : 
     260           0 :     CPLAssert(false);
     261             :     return CE_Failure;
     262             : }
     263             : 
     264             : /************************************************************************/
     265             : /*                         SAFECalibRasterBand                          */
     266             : /************************************************************************/
     267             : 
     268           0 : SAFECalibratedRasterBand::SAFECalibratedRasterBand(
     269             :     SAFEDataset *poDSIn, GDALDataType eDataTypeIn, const CPLString &osSwath,
     270             :     const CPLString &osPolarization,
     271             :     std::unique_ptr<GDALDataset> &&poBandDatasetIn,
     272           0 :     const char *pszCalibrationFilename, CalibrationType eCalibrationType)
     273           0 :     : poBandDataset(std::move(poBandDatasetIn))
     274             : {
     275           0 :     poDS = poDSIn;
     276           0 :     GDALRasterBand *poSrcBand = poBandDataset->GetRasterBand(1);
     277           0 :     poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     278           0 :     eDataType = eDataTypeIn;
     279             : 
     280           0 :     if (!osSwath.empty())
     281           0 :         SetMetadataItem("SWATH", osSwath.c_str());
     282             : 
     283           0 :     if (!osPolarization.empty())
     284           0 :         SetMetadataItem("POLARIZATION", osPolarization.c_str());
     285             : 
     286           0 :     m_osCalibrationFilename = pszCalibrationFilename;
     287           0 :     m_eInputDataType = eDataTypeIn;
     288           0 :     eDataType = GDT_Float32;
     289           0 :     m_eCalibrationType = eCalibrationType;
     290           0 : }
     291             : 
     292             : /************************************************************************/
     293             : /*                              ReadLUT()                               */
     294             : /************************************************************************/
     295             : /* Read the provided LUT in to m_ndTable                                */
     296             : /************************************************************************/
     297           0 : bool SAFECalibratedRasterBand::ReadLUT()
     298             : {
     299           0 :     const char *const papszCalibrationNodes[3] = {
     300             :         "=calibrationVector.sigmaNought", "=calibrationVector.betaNought",
     301             :         "=calibrationVector.gamma"};
     302           0 :     CPLString osCalibrationNodeName = papszCalibrationNodes[m_eCalibrationType];
     303           0 :     const char *pszEndSpace = " ";
     304           0 :     CPLString osStartTime, osEndTime;
     305           0 :     CPLXMLNode *poLUT = CPLParseXMLFile(m_osCalibrationFilename);
     306           0 :     if (!poLUT)
     307           0 :         return false;
     308             : 
     309           0 :     CPLString osParseLUT;
     310           0 :     CPLString osParsePixelLUT;
     311           0 :     CPLString osParseAzimuthLUT;
     312           0 :     CPLString osParseLineNoLUT;
     313           0 :     for (CPLXMLNode *psNode = poLUT; psNode != nullptr;)
     314             :     {
     315           0 :         if (psNode->psNext != nullptr)
     316           0 :             psNode = psNode->psNext;
     317             : 
     318           0 :         if (EQUAL(psNode->pszValue, "calibration"))
     319             :         {
     320           0 :             for (psNode = psNode->psChild; psNode != nullptr;)
     321             :             {
     322           0 :                 if (EQUAL(psNode->pszValue, "adsHeader"))
     323             :                 {
     324             :                     osStartTime =
     325           0 :                         CPLGetXMLValue(psNode, "=adsHeader.startTime", " ");
     326             :                     osEndTime =
     327           0 :                         CPLGetXMLValue(psNode, "=adsHeader.stopTime", " ");
     328             :                 }
     329             : 
     330           0 :                 if (psNode->psNext != nullptr)
     331           0 :                     psNode = psNode->psNext;
     332             : 
     333           0 :                 if (EQUAL(psNode->pszValue, "calibrationVectorList"))
     334             :                 {
     335           0 :                     for (psNode = psNode->psChild; psNode != nullptr;
     336           0 :                          psNode = psNode->psNext)
     337             :                     {
     338           0 :                         if (EQUAL(psNode->pszValue, "calibrationVector"))
     339             :                         {
     340             :                             osParseAzimuthLUT += CPLGetXMLValue(
     341           0 :                                 psNode, "=calibrationVector.azimuthTime", " ");
     342           0 :                             osParseAzimuthLUT += pszEndSpace;
     343             :                             osParseLineNoLUT += CPLGetXMLValue(
     344           0 :                                 psNode, "=calibrationVector.line", " ");
     345           0 :                             osParseLineNoLUT += pszEndSpace;
     346             :                             osParsePixelLUT =
     347           0 :                                 CPLGetXMLValue(psNode, "pixel", " ");
     348           0 :                             m_nNumPixels = static_cast<int>(CPLAtof(
     349             :                                 CPLGetXMLValue(psNode, "pixel.count", " ")));
     350             :                             osParseLUT += CPLGetXMLValue(
     351           0 :                                 psNode, osCalibrationNodeName, " ");
     352           0 :                             osParseLUT += pszEndSpace;
     353             :                         }
     354             :                     }
     355             :                 }
     356             :             }
     357             :         }
     358             :     }
     359           0 :     CPLDestroyXMLNode(poLUT);
     360             : 
     361           0 :     osParsePixelLUT += pszEndSpace;
     362             : 
     363             :     CPLStringList oStartTimeList(
     364           0 :         CSLTokenizeString2(osStartTime, " ", CSLT_HONOURSTRINGS));
     365           0 :     if (!oStartTimeList.size())
     366           0 :         return false;
     367           0 :     m_oStartTimePoint = getTimePoint(oStartTimeList[0]);
     368             :     CPLStringList oEndTimeList(
     369           0 :         CSLTokenizeString2(osEndTime, " ", CSLT_HONOURSTRINGS));
     370           0 :     if (!oEndTimeList.size())
     371           0 :         return false;
     372           0 :     m_oStopTimePoint = getTimePoint(oEndTimeList[0]);
     373             :     m_oAzimuthList.Assign(
     374           0 :         CSLTokenizeString2(osParseAzimuthLUT, " ", CSLT_HONOURSTRINGS));
     375             :     CPLStringList oLUTList(
     376           0 :         CSLTokenizeString2(osParseLUT, " ", CSLT_HONOURSTRINGS));
     377             :     CPLStringList oPixelList(
     378           0 :         CSLTokenizeString2(osParsePixelLUT, " ", CSLT_HONOURSTRINGS));
     379             :     CPLStringList oLineNoList(
     380           0 :         CSLTokenizeString2(osParseLineNoLUT, " ", CSLT_HONOURSTRINGS));
     381             : 
     382           0 :     m_anPixelLUT.resize(m_nNumPixels);
     383           0 :     for (int i = 0; i < m_nNumPixels; i++)
     384           0 :         m_anPixelLUT[i] = static_cast<int>(CPLAtof(oPixelList[i]));
     385             : 
     386           0 :     int nTableSize = oLUTList.size();
     387           0 :     m_afTable.resize(nTableSize);
     388           0 :     for (int i = 0; i < nTableSize; i++)
     389           0 :         m_afTable[i] = static_cast<float>(CPLAtof(oLUTList[i]));
     390             : 
     391           0 :     int nLineListSize = oLineNoList.size();
     392           0 :     m_anLineLUT.resize(nLineListSize);
     393           0 :     for (int i = 0; i < nLineListSize; i++)
     394           0 :         m_anLineLUT[i] = static_cast<int>(CPLAtof(oLineNoList[i]));
     395             : 
     396           0 :     return true;
     397             : }
     398             : 
     399             : /************************************************************************/
     400             : /*                             IReadBlock()                             */
     401             : /************************************************************************/
     402             : 
     403           0 : CPLErr SAFECalibratedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     404             :                                             void *pImage)
     405             : 
     406             : {
     407             :     /* -------------------------------------------------------------------- */
     408             :     /*      If the last strip is partial, we need to avoid                  */
     409             :     /*      over-requesting.  We also need to initialize the extra part     */
     410             :     /*      of the block to zero.                                           */
     411             :     /* -------------------------------------------------------------------- */
     412           0 :     const int nXOff = nBlockXOff * nBlockXSize;
     413           0 :     const int nRequestXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
     414           0 :     const int nYOff = nBlockYOff * nBlockYSize;
     415           0 :     const int nRequestYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
     416             : 
     417           0 :     if (nRequestYSize < nBlockYSize)
     418             :     {
     419           0 :         memset(pImage, 0,
     420           0 :                static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
     421           0 :                    nBlockXSize * nBlockYSize);
     422             :     }
     423             : 
     424             :     /*-------------------------------------------------------------------- */
     425             :     /*      If the input imagery is tiled, also need to avoid over-        */
     426             :     /*      requesting in the X-direction.                                 */
     427             :     /* ------------------------------------------------------------------- */
     428           0 :     if (nRequestXSize < nBlockXSize)
     429             :     {
     430           0 :         memset(pImage, 0,
     431           0 :                static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
     432           0 :                    nBlockXSize * nBlockYSize);
     433             :     }
     434             : 
     435             :     // Check LUT values and fail before reading
     436           0 :     int nLineCalVecIdx = getCalibrationVectorIndex(nBlockYOff);
     437           0 :     const char *pszVec0Str = m_oAzimuthList[nLineCalVecIdx];
     438           0 :     const char *pszVec1Str = m_oAzimuthList[nLineCalVecIdx + 1];
     439           0 :     if (((m_eInputDataType == GDT_CInt16) || (m_eInputDataType == GDT_Int16)) &&
     440           0 :         (!pszVec0Str || !pszVec1Str))
     441           0 :         return CE_Failure;
     442             : 
     443             :     TimePoint azTime = getazTime(m_oStartTimePoint, m_oStopTimePoint,
     444           0 :                                  nRasterYSize, nBlockYOff);
     445           0 :     TimePoint oVec0Time = getTimePoint(pszVec0Str);
     446           0 :     TimePoint oVec1Time = getTimePoint(pszVec1Str);
     447             :     double dfMuY =
     448           0 :         getTimeDiff(oVec0Time, azTime) / getTimeDiff(oVec0Time, oVec1Time);
     449             : 
     450           0 :     if (m_eInputDataType == GDT_CInt16)
     451             :     {
     452           0 :         CPLErr eErr = CE_None;
     453             :         GInt16 *pnImageTmp = static_cast<GInt16 *>(
     454           0 :             VSI_MALLOC3_VERBOSE(2 * sizeof(int16_t), nBlockXSize, nBlockYSize));
     455           0 :         if (!pnImageTmp)
     456           0 :             return CE_Failure;
     457             : 
     458           0 :         if (poBandDataset->GetRasterCount() == 2)
     459             :         {
     460           0 :             eErr = poBandDataset->RasterIO(
     461           0 :                 GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
     462             :                 nRequestXSize, nRequestYSize, pnImageTmp, nRequestXSize,
     463           0 :                 nRequestYSize, GDT_Int16, 2, nullptr, 4, nBlockXSize * 4, 2,
     464             :                 nullptr);
     465             :         }
     466             :         /* --------------------------------------------------------------------
     467             :          */
     468             :         /*      File has one sample marked as sample format void, a 32bits. */
     469             :         /* --------------------------------------------------------------------
     470             :          */
     471           0 :         else if (poBandDataset->GetRasterCount() == 1)
     472             :         {
     473           0 :             eErr = poBandDataset->RasterIO(
     474           0 :                 GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
     475             :                 nRequestXSize, nRequestYSize, pnImageTmp, nRequestXSize,
     476           0 :                 nRequestYSize, GDT_CInt16, 1, nullptr, 4, nBlockXSize * 4, 0,
     477             :                 nullptr);
     478             :         }
     479             : 
     480             :         // Interpolation of LUT Value
     481             : #ifdef USE_OMP
     482             :         omp_set_num_threads(GetNumThreadsToUse());
     483             : #pragma omp parallel
     484             : #endif
     485           0 :         for (int i = 0; i < nBlockYSize; i++)
     486             :         {
     487             : #ifdef USE_OMP
     488             : #pragma omp for nowait
     489             : #endif
     490           0 :             for (int j = 0; j < nBlockXSize; j++)
     491             :             {
     492           0 :                 int nPixOff = (2 * (i * nBlockXSize)) + (j * 2);
     493           0 :                 int nOutPixOff = (i * nBlockXSize) + j;
     494           0 :                 int nPixelCalvecIdx = getPixelIndex(j);
     495           0 :                 double dfMuX = (double)(j - m_anPixelLUT[nPixelCalvecIdx]) /
     496           0 :                                (double)(m_anPixelLUT[nPixelCalvecIdx + 1] -
     497           0 :                                         m_anPixelLUT[nPixelCalvecIdx]);
     498           0 :                 int lutIdx1 = (nLineCalVecIdx * m_nNumPixels) + nPixelCalvecIdx;
     499           0 :                 int lutIdx2 =
     500           0 :                     (nLineCalVecIdx * m_nNumPixels) + (nPixelCalvecIdx + 1);
     501           0 :                 int lutIdx3 =
     502           0 :                     ((nLineCalVecIdx + 1) * m_nNumPixels) + nPixelCalvecIdx;
     503           0 :                 int lutIdx4 = ((nLineCalVecIdx + 1) * m_nNumPixels) +
     504           0 :                               (nPixelCalvecIdx + 1);
     505             :                 double dfLutValue =
     506           0 :                     ((1 - dfMuY) * (((1 - dfMuX) * m_afTable[lutIdx1]) +
     507           0 :                                     (dfMuX * m_afTable[lutIdx2]))) +
     508           0 :                     (dfMuY * (((1 - dfMuX) * m_afTable[lutIdx3]) +
     509           0 :                               (dfMuX * m_afTable[lutIdx4])));
     510           0 :                 double dfNum = static_cast<double>(
     511           0 :                     (pnImageTmp[nPixOff] * pnImageTmp[nPixOff]) +
     512           0 :                     (pnImageTmp[nPixOff + 1] * pnImageTmp[nPixOff + 1]));
     513           0 :                 double dfCalibValue = dfNum / (dfLutValue * dfLutValue);
     514           0 :                 ((float *)pImage)[nOutPixOff] = (float)dfCalibValue;
     515             :             }
     516             :         }
     517           0 :         CPLFree(pnImageTmp);
     518           0 :         return eErr;
     519             :     }
     520           0 :     else if (m_eInputDataType == GDT_UInt16)
     521             :     {
     522           0 :         CPLErr eErr = CE_None;
     523           0 :         GUInt16 *pnImageTmp = static_cast<GUInt16 *>(VSI_MALLOC3_VERBOSE(
     524             :             nBlockXSize, nBlockYSize, GDALGetDataTypeSizeBytes(GDT_UInt16)));
     525           0 :         if (!pnImageTmp)
     526           0 :             return CE_Failure;
     527           0 :         eErr = poBandDataset->RasterIO(GF_Read, nBlockXOff * nBlockXSize,
     528           0 :                                        nBlockYOff * nBlockYSize, nRequestXSize,
     529             :                                        nRequestYSize, pnImageTmp, nRequestXSize,
     530             :                                        nRequestYSize, GDT_UInt16, 1, nullptr, 2,
     531           0 :                                        nBlockXSize * 2, 0, nullptr);
     532             : 
     533             : #ifdef USE_OMP
     534             :         omp_set_num_threads(GetNumThreadsToUse());
     535             : #pragma omp parallel
     536             : #endif
     537           0 :         for (int i = 0; i < nBlockYSize; i++)
     538             :         {
     539             : #ifdef USE_OMP
     540             : #pragma omp for nowait
     541             : #endif
     542           0 :             for (int j = 0; j < nBlockXSize; j++)
     543             :             {
     544           0 :                 int nPixOff = (i * nBlockXSize) + j;
     545           0 :                 int nPixelCalvecIdx = getPixelIndex(j);
     546           0 :                 double dfMuX = (double)(j - m_anPixelLUT[nPixelCalvecIdx]) /
     547           0 :                                (double)(m_anPixelLUT[nPixelCalvecIdx + 1] -
     548           0 :                                         m_anPixelLUT[nPixelCalvecIdx]);
     549           0 :                 int lutIdx1 = (nLineCalVecIdx * m_nNumPixels) + nPixelCalvecIdx;
     550           0 :                 int lutIdx2 =
     551           0 :                     (nLineCalVecIdx * m_nNumPixels) + (nPixelCalvecIdx + 1);
     552           0 :                 int lutIdx3 =
     553           0 :                     ((nLineCalVecIdx + 1) * m_nNumPixels) + (nPixelCalvecIdx);
     554           0 :                 int lutIdx4 = ((nLineCalVecIdx + 1) * m_nNumPixels) +
     555           0 :                               (nPixelCalvecIdx + 1);
     556             :                 double dfLutValue =
     557           0 :                     ((1 - dfMuY) * (((1 - dfMuX) * m_afTable[lutIdx1]) +
     558           0 :                                     (dfMuX * m_afTable[lutIdx2]))) +
     559           0 :                     (dfMuY * (((1 - dfMuX) * m_afTable[lutIdx3]) +
     560           0 :                               (dfMuX * m_afTable[lutIdx4])));
     561           0 :                 double dfCalibValue =
     562           0 :                     (double)(pnImageTmp[nPixOff] * pnImageTmp[nPixOff]) /
     563           0 :                     (dfLutValue * dfLutValue);
     564           0 :                 ((float *)pImage)[nPixOff] = (float)dfCalibValue;
     565             :             }
     566             :         }
     567           0 :         CPLFree(pnImageTmp);
     568           0 :         return eErr;
     569             :     }
     570           0 :     else if (eDataType == GDT_UInt8)  // Check if this is required.
     571           0 :         return poBandDataset->RasterIO(
     572           0 :             GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
     573             :             nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
     574           0 :             GDT_UInt8, 1, nullptr, 1, nBlockXSize, 0, nullptr);
     575             : 
     576           0 :     CPLAssert(false);
     577             :     return CE_Failure;
     578             : }
     579             : 
     580             : /************************************************************************/
     581             : /* ==================================================================== */
     582             : /*                              SAFEDataset                              */
     583             : /* ==================================================================== */
     584             : /************************************************************************/
     585             : 
     586             : /************************************************************************/
     587             : /*                            SAFEDataset()                             */
     588             : /************************************************************************/
     589             : 
     590          11 : SAFEDataset::SAFEDataset()
     591             : {
     592          11 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     593          11 : }
     594             : 
     595             : /************************************************************************/
     596             : /*                            ~SAFEDataset()                            */
     597             : /************************************************************************/
     598             : 
     599          22 : SAFEDataset::~SAFEDataset()
     600             : 
     601             : {
     602          11 :     SAFEDataset::FlushCache(true);
     603             : 
     604          11 :     if (nGCPCount > 0)
     605             :     {
     606          11 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
     607          11 :         CPLFree(pasGCPList);
     608             :     }
     609             : 
     610          11 :     SAFEDataset::CloseDependentDatasets();
     611             : 
     612          11 :     CSLDestroy(papszSubDatasets);
     613          11 :     CSLDestroy(papszExtraFiles);
     614          22 : }
     615             : 
     616             : /************************************************************************/
     617             : /*                       CloseDependentDatasets()                       */
     618             : /************************************************************************/
     619             : 
     620          11 : int SAFEDataset::CloseDependentDatasets()
     621             : {
     622          11 :     int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
     623             : 
     624          11 :     if (nBands != 0)
     625          11 :         bHasDroppedRef = TRUE;
     626             : 
     627          28 :     for (int iBand = 0; iBand < nBands; iBand++)
     628             :     {
     629          17 :         delete papoBands[iBand];
     630             :     }
     631          11 :     nBands = 0;
     632             : 
     633          11 :     return bHasDroppedRef;
     634             : }
     635             : 
     636             : /************************************************************************/
     637             : /*                         GetMetaDataObject()                          */
     638             : /************************************************************************/
     639             : 
     640             : const CPLXMLNode *
     641         121 : SAFEDataset::GetMetaDataObject(const CPLXMLNode *psMetaDataObjects,
     642             :                                const char *metadataObjectId)
     643             : {
     644             :     /* -------------------------------------------------------------------- */
     645             :     /*      Look for DataObject Element by ID.                              */
     646             :     /* -------------------------------------------------------------------- */
     647        1848 :     for (const CPLXMLNode *psMDO = psMetaDataObjects->psChild; psMDO != nullptr;
     648        1727 :          psMDO = psMDO->psNext)
     649             :     {
     650        1848 :         if (psMDO->eType != CXT_Element ||
     651        1848 :             !(EQUAL(psMDO->pszValue, "metadataObject")))
     652             :         {
     653           0 :             continue;
     654             :         }
     655             : 
     656        1848 :         const char *pszElementID = CPLGetXMLValue(psMDO, "ID", "");
     657             : 
     658        1848 :         if (EQUAL(pszElementID, metadataObjectId))
     659             :         {
     660         121 :             return psMDO;
     661             :         }
     662             :     }
     663             : 
     664           0 :     CPLError(CE_Warning, CPLE_AppDefined, "MetadataObject not found with ID=%s",
     665             :              metadataObjectId);
     666             : 
     667           0 :     return nullptr;
     668             : }
     669             : 
     670             : /************************************************************************/
     671             : /*                           GetDataObject()                            */
     672             : /************************************************************************/
     673             : 
     674          88 : const CPLXMLNode *SAFEDataset::GetDataObject(const CPLXMLNode *psDataObjects,
     675             :                                              const char *dataObjectId)
     676             : {
     677             :     /* -------------------------------------------------------------------- */
     678             :     /*      Look for DataObject Element by ID.                              */
     679             :     /* -------------------------------------------------------------------- */
     680         836 :     for (const CPLXMLNode *psDO = psDataObjects->psChild; psDO != nullptr;
     681         748 :          psDO = psDO->psNext)
     682             :     {
     683         836 :         if (psDO->eType != CXT_Element ||
     684         836 :             !(EQUAL(psDO->pszValue, "dataObject")))
     685             :         {
     686           0 :             continue;
     687             :         }
     688             : 
     689         836 :         const char *pszElementID = CPLGetXMLValue(psDO, "ID", "");
     690             : 
     691         836 :         if (EQUAL(pszElementID, dataObjectId))
     692             :         {
     693          88 :             return psDO;
     694             :         }
     695             :     }
     696             : 
     697           0 :     CPLError(CE_Warning, CPLE_AppDefined, "DataObject not found with ID=%s",
     698             :              dataObjectId);
     699             : 
     700           0 :     return nullptr;
     701             : }
     702             : 
     703             : const CPLXMLNode *
     704          66 : SAFEDataset::GetDataObject(const CPLXMLNode *psMetaDataObjects,
     705             :                            const CPLXMLNode *psDataObjects,
     706             :                            const char *metadataObjectId)
     707             : {
     708             :     /* -------------------------------------------------------------------- */
     709             :     /*      Look for MetadataObject Element by ID.                          */
     710             :     /* -------------------------------------------------------------------- */
     711             :     const CPLXMLNode *psMDO =
     712          66 :         SAFEDataset::GetMetaDataObject(psMetaDataObjects, metadataObjectId);
     713             : 
     714          66 :     if (psMDO != nullptr)
     715             :     {
     716             :         const char *dataObjectId =
     717          66 :             CPLGetXMLValue(psMDO, "dataObjectPointer.dataObjectID", "");
     718          66 :         if (*dataObjectId != '\0')
     719             :         {
     720          66 :             return SAFEDataset::GetDataObject(psDataObjects, dataObjectId);
     721             :         }
     722             :     }
     723             : 
     724           0 :     CPLError(CE_Warning, CPLE_AppDefined, "DataObject not found with MetaID=%s",
     725             :              metadataObjectId);
     726             : 
     727           0 :     return nullptr;
     728             : }
     729             : 
     730             : /************************************************************************/
     731             : /*                            GetFileList()                             */
     732             : /************************************************************************/
     733             : 
     734           5 : char **SAFEDataset::GetFileList()
     735             : 
     736             : {
     737           5 :     char **papszFileList = GDALPamDataset::GetFileList();
     738             : 
     739           5 :     papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
     740             : 
     741           5 :     return papszFileList;
     742             : }
     743             : 
     744             : /************************************************************************/
     745             : /*                              Identify()                              */
     746             : /************************************************************************/
     747             : 
     748       65686 : int SAFEDataset::Identify(GDALOpenInfo *poOpenInfo)
     749             : {
     750             :     /* Check for the case where we're trying to read the calibrated data: */
     751       65686 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL1_CALIB:"))
     752             :     {
     753          12 :         return TRUE;
     754             :     }
     755             : 
     756             :     /* Check for directory access when there is a manifest.safe file in the
     757             :        directory. */
     758       65674 :     if (poOpenInfo->bIsDirectory)
     759             :     {
     760             :         VSIStatBufL sStat;
     761           0 :         CPLString osMDFilename = CPLFormCIFilenameSafe(
     762        1288 :             poOpenInfo->pszFilename, "manifest.safe", nullptr);
     763             : 
     764         644 :         if (VSIStatL(osMDFilename, &sStat) == 0 && VSI_ISREG(sStat.st_mode))
     765             :         {
     766           6 :             GDALOpenInfo oOpenInfo(osMDFilename, GA_ReadOnly, nullptr);
     767           3 :             return Identify(&oOpenInfo);
     768             :         }
     769         641 :         return FALSE;
     770             :     }
     771             : 
     772             :     /* otherwise, do our normal stuff */
     773       65030 :     if (!EQUAL(CPLGetFilename(poOpenInfo->pszFilename), "manifest.safe"))
     774       65017 :         return FALSE;
     775             : 
     776          13 :     if (poOpenInfo->nHeaderBytes < 100)
     777           0 :         return FALSE;
     778             : 
     779          13 :     const char *pszHeader =
     780             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
     781          13 :     if (!strstr(pszHeader, "<xfdu:XFDU"))
     782           0 :         return FALSE;
     783             : 
     784             :     // This driver doesn't handle Sentinel-2 or RCM (RADARSAT Constellation Mission) data
     785          13 :     if (strstr(pszHeader, "sentinel-2") ||
     786          13 :         strstr(pszHeader, "rcm_prod_manifest.xsd"))
     787           1 :         return FALSE;
     788             : 
     789          12 :     return TRUE;
     790             : }
     791             : 
     792             : /************************************************************************/
     793             : /*                                Open()                                */
     794             : /************************************************************************/
     795             : 
     796          12 : GDALDataset *SAFEDataset::Open(GDALOpenInfo *poOpenInfo)
     797             : 
     798             : {
     799             : 
     800             :     // Is this a SENTINEL-1 manifest.safe definition?
     801          12 :     if (!SAFEDataset::Identify(poOpenInfo))
     802             :     {
     803           0 :         return nullptr;
     804             :     }
     805             : 
     806             :     /* -------------------------------------------------------------------- */
     807             :     /*        Get subdataset information, if relevant                       */
     808             :     /* -------------------------------------------------------------------- */
     809          24 :     CPLString osMDFilename;
     810          12 :     bool bIsSubDS = false;
     811          12 :     bool bIsSLC = false;
     812             :     // Subdataset 1st level selection (ex: for swath selection)
     813          24 :     CPLString osSelectedSubDS1;
     814             :     // Subdataset 2nd level selection (ex: for polarization selection)
     815          24 :     CPLString osSelectedSubDS2;
     816          24 :     CPLString osSelectedSubDS3;
     817             :     // 0 for SIGMA , 1 for BETA, 2 for GAMMA and # for UNCALIB dataset
     818          12 :     SAFECalibratedRasterBand::CalibrationType eCalibrationType =
     819             :         SAFECalibratedRasterBand::SIGMA_NOUGHT;
     820          12 :     bool bCalibrated = false;
     821             : 
     822             :     // 0 for amplitude, 1 for complex (2 band : I , Q) and 2 for INTENSITY
     823             :     typedef enum
     824             :     {
     825             :         UNKNOWN = -1,
     826             :         AMPLITUDE,
     827             :         COMPLEX,
     828             :         INTENSITY
     829             :     } RequestDataType;
     830             : 
     831          12 :     RequestDataType eRequestType = UNKNOWN;
     832             :     // Calibration Information selection
     833          24 :     CPLString osSubdatasetName;
     834             : 
     835          12 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL1_CALIB:"))
     836             :     {
     837           6 :         bIsSubDS = true;
     838           6 :         osMDFilename = poOpenInfo->pszFilename + strlen("SENTINEL1_CALIB:");
     839           6 :         const char *pszSelectionCalib = strchr(osMDFilename.c_str(), ':');
     840          12 :         if (pszSelectionCalib == nullptr ||
     841           6 :             pszSelectionCalib == osMDFilename.c_str())
     842             :         {
     843           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     844             :                      "Invalid syntax for SENTINEL1_CALIB:");
     845           1 :             return nullptr;
     846             :         }
     847             : 
     848           6 :         CPLString osCalibrationValue = osMDFilename;
     849           6 :         osCalibrationValue.resize(pszSelectionCalib - osMDFilename.c_str());
     850           6 :         osMDFilename = pszSelectionCalib + strlen(":");
     851           6 :         if (EQUAL(osCalibrationValue.c_str(), "UNCALIB"))
     852             :         {
     853           3 :             bCalibrated = false;
     854             :         }
     855           3 :         else if (EQUAL(osCalibrationValue.c_str(), "SIGMA0"))
     856             :         {
     857           3 :             bCalibrated = true;
     858           3 :             eCalibrationType = SAFECalibratedRasterBand::SIGMA_NOUGHT;
     859             :         }
     860           0 :         else if (EQUAL(osCalibrationValue.c_str(), "BETA0"))
     861             :         {
     862           0 :             bCalibrated = true;
     863           0 :             eCalibrationType = SAFECalibratedRasterBand::BETA_NOUGHT;
     864             :         }
     865           0 :         else if (EQUAL(osCalibrationValue.c_str(), "GAMMA"))
     866             :         {
     867           0 :             bCalibrated = true;
     868           0 :             eCalibrationType = SAFECalibratedRasterBand::GAMMA;
     869             :         }
     870             :         else
     871             :         {
     872           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     873             :                      "Invalid syntax for SENTINEL1_CALIB:");
     874           0 :             return nullptr;
     875             :         }
     876             : 
     877           6 :         const auto nSelectionUnitPos = osMDFilename.rfind(':');
     878           6 :         if (nSelectionUnitPos == std::string::npos || nSelectionUnitPos == 0)
     879             :         {
     880           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     881             :                      "Invalid syntax for SENTINEL1_CALIB:");
     882           0 :             return nullptr;
     883             :         }
     884             : 
     885           6 :         CPLString osUnitValue = osMDFilename.substr(nSelectionUnitPos + 1);
     886           6 :         osMDFilename.resize(nSelectionUnitPos);
     887           6 :         if (EQUAL(osUnitValue.c_str(), "AMPLITUDE"))
     888           3 :             eRequestType = AMPLITUDE;
     889           3 :         else if (EQUAL(osUnitValue.c_str(), "COMPLEX"))
     890           0 :             eRequestType = COMPLEX;
     891           3 :         else if (EQUAL(osUnitValue.c_str(), "INTENSITY"))
     892           2 :             eRequestType = INTENSITY;
     893             :         else
     894             :         {
     895           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     896             :                      "Invalid syntax for SENTINEL1_CALIB:");
     897           1 :             return nullptr;
     898             :         }
     899             : 
     900           5 :         const auto nSelection1Pos = osMDFilename.rfind(':');
     901           5 :         if (nSelection1Pos == std::string::npos || nSelection1Pos == 0)
     902             :         {
     903           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     904             :                      "Invalid syntax for SENTINEL1_CALIB:");
     905           0 :             return nullptr;
     906             :         }
     907           5 :         osSelectedSubDS1 = osMDFilename.substr(nSelection1Pos + 1);
     908           5 :         osMDFilename.resize(nSelection1Pos);
     909             : 
     910           5 :         const auto nSelection2Pos = osSelectedSubDS1.find('_');
     911           5 :         if (nSelection2Pos != std::string::npos && nSelection2Pos != 0)
     912             :         {
     913           4 :             osSelectedSubDS2 = osSelectedSubDS1.substr(nSelection2Pos + 1);
     914           4 :             osSelectedSubDS1.resize(nSelection2Pos);
     915           4 :             const auto nSelection3Pos = osSelectedSubDS2.find('_');
     916           4 :             if (nSelection3Pos != std::string::npos && nSelection3Pos != 0)
     917             :             {
     918           2 :                 osSelectedSubDS3 = osSelectedSubDS2.substr(nSelection3Pos + 1);
     919           2 :                 osSelectedSubDS2.resize(nSelection3Pos);
     920             :             }
     921             :         }
     922             : 
     923             :         // update directory check:
     924             :         VSIStatBufL sStat;
     925           5 :         if (VSIStatL(osMDFilename.c_str(), &sStat) == 0)
     926           5 :             poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode);
     927           5 :         if (!bCalibrated)
     928           3 :             osSubdatasetName = "UNCALIB";
     929           2 :         else if (eCalibrationType == SAFECalibratedRasterBand::SIGMA_NOUGHT)
     930           2 :             osSubdatasetName = "SIGMA0";
     931           0 :         else if (eCalibrationType == SAFECalibratedRasterBand::BETA_NOUGHT)
     932           0 :             osSubdatasetName = "BETA0";
     933           0 :         else if (eCalibrationType == SAFECalibratedRasterBand::GAMMA)
     934           0 :             osSubdatasetName = "GAMMA";
     935           5 :         osSubdatasetName += ":";
     936           5 :         if (!osUnitValue.empty())
     937             :         {
     938           5 :             osSubdatasetName += osUnitValue;
     939           5 :             osSubdatasetName += ":";
     940             :         }
     941           5 :         if (!osSelectedSubDS1.empty())
     942             :         {
     943           5 :             osSubdatasetName += osSelectedSubDS1;
     944           5 :             osSubdatasetName += ":";
     945             :         }
     946           5 :         if (!osSelectedSubDS2.empty())
     947             :         {
     948           4 :             osSubdatasetName += osSelectedSubDS2;
     949           4 :             osSubdatasetName += ":";
     950             :         }
     951           5 :         if (!osSelectedSubDS3.empty())
     952             :         {
     953           2 :             osSubdatasetName += osSelectedSubDS3;
     954           2 :             osSubdatasetName += ":";
     955             :         }
     956           5 :         if (!osSubdatasetName.empty())
     957             :         {
     958           5 :             if (osSubdatasetName.back() == ':')
     959           5 :                 osSubdatasetName.pop_back();
     960             :         }
     961             :     }
     962             :     else
     963             :     {
     964           6 :         osMDFilename = poOpenInfo->pszFilename;
     965             :     }
     966             : 
     967          11 :     if (poOpenInfo->bIsDirectory)
     968             :     {
     969           4 :         osMDFilename = CPLFormCIFilenameSafe(osMDFilename.c_str(),
     970           2 :                                              "manifest.safe", nullptr);
     971             :     }
     972             : 
     973             :     /* -------------------------------------------------------------------- */
     974             :     /*      Ingest the manifest.safe file.                                  */
     975             :     /* -------------------------------------------------------------------- */
     976             : 
     977          22 :     auto psManifest = CPLXMLTreeCloser(CPLParseXMLFile(osMDFilename));
     978          11 :     if (psManifest == nullptr)
     979           0 :         return nullptr;
     980             : 
     981          22 :     CPLString osPath(CPLGetPathSafe(osMDFilename));
     982             : 
     983             :     /* -------------------------------------------------------------------- */
     984             :     /*      Confirm the requested access is supported.                      */
     985             :     /* -------------------------------------------------------------------- */
     986          11 :     if (poOpenInfo->eAccess == GA_Update)
     987             :     {
     988           0 :         ReportUpdateNotSupportedByDriver("SAFE");
     989           0 :         return nullptr;
     990             :     }
     991             : 
     992             :     /* -------------------------------------------------------------------- */
     993             :     /*      Get contentUnit parent element.                                 */
     994             :     /* -------------------------------------------------------------------- */
     995          11 :     const CPLXMLNode *psContentUnits = CPLGetXMLNode(
     996             :         psManifest.get(), "=xfdu:XFDU.informationPackageMap.xfdu:contentUnit");
     997          11 :     if (psContentUnits == nullptr)
     998             :     {
     999           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1000             :                  "Failed to find <xfdu:XFDU><informationPackageMap>"
    1001             :                  "<xfdu:contentUnit> in manifest file.");
    1002           0 :         return nullptr;
    1003             :     }
    1004             : 
    1005             :     /* -------------------------------------------------------------------- */
    1006             :     /*      Get Metadata Objects element.                                   */
    1007             :     /* -------------------------------------------------------------------- */
    1008             :     const CPLXMLNode *psMetaDataObjects =
    1009          11 :         CPLGetXMLNode(psManifest.get(), "=xfdu:XFDU.metadataSection");
    1010          11 :     if (psMetaDataObjects == nullptr)
    1011             :     {
    1012           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1013             :                  "Failed to find <xfdu:XFDU><metadataSection>"
    1014             :                  "in manifest file.");
    1015           0 :         return nullptr;
    1016             :     }
    1017             : 
    1018             :     /* -------------------------------------------------------------------- */
    1019             :     /*      Get Data Objects element.                                       */
    1020             :     /* -------------------------------------------------------------------- */
    1021             :     const CPLXMLNode *psDataObjects =
    1022          11 :         CPLGetXMLNode(psManifest.get(), "=xfdu:XFDU.dataObjectSection");
    1023          11 :     if (psDataObjects == nullptr)
    1024             :     {
    1025           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1026             :                  "Failed to find <xfdu:XFDU><dataObjectSection> in document.");
    1027           0 :         return nullptr;
    1028             :     }
    1029             : 
    1030             :     /* -------------------------------------------------------------------- */
    1031             :     /*      Create the dataset.                                             */
    1032             :     /* -------------------------------------------------------------------- */
    1033          22 :     auto poDS = std::make_unique<SAFEDataset>();
    1034             : 
    1035          11 :     poDS->psManifest = std::move(psManifest);
    1036             : 
    1037             :     /* -------------------------------------------------------------------- */
    1038             :     /*      Look for "Measurement Data Unit" contentUnit elements.          */
    1039             :     /* -------------------------------------------------------------------- */
    1040             :     // Map with all measures aggregated by swath
    1041          22 :     std::map<CPLString, std::set<CPLString>> oMapSwaths2Pols;
    1042          22 :     std::vector<CPLString> oImageNumberSwPol;
    1043          11 :     bool isWave = false;
    1044             : 
    1045          11 :     for (CPLXMLNode *psContentUnit = psContentUnits->psChild;
    1046         374 :          psContentUnit != nullptr; psContentUnit = psContentUnit->psNext)
    1047             :     {
    1048         363 :         if (psContentUnit->eType != CXT_Element ||
    1049         316 :             !(EQUAL(psContentUnit->pszValue, "xfdu:contentUnit")))
    1050             :         {
    1051          47 :             continue;
    1052             :         }
    1053             : 
    1054         316 :         const char *pszUnitType = CPLGetXMLValue(psContentUnit, "unitType", "");
    1055             : 
    1056         316 :         const char *pszAnnotation = nullptr;
    1057         316 :         const char *pszCalibration = nullptr;
    1058         316 :         const char *pszMeasurement = nullptr;
    1059             : 
    1060         316 :         if (EQUAL(pszUnitType, "Measurement Data Unit"))
    1061             :         {
    1062             :             /* Get dmdID and dataObjectID */
    1063          30 :             const char *pszDmdID = CPLGetXMLValue(psContentUnit, "dmdID", "");
    1064          30 :             const char *pszDataObjectID = CPLGetXMLValue(
    1065             :                 psContentUnit, "dataObjectPointer.dataObjectID", "");
    1066          30 :             if (*pszDataObjectID == '\0' || *pszDmdID == '\0')
    1067          13 :                 continue;
    1068             : 
    1069             :             const CPLXMLNode *psDataObject =
    1070          22 :                 SAFEDataset::GetDataObject(psDataObjects, pszDataObjectID);
    1071             : 
    1072          22 :             const char *pszRepId = CPLGetXMLValue(psDataObject, "repID", "");
    1073          22 :             if (!EQUAL(pszRepId, "s1Level1MeasurementSchema"))
    1074           0 :                 continue;
    1075             : 
    1076          22 :             pszMeasurement = CPLGetXMLValue(psDataObject,
    1077             :                                             "byteStream.fileLocation.href", "");
    1078          22 :             if (*pszMeasurement == '\0')
    1079           0 :                 continue;
    1080             : 
    1081          22 :             if (CPLHasPathTraversal(pszMeasurement))
    1082             :             {
    1083           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1084             :                          "Path traversal detected in %s", pszMeasurement);
    1085           0 :                 return nullptr;
    1086             :             }
    1087             : 
    1088             :             const CPLStringList aosTokens(CSLTokenizeString2(
    1089             :                 pszDmdID, " ",
    1090             :                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
    1091          22 :                     CSLT_STRIPENDSPACES));
    1092             : 
    1093         110 :             for (const char *pszId : aosTokens)
    1094             :             {
    1095          88 :                 if (*pszId == '\0')
    1096          22 :                     continue;
    1097             : 
    1098             :                 // Map the metadata ID to the object element
    1099          66 :                 const CPLXMLNode *psDO = SAFEDataset::GetDataObject(
    1100             :                     psMetaDataObjects, psDataObjects, pszId);
    1101          66 :                 if (psDO == nullptr)
    1102           0 :                     continue;
    1103             : 
    1104             :                 // check object type
    1105          66 :                 pszRepId = CPLGetXMLValue(psDO, "repID", "");
    1106          66 :                 if (EQUAL(pszRepId, "s1Level1ProductSchema"))
    1107             :                 {
    1108             :                     /* Get annotation filename */
    1109          22 :                     pszAnnotation = CPLGetXMLValue(
    1110             :                         psDO, "byteStream.fileLocation.href", "");
    1111          22 :                     if (*pszAnnotation == '\0')
    1112           0 :                         continue;
    1113             :                 }
    1114          44 :                 else if (EQUAL(pszRepId, "s1Level1CalibrationSchema"))
    1115             :                 {
    1116          22 :                     pszCalibration = CPLGetXMLValue(
    1117             :                         psDO, "byteStream.fileLocation.href", "");
    1118          22 :                     if (*pszCalibration == '\0')
    1119           0 :                         continue;
    1120             :                 }
    1121             :                 else
    1122             :                 {
    1123          22 :                     continue;
    1124             :                 }
    1125             :             }
    1126             : 
    1127          22 :             if (pszAnnotation == nullptr || pszCalibration == nullptr)
    1128           0 :                 continue;
    1129             : 
    1130          22 :             if (CPLHasPathTraversal(pszAnnotation))
    1131             :             {
    1132           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1133             :                          "Path traversal detected in %s", pszAnnotation);
    1134           0 :                 return nullptr;
    1135             :             }
    1136             : 
    1137          22 :             if (CPLHasPathTraversal(pszCalibration))
    1138             :             {
    1139           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1140             :                          "Path traversal detected in %s", pszCalibration);
    1141           0 :                 return nullptr;
    1142             :             }
    1143             : 
    1144             :             // open Annotation XML file
    1145             :             const CPLString osAnnotationFilePath =
    1146          22 :                 CPLFormFilenameSafe(osPath, pszAnnotation, nullptr);
    1147             :             const CPLString osCalibrationFilePath =
    1148          22 :                 CPLFormFilenameSafe(osPath, pszCalibration, nullptr);
    1149             : 
    1150             :             CPLXMLTreeCloser psAnnotation(
    1151          22 :                 CPLParseXMLFile(osAnnotationFilePath));
    1152          22 :             if (psAnnotation.get() == nullptr)
    1153           0 :                 continue;
    1154             :             CPLXMLTreeCloser psCalibration(
    1155          22 :                 CPLParseXMLFile(osCalibrationFilePath));
    1156          22 :             if (psCalibration.get() == nullptr)
    1157           0 :                 continue;
    1158             : 
    1159             :             /* --------------------------------------------------------------------
    1160             :              */
    1161             :             /*      Get overall image information. */
    1162             :             /* --------------------------------------------------------------------
    1163             :              */
    1164             :             const CPLString osProductType = CPLGetXMLValue(
    1165          22 :                 psAnnotation.get(), "=product.adsHeader.productType", "UNK");
    1166             :             const CPLString osMissionId = CPLGetXMLValue(
    1167          22 :                 psAnnotation.get(), "=product.adsHeader.missionId", "UNK");
    1168             :             const CPLString osPolarization = CPLGetXMLValue(
    1169          22 :                 psAnnotation.get(), "=product.adsHeader.polarisation", "UNK");
    1170             :             const CPLString osMode = CPLGetXMLValue(
    1171          22 :                 psAnnotation.get(), "=product.adsHeader.mode", "UNK");
    1172             :             const CPLString osSwath = CPLGetXMLValue(
    1173          22 :                 psAnnotation.get(), "=product.adsHeader.swath", "UNK");
    1174             :             const CPLString osImageNumber = CPLGetXMLValue(
    1175          22 :                 psAnnotation.get(), "=product.adsHeader.imageNumber", "UNK");
    1176             : 
    1177          22 :             oMapSwaths2Pols[osSwath].insert(osPolarization);
    1178          22 :             oImageNumberSwPol.push_back(osImageNumber + " " + osSwath + " " +
    1179             :                                         osPolarization);
    1180          22 :             if (EQUAL(osMode.c_str(), "WV"))
    1181           6 :                 isWave = true;
    1182             : 
    1183          22 :             if (EQUAL(osProductType.c_str(), "SLC"))
    1184           6 :                 bIsSLC = true;
    1185             : 
    1186             :             // if the dataunit is amplitude or complex and there is calibration
    1187             :             // applied it's not possible as calibrated datasets are intensity.
    1188          22 :             if (eRequestType != INTENSITY && bCalibrated)
    1189           0 :                 continue;
    1190             : 
    1191          22 :             if (osSelectedSubDS1.empty())
    1192             :             {
    1193             :                 // If not subdataset was selected,
    1194             :                 // open the first one we can find.
    1195           6 :                 osSelectedSubDS1 = osSwath;
    1196             :             }
    1197             : 
    1198          22 :             if (osSelectedSubDS3.empty() && isWave)
    1199             :             {
    1200             :                 // If the selected mode is Wave mode (different file structure)
    1201             :                 // open the first vignette in the dataset.
    1202           1 :                 osSelectedSubDS3 = osImageNumber;
    1203             :             }
    1204          22 :             if (!EQUAL(osSelectedSubDS1.c_str(), osSwath.c_str()))
    1205             :             {
    1206             :                 // do not mix swath, otherwise it does not work for SLC products
    1207           3 :                 continue;
    1208             :             }
    1209             : 
    1210          25 :             if (!osSelectedSubDS2.empty() &&
    1211           6 :                 (osSelectedSubDS2.find(osPolarization) == std::string::npos))
    1212             :             {
    1213             :                 // Add only selected polarizations.
    1214           2 :                 continue;
    1215             :             }
    1216             : 
    1217          20 :             if (!osSelectedSubDS3.empty() &&
    1218           3 :                 !EQUAL(osSelectedSubDS3.c_str(), osImageNumber.c_str()))
    1219             :             {
    1220             :                 // Add only selected image number (for Wave)
    1221           0 :                 continue;
    1222             :             }
    1223             : 
    1224             :             // Changed the position of this code segment till nullptr
    1225          17 :             poDS->nRasterXSize = atoi(CPLGetXMLValue(
    1226          17 :                 psAnnotation.get(),
    1227             :                 "=product.imageAnnotation.imageInformation.numberOfSamples",
    1228             :                 "-1"));
    1229          17 :             poDS->nRasterYSize = atoi(CPLGetXMLValue(
    1230          17 :                 psAnnotation.get(),
    1231             :                 "=product.imageAnnotation.imageInformation.numberOfLines",
    1232             :                 "-1"));
    1233          17 :             if (poDS->nRasterXSize <= 1 || poDS->nRasterYSize <= 1)
    1234             :             {
    1235           0 :                 CPLError(
    1236             :                     CE_Failure, CPLE_OpenFailed,
    1237             :                     "Non-sane raster dimensions provided in manifest.safe. "
    1238             :                     "If this is a valid SENTINEL-1 scene, please contact your "
    1239             :                     "data provider for a corrected dataset.");
    1240           0 :                 return nullptr;
    1241             :             }
    1242             : 
    1243          17 :             poDS->SetMetadataItem("PRODUCT_TYPE", osProductType.c_str());
    1244          17 :             poDS->SetMetadataItem("MISSION_ID", osMissionId.c_str());
    1245          17 :             poDS->SetMetadataItem("MODE", osMode.c_str());
    1246          17 :             poDS->SetMetadataItem("SWATH", osSwath.c_str());
    1247             : 
    1248             :             /* --------------------------------------------------------------------
    1249             :              */
    1250             :             /*      Get dataType (so we can recognize complex data), and the */
    1251             :             /*      bitsPerSample. */
    1252             :             /* --------------------------------------------------------------------
    1253             :              */
    1254             : 
    1255          17 :             const char *pszDataType = CPLGetXMLValue(
    1256          17 :                 psAnnotation.get(),
    1257             :                 "=product.imageAnnotation.imageInformation.outputPixels", "");
    1258             : 
    1259             :             GDALDataType eDataType;
    1260          17 :             if (EQUAL(pszDataType, "16 bit Signed Integer"))
    1261           3 :                 eDataType = GDT_CInt16;
    1262          14 :             else if (EQUAL(pszDataType, "16 bit Unsigned Integer"))
    1263          14 :                 eDataType = GDT_UInt16;
    1264             :             else
    1265             :             {
    1266           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1267             :                          "dataType=%s: not a supported configuration.",
    1268             :                          pszDataType);
    1269           0 :                 return nullptr;
    1270             :             }
    1271             : 
    1272             :             /* Extract pixel spacing information */
    1273          17 :             const char *pszPixelSpacing = CPLGetXMLValue(
    1274          17 :                 psAnnotation.get(),
    1275             :                 "=product.imageAnnotation.imageInformation.rangePixelSpacing",
    1276             :                 "UNK");
    1277          17 :             poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing);
    1278             : 
    1279          17 :             const char *pszLineSpacing = CPLGetXMLValue(
    1280          17 :                 psAnnotation.get(),
    1281             :                 "=product.imageAnnotation.imageInformation.azimuthPixelSpacing",
    1282             :                 "UNK");
    1283          17 :             poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing);
    1284             : 
    1285             :             /* --------------------------------------------------------------------
    1286             :              */
    1287             :             /*      Form full filename (path of manifest.safe + measurement
    1288             :              * file).  */
    1289             :             /* --------------------------------------------------------------------
    1290             :              */
    1291             :             const std::string osFullFilename(
    1292          17 :                 CPLFormFilenameSafe(osPath, pszMeasurement, nullptr));
    1293             : 
    1294             :             /* --------------------------------------------------------------------
    1295             :              */
    1296             :             /*      Try and open the file. */
    1297             :             /* --------------------------------------------------------------------
    1298             :              */
    1299           0 :             std::unique_ptr<GDALDataset> poBandFile;
    1300             :             {
    1301          17 :                 CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{};
    1302          34 :                 poBandFile = std::unique_ptr<GDALDataset>(
    1303             :                     GDALDataset::Open(osFullFilename.c_str(),
    1304          17 :                                       GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
    1305             :             }
    1306             : 
    1307          17 :             if (poBandFile == nullptr)
    1308             :             {
    1309             :                 // NOP
    1310             :             }
    1311          17 :             else if (poBandFile->GetRasterCount() == 0)
    1312             :             {
    1313           0 :                 poBandFile.reset();
    1314             :             }
    1315             :             else
    1316             :             {
    1317          34 :                 poDS->papszExtraFiles =
    1318          17 :                     CSLAddString(poDS->papszExtraFiles, osAnnotationFilePath);
    1319          34 :                 poDS->papszExtraFiles =
    1320          17 :                     CSLAddString(poDS->papszExtraFiles, osCalibrationFilePath);
    1321          34 :                 poDS->papszExtraFiles =
    1322          17 :                     CSLAddString(poDS->papszExtraFiles, osFullFilename.c_str());
    1323             :                 /* --------------------------------------------------------------------
    1324             :                  */
    1325             :                 /*      Collect Annotation Processing Information */
    1326             :                 /* --------------------------------------------------------------------
    1327             :                  */
    1328          17 :                 CPLXMLNode *psProcessingInfo = CPLGetXMLNode(
    1329             :                     psAnnotation.get(),
    1330             :                     "=product.imageAnnotation.processingInformation");
    1331             : 
    1332          17 :                 if (psProcessingInfo != nullptr)
    1333             :                 {
    1334          28 :                     OGRSpatialReference oLL, oPrj;
    1335             : 
    1336             :                     const char *pszEllipsoidName =
    1337          14 :                         CPLGetXMLValue(psProcessingInfo, "ellipsoidName", "");
    1338          14 :                     const double minor_axis = CPLAtof(CPLGetXMLValue(
    1339             :                         psProcessingInfo, "ellipsoidSemiMinorAxis", "0.0"));
    1340          14 :                     const double major_axis = CPLAtof(CPLGetXMLValue(
    1341             :                         psProcessingInfo, "ellipsoidSemiMajorAxis", "0.0"));
    1342             : 
    1343          14 :                     if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) ||
    1344             :                         (major_axis == 0.0))
    1345             :                     {
    1346           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1347             :                                  "Warning- incomplete"
    1348             :                                  " ellipsoid information.  Using wgs-84 "
    1349             :                                  "parameters.\n");
    1350           0 :                         oLL.SetWellKnownGeogCS("WGS84");
    1351           0 :                         oPrj.SetWellKnownGeogCS("WGS84");
    1352             :                     }
    1353          14 :                     else if (EQUAL(pszEllipsoidName, "WGS84"))
    1354             :                     {
    1355          14 :                         oLL.SetWellKnownGeogCS("WGS84");
    1356          14 :                         oPrj.SetWellKnownGeogCS("WGS84");
    1357             :                     }
    1358             :                     else
    1359             :                     {
    1360           0 :                         const double inv_flattening =
    1361           0 :                             major_axis / (major_axis - minor_axis);
    1362           0 :                         oLL.SetGeogCS("", "", pszEllipsoidName, major_axis,
    1363             :                                       inv_flattening);
    1364           0 :                         oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis,
    1365             :                                        inv_flattening);
    1366             :                     }
    1367             : 
    1368          14 :                     oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1369          14 :                     poDS->m_oGCPSRS = std::move(oLL);
    1370             :                 }
    1371             : 
    1372             :                 /* --------------------------------------------------------------------
    1373             :                  */
    1374             :                 /*      Collect GCPs. */
    1375             :                 /* --------------------------------------------------------------------
    1376             :                  */
    1377          17 :                 CPLXMLNode *psGeoGrid = CPLGetXMLNode(
    1378             :                     psAnnotation.get(),
    1379             :                     "=product.geolocationGrid.geolocationGridPointList");
    1380             : 
    1381          17 :                 if (psGeoGrid != nullptr)
    1382             :                 {
    1383          17 :                     if (poDS->nGCPCount > 0)
    1384             :                     {
    1385           6 :                         GDALDeinitGCPs(poDS->nGCPCount, poDS->pasGCPList);
    1386           6 :                         CPLFree(poDS->pasGCPList);
    1387             :                     }
    1388             : 
    1389             :                     /* count GCPs */
    1390          17 :                     poDS->nGCPCount = 0;
    1391             : 
    1392          17 :                     for (CPLXMLNode *psNode = psGeoGrid->psChild;
    1393          52 :                          psNode != nullptr; psNode = psNode->psNext)
    1394             :                     {
    1395          35 :                         if (EQUAL(psNode->pszValue, "geolocationGridPoint"))
    1396          18 :                             poDS->nGCPCount++;
    1397             :                     }
    1398             : 
    1399          17 :                     if (poDS->nGCPCount > 0)
    1400             :                     {
    1401          34 :                         poDS->pasGCPList = static_cast<GDAL_GCP *>(
    1402          17 :                             CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount));
    1403             : 
    1404          17 :                         poDS->nGCPCount = 0;
    1405             : 
    1406          17 :                         for (CPLXMLNode *psNode = psGeoGrid->psChild;
    1407          52 :                              psNode != nullptr; psNode = psNode->psNext)
    1408             :                         {
    1409             :                             GDAL_GCP *psGCP =
    1410          35 :                                 poDS->pasGCPList + poDS->nGCPCount;
    1411             : 
    1412          35 :                             if (!EQUAL(psNode->pszValue,
    1413             :                                        "geolocationGridPoint"))
    1414          17 :                                 continue;
    1415             : 
    1416          18 :                             poDS->nGCPCount++;
    1417             : 
    1418             :                             char szID[32];
    1419          18 :                             snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount);
    1420          18 :                             psGCP->pszId = CPLStrdup(szID);
    1421          18 :                             psGCP->pszInfo = CPLStrdup("");
    1422          18 :                             psGCP->dfGCPPixel =
    1423          18 :                                 CPLAtof(CPLGetXMLValue(psNode, "pixel", "0"));
    1424          18 :                             psGCP->dfGCPLine =
    1425          18 :                                 CPLAtof(CPLGetXMLValue(psNode, "line", "0"));
    1426          18 :                             psGCP->dfGCPX = CPLAtof(
    1427             :                                 CPLGetXMLValue(psNode, "longitude", ""));
    1428          18 :                             psGCP->dfGCPY =
    1429          18 :                                 CPLAtof(CPLGetXMLValue(psNode, "latitude", ""));
    1430          18 :                             psGCP->dfGCPZ =
    1431          18 :                                 CPLAtof(CPLGetXMLValue(psNode, "height", ""));
    1432             :                         }
    1433             :                     }
    1434             :                 }
    1435             : 
    1436             :                 // Create bands
    1437          17 :                 if (EQUAL(osProductType.c_str(), "SLC"))
    1438             :                 {
    1439             :                     // only add bands if no subDS or uncalibrated subdataset
    1440             :                     // with complex data. (Calibrated will always be intensity
    1441             :                     // only)
    1442           3 :                     if (!bCalibrated &&
    1443           0 :                         (eRequestType == UNKNOWN || eRequestType == COMPLEX))
    1444             :                     {
    1445           1 :                         poBandFile->MarkAsShared();
    1446             :                         SAFESLCRasterBand *poBand0 = new SAFESLCRasterBand(
    1447           1 :                             poDS.get(), eDataType, osSwath, osPolarization,
    1448           1 :                             std::move(poBandFile), SAFESLCRasterBand::COMPLEX);
    1449           1 :                         poDS->SetBand(poDS->GetRasterCount() + 1, poBand0);
    1450             :                     }
    1451           2 :                     else if (eRequestType == INTENSITY)  // Intensity
    1452             :                     {
    1453             :                         SAFESLCRasterBand *poBand1 = new SAFESLCRasterBand(
    1454           2 :                             poDS.get(), eDataType, osSwath, osPolarization,
    1455           2 :                             std::move(poBandFile),
    1456           2 :                             SAFESLCRasterBand::INTENSITY);
    1457           2 :                         poDS->SetBand(poDS->GetRasterCount() + 1, poBand1);
    1458             :                     }
    1459             :                 }
    1460          14 :                 else if (!bCalibrated &&
    1461           4 :                          (eRequestType == UNKNOWN || eRequestType == AMPLITUDE))
    1462             :                 {
    1463             :                     SAFERasterBand *poBand = new SAFERasterBand(
    1464          14 :                         poDS.get(), eDataType, osSwath, osPolarization,
    1465          14 :                         std::move(poBandFile));
    1466          14 :                     poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
    1467             :                 }
    1468           0 :                 else if (bCalibrated &&
    1469           0 :                          (eRequestType == UNKNOWN || eRequestType == COMPLEX))
    1470             :                 {
    1471             :                     auto poBand = std::make_unique<SAFECalibratedRasterBand>(
    1472           0 :                         poDS.get(), eDataType, osSwath, osPolarization,
    1473           0 :                         std::move(poBandFile), osCalibrationFilePath,
    1474           0 :                         eCalibrationType);
    1475           0 :                     if (!poBand->ReadLUT())
    1476             :                     {
    1477           0 :                         CPLError(CE_Failure, CPLE_OpenFailed,
    1478             :                                  "Reading calibration LUT(s) failed: %s.",
    1479             :                                  osCalibrationFilePath.c_str());
    1480           0 :                         return nullptr;
    1481             :                     }
    1482           0 :                     poDS->SetBand(poDS->GetRasterCount() + 1,
    1483           0 :                                   std::move(poBand));
    1484             :                 }
    1485             :             }
    1486             :         }
    1487             :     }
    1488             : 
    1489             :     // loop through all Swath/pols to add subdatasets
    1490          11 :     if (!bIsSubDS)
    1491             :     {
    1492             :         const CPLString aosCalibrationValues[4] = {"SIGMA0", "BETA0", "GAMMA",
    1493          36 :                                                    "UNCALIB"};
    1494             :         const CPLString aosDataUnitValues[3] = {"AMPLITUDE", "COMPLEX",
    1495          30 :                                                 "INTENSITY"};
    1496           6 :         if (!isWave)
    1497             :         {
    1498          10 :             for (const auto &iterSwath : oMapSwaths2Pols)
    1499             :             {
    1500          10 :                 CPLString osSubDS1 = iterSwath.first;
    1501          10 :                 CPLString osSubDS2;
    1502             : 
    1503          15 :                 for (const auto &pol : iterSwath.second)
    1504             :                 {
    1505          10 :                     if (!osSubDS2.empty())
    1506           5 :                         osSubDS2 += "+";
    1507          10 :                     osSubDS2 += pol;
    1508             :                     // Create single band or multiband complex SubDataset
    1509          10 :                     int i = 0;
    1510          10 :                     if (bIsSLC)
    1511             :                     {
    1512           0 :                         for (i = 0; i < 3; i++)
    1513             :                         {
    1514           0 :                             CPLString osCalibTemp = aosCalibrationValues[i];
    1515           0 :                             poDS->AddSubDataset(
    1516             :                                 CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s_%s:%s",
    1517             :                                            osCalibTemp.c_str(),
    1518             :                                            osMDFilename.c_str(),
    1519             :                                            osSubDS1.c_str(), pol.c_str(),
    1520             :                                            aosDataUnitValues[2].c_str()),
    1521             :                                 CPLSPrintf("Single band with %s swath and %s "
    1522             :                                            "polarization and %s calibration",
    1523             :                                            osSubDS1.c_str(), pol.c_str(),
    1524             :                                            osCalibTemp.c_str()));
    1525             :                         }
    1526             : 
    1527           0 :                         CPLString osCalibTemp = aosCalibrationValues[i];
    1528           0 :                         poDS->AddSubDataset(
    1529             :                             CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s_%s:%s",
    1530             :                                        osCalibTemp.c_str(),
    1531             :                                        osMDFilename.c_str(), osSubDS1.c_str(),
    1532             :                                        pol.c_str(),
    1533             :                                        aosDataUnitValues[1].c_str()),
    1534             :                             CPLSPrintf("Single band with %s swath and %s "
    1535             :                                        "polarization and %s calibration",
    1536             :                                        osSubDS1.c_str(), pol.c_str(),
    1537             :                                        osCalibTemp.c_str()));
    1538           0 :                         poDS->AddSubDataset(
    1539             :                             CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s_%s:%s",
    1540             :                                        osCalibTemp.c_str(),
    1541             :                                        osMDFilename.c_str(), osSubDS1.c_str(),
    1542             :                                        pol.c_str(),
    1543             :                                        aosDataUnitValues[2].c_str()),
    1544             :                             CPLSPrintf("Single band with %s swath and %s "
    1545             :                                        "polarization and %s calibration",
    1546             :                                        osSubDS1.c_str(), pol.c_str(),
    1547             :                                        osCalibTemp.c_str()));
    1548             :                     }
    1549             :                     else
    1550             :                     {
    1551          10 :                         i = 3;
    1552          10 :                         CPLString osCalibTemp = aosCalibrationValues[i];
    1553             : 
    1554          10 :                         poDS->AddSubDataset(
    1555             :                             CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s_%s:%s",
    1556             :                                        osCalibTemp.c_str(),
    1557             :                                        osMDFilename.c_str(), osSubDS1.c_str(),
    1558             :                                        pol.c_str(),
    1559             :                                        aosDataUnitValues[0].c_str()),
    1560             :                             CPLSPrintf("Single band with %s swath and %s "
    1561             :                                        "polarization and %s calibration",
    1562             :                                        osSubDS1.c_str(), pol.c_str(),
    1563             :                                        osCalibTemp.c_str()));
    1564             :                     }
    1565             :                 }
    1566             : 
    1567           5 :                 if (iterSwath.second.size() > 1)
    1568             :                 {
    1569             :                     // Create single band subdataset with all polarizations
    1570           5 :                     int i = 0;
    1571           5 :                     if (bIsSLC)
    1572             :                     {
    1573           0 :                         for (i = 0; i < 3; i++)
    1574             :                         {
    1575           0 :                             CPLString osCalibTemp = aosCalibrationValues[i];
    1576             : 
    1577           0 :                             poDS->AddSubDataset(
    1578             :                                 CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s:%s",
    1579             :                                            osCalibTemp.c_str(),
    1580             :                                            osMDFilename.c_str(),
    1581             :                                            osSubDS1.c_str(),
    1582             :                                            aosDataUnitValues[2].c_str()),
    1583             :                                 CPLSPrintf(
    1584             :                                     "%s swath with all polarizations (%s) as "
    1585             :                                     "bands and %s calibration",
    1586             :                                     osSubDS1.c_str(), osSubDS2.c_str(),
    1587             :                                     osCalibTemp.c_str()));
    1588             :                         }
    1589             : 
    1590           0 :                         CPLString osCalibTemp = aosCalibrationValues[i];
    1591           0 :                         poDS->AddSubDataset(
    1592             :                             CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s:%s",
    1593             :                                        osCalibTemp.c_str(),
    1594             :                                        osMDFilename.c_str(), osSubDS1.c_str(),
    1595             :                                        aosDataUnitValues[1].c_str()),
    1596             :                             CPLSPrintf(
    1597             :                                 "%s swath with all polarizations (%s) as "
    1598             :                                 "bands and %s calibration",
    1599             :                                 osSubDS1.c_str(), osSubDS2.c_str(),
    1600             :                                 osCalibTemp.c_str()));
    1601           0 :                         poDS->AddSubDataset(
    1602             :                             CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s:%s",
    1603             :                                        osCalibTemp.c_str(),
    1604             :                                        osMDFilename.c_str(), osSubDS1.c_str(),
    1605             :                                        aosDataUnitValues[2].c_str()),
    1606             :                             CPLSPrintf(
    1607             :                                 "%s swath with all polarizations (%s) as "
    1608             :                                 "bands and %s calibration",
    1609             :                                 osSubDS1.c_str(), osSubDS2.c_str(),
    1610             :                                 osCalibTemp.c_str()));
    1611             :                     }
    1612             :                     else
    1613             :                     {
    1614           5 :                         i = 3;
    1615           5 :                         CPLString osCalibTemp = aosCalibrationValues[i];
    1616           5 :                         poDS->AddSubDataset(
    1617             :                             CPLSPrintf("SENTINEL1_CALIB:%s:%s:%s:%s",
    1618             :                                        osCalibTemp.c_str(),
    1619             :                                        osMDFilename.c_str(), osSubDS1.c_str(),
    1620             :                                        aosDataUnitValues[0].c_str()),
    1621             :                             CPLSPrintf(
    1622             :                                 "%s swath with all polarizations (%s) as "
    1623             :                                 "bands and %s calibration",
    1624             :                                 osSubDS1.c_str(), osSubDS2.c_str(),
    1625             :                                 osCalibTemp.c_str()));
    1626             :                     }
    1627             :                 }
    1628             :             }
    1629             :         }
    1630             :         else
    1631             :         {
    1632           3 :             for (const CPLString &osImgSwPol : oImageNumberSwPol)
    1633             :             {
    1634           4 :                 CPLString osImgSwPolTmp = osImgSwPol;
    1635           2 :                 const char *pszImage = strchr(osImgSwPolTmp.c_str(), ' ');
    1636           4 :                 CPLString osImage, osSwath, osPolarization;
    1637           2 :                 if (pszImage != nullptr)
    1638             :                 {
    1639           2 :                     osImage = osImgSwPolTmp;
    1640           2 :                     osImage.resize(pszImage - osImgSwPolTmp.c_str());
    1641           2 :                     osImgSwPolTmp = pszImage + strlen(" ");
    1642           2 :                     const char *pszSwath = strchr(osImgSwPolTmp.c_str(), ' ');
    1643           2 :                     if (pszSwath != nullptr)
    1644             :                     {
    1645           2 :                         osSwath = osImgSwPolTmp;
    1646           2 :                         osSwath.resize(pszSwath - osImgSwPolTmp.c_str());
    1647           2 :                         osPolarization = pszSwath + strlen(" ");
    1648           2 :                         int i = 0;
    1649             : 
    1650           2 :                         if (bIsSLC)
    1651             :                         {
    1652           8 :                             for (i = 0; i < 3; i++)
    1653             :                             {
    1654           6 :                                 CPLString osCalibTemp = aosCalibrationValues[i];
    1655             : 
    1656           6 :                                 poDS->AddSubDataset(
    1657             :                                     CPLSPrintf(
    1658             :                                         "SENTINEL1_CALIB:%s:%s:%s_%s_%s:%s",
    1659             :                                         osCalibTemp.c_str(),
    1660             :                                         osMDFilename.c_str(), osSwath.c_str(),
    1661             :                                         osPolarization.c_str(), osImage.c_str(),
    1662             :                                         aosDataUnitValues[2].c_str()),
    1663             :                                     CPLSPrintf(
    1664             :                                         "Single band with %s swath and %s "
    1665             :                                         "polarization and %s calibration",
    1666             :                                         osSwath.c_str(), osPolarization.c_str(),
    1667             :                                         osCalibTemp.c_str()));
    1668             :                             }
    1669             : 
    1670           2 :                             CPLString osCalibTemp = aosCalibrationValues[i];
    1671             : 
    1672           2 :                             poDS->AddSubDataset(
    1673             :                                 CPLSPrintf(
    1674             :                                     "SENTINEL1_CALIB:%s:%s:%s_%s_%s:%s",
    1675             :                                     osCalibTemp.c_str(), osMDFilename.c_str(),
    1676             :                                     osSwath.c_str(), osPolarization.c_str(),
    1677             :                                     osImage.c_str(),
    1678             :                                     aosDataUnitValues[1].c_str()),
    1679             :                                 CPLSPrintf("Single band with %s swath and %s "
    1680             :                                            "polarization and %s calibration",
    1681             :                                            osSwath.c_str(),
    1682             :                                            osPolarization.c_str(),
    1683             :                                            osCalibTemp.c_str()));
    1684             : 
    1685           2 :                             poDS->AddSubDataset(
    1686             :                                 CPLSPrintf(
    1687             :                                     "SENTINEL1_CALIB:%s:%s:%s_%s_%s:%s",
    1688             :                                     osCalibTemp.c_str(), osMDFilename.c_str(),
    1689             :                                     osSwath.c_str(), osPolarization.c_str(),
    1690             :                                     osImage.c_str(),
    1691             :                                     aosDataUnitValues[2].c_str()),
    1692             :                                 CPLSPrintf("Single band with %s swath and %s "
    1693             :                                            "polarization and %s calibration",
    1694             :                                            osSwath.c_str(),
    1695             :                                            osPolarization.c_str(),
    1696             :                                            osCalibTemp.c_str()));
    1697             :                         }
    1698             :                         else
    1699             :                         {
    1700           0 :                             i = 3;
    1701           0 :                             CPLString osCalibTemp = aosCalibrationValues[i];
    1702             : 
    1703           0 :                             poDS->AddSubDataset(
    1704             :                                 CPLSPrintf(
    1705             :                                     "SENTINEL1_CALIB:%s:%s:%s_%s_%s:%s",
    1706             :                                     osCalibTemp.c_str(), osMDFilename.c_str(),
    1707             :                                     osSwath.c_str(), osPolarization.c_str(),
    1708             :                                     osImage.c_str(),
    1709             :                                     aosDataUnitValues[0].c_str()),
    1710             :                                 CPLSPrintf("Single band with %s swath and %s "
    1711             :                                            "polarization and %s calibration",
    1712             :                                            osSwath.c_str(),
    1713             :                                            osPolarization.c_str(),
    1714             :                                            osCalibTemp.c_str()));
    1715             :                         }
    1716             :                     }
    1717             :                 }
    1718             :             }
    1719             :         }
    1720             :     }
    1721          11 :     if (poDS->GetRasterCount() == 0)
    1722             :     {
    1723           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Measurement bands not found.");
    1724           0 :         return nullptr;
    1725             :     }
    1726             : 
    1727             :     /* -------------------------------------------------------------------- */
    1728             :     /*      Collect more metadata elements                                  */
    1729             :     /* -------------------------------------------------------------------- */
    1730             : 
    1731             :     /* -------------------------------------------------------------------- */
    1732             :     /*      Platform information                                            */
    1733             :     /* -------------------------------------------------------------------- */
    1734             :     const CPLXMLNode *psPlatformAttrs =
    1735          11 :         SAFEDataset::GetMetaDataObject(psMetaDataObjects, "platform");
    1736             : 
    1737          11 :     if (psPlatformAttrs != nullptr)
    1738             :     {
    1739             :         const char *pszItem =
    1740          11 :             CPLGetXMLValue(psPlatformAttrs,
    1741             :                            "metadataWrap.xmlData.safe:platform"
    1742             :                            ".safe:familyName",
    1743             :                            "");
    1744          11 :         poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem);
    1745             : 
    1746             :         pszItem =
    1747          11 :             CPLGetXMLValue(psPlatformAttrs,
    1748             :                            "metadataWrap.xmlData.safe:platform"
    1749             :                            ".safe:instrument.safe:familyName.abbreviation",
    1750             :                            "");
    1751          11 :         poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem);
    1752             : 
    1753          11 :         pszItem = CPLGetXMLValue(psPlatformAttrs,
    1754             :                                  "metadataWrap.xmlData.safe:platform"
    1755             :                                  ".safe:instrument.safe:extension"
    1756             :                                  ".s1sarl1:instrumentMode.s1sarl1:mode",
    1757             :                                  "UNK");
    1758          11 :         poDS->SetMetadataItem("BEAM_MODE", pszItem);
    1759             : 
    1760          11 :         pszItem = CPLGetXMLValue(psPlatformAttrs,
    1761             :                                  "metadataWrap.xmlData.safe:platform"
    1762             :                                  ".safe:instrument.safe:extension"
    1763             :                                  ".s1sarl1:instrumentMode.s1sarl1:swath",
    1764             :                                  "UNK");
    1765          11 :         poDS->SetMetadataItem("BEAM_SWATH", pszItem);
    1766             :     }
    1767             : 
    1768             :     /* -------------------------------------------------------------------- */
    1769             :     /*      Acquisition Period information                                  */
    1770             :     /* -------------------------------------------------------------------- */
    1771             :     const CPLXMLNode *psAcquisitionAttrs =
    1772          11 :         SAFEDataset::GetMetaDataObject(psMetaDataObjects, "acquisitionPeriod");
    1773             : 
    1774          11 :     if (psAcquisitionAttrs != nullptr)
    1775             :     {
    1776             :         const char *pszItem =
    1777          11 :             CPLGetXMLValue(psAcquisitionAttrs,
    1778             :                            "metadataWrap.xmlData.safe:acquisitionPeriod"
    1779             :                            ".safe:startTime",
    1780             :                            "UNK");
    1781          11 :         poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem);
    1782          11 :         pszItem = CPLGetXMLValue(psAcquisitionAttrs,
    1783             :                                  "metadataWrap.xmlData.safe:acquisitionPeriod"
    1784             :                                  ".safe:stopTime",
    1785             :                                  "UNK");
    1786          11 :         poDS->SetMetadataItem("ACQUISITION_STOP_TIME", pszItem);
    1787             :     }
    1788             : 
    1789             :     /* -------------------------------------------------------------------- */
    1790             :     /*      Processing information                                          */
    1791             :     /* -------------------------------------------------------------------- */
    1792             :     const CPLXMLNode *psProcessingAttrs =
    1793          11 :         SAFEDataset::GetMetaDataObject(psMetaDataObjects, "processing");
    1794             : 
    1795          11 :     if (psProcessingAttrs != nullptr)
    1796             :     {
    1797          11 :         const char *pszItem = CPLGetXMLValue(
    1798             :             psProcessingAttrs,
    1799             :             "metadataWrap.xmlData.safe:processing.safe:facility.name", "UNK");
    1800          11 :         poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem);
    1801             :     }
    1802             : 
    1803             :     /* -------------------------------------------------------------------- */
    1804             :     /*      Measurement Orbit Reference information                         */
    1805             :     /* -------------------------------------------------------------------- */
    1806          11 :     const CPLXMLNode *psOrbitAttrs = SAFEDataset::GetMetaDataObject(
    1807             :         psMetaDataObjects, "measurementOrbitReference");
    1808             : 
    1809          11 :     if (psOrbitAttrs != nullptr)
    1810             :     {
    1811             :         const char *pszItem =
    1812          11 :             CPLGetXMLValue(psOrbitAttrs,
    1813             :                            "metadataWrap.xmlData.safe:orbitReference"
    1814             :                            ".safe:orbitNumber",
    1815             :                            "UNK");
    1816          11 :         poDS->SetMetadataItem("ORBIT_NUMBER", pszItem);
    1817          11 :         pszItem = CPLGetXMLValue(psOrbitAttrs,
    1818             :                                  "metadataWrap.xmlData.safe:orbitReference"
    1819             :                                  ".safe:extension.s1:orbitProperties.s1:pass",
    1820             :                                  "UNK");
    1821          11 :         poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem);
    1822             :     }
    1823             : 
    1824             :     /* -------------------------------------------------------------------- */
    1825             :     /*      Footprint                                                       */
    1826             :     /* -------------------------------------------------------------------- */
    1827          11 :     const CPLXMLNode *psFrameSet = SAFEDataset::GetMetaDataObject(
    1828             :         psMetaDataObjects, "measurementFrameSet");
    1829             : 
    1830          11 :     if (psFrameSet)
    1831             :     {
    1832          11 :         const auto psFootPrint = CPLGetXMLNode(psFrameSet, "metadataWrap."
    1833             :                                                            "xmlData."
    1834             :                                                            "safe:frameSet."
    1835             :                                                            "safe:frame."
    1836             :                                                            "safe:footPrint");
    1837          11 :         if (psFootPrint)
    1838             :         {
    1839             :             const char *pszSRSName =
    1840          11 :                 CPLGetXMLValue(psFootPrint, "srsName", nullptr);
    1841             :             const char *pszCoordinates =
    1842          11 :                 CPLGetXMLValue(psFootPrint, "gml:coordinates", nullptr);
    1843          11 :             if (pszSRSName &&
    1844          11 :                 EQUAL(pszSRSName,
    1845          11 :                       "http://www.opengis.net/gml/srs/epsg.xml#4326") &&
    1846             :                 pszCoordinates)
    1847             :             {
    1848             :                 const CPLStringList aosValues(
    1849          22 :                     CSLTokenizeString2(pszCoordinates, " ,", 0));
    1850          11 :                 if (aosValues.size() == 8)
    1851             :                 {
    1852          22 :                     poDS->SetMetadataItem(
    1853             :                         "FOOTPRINT",
    1854             :                         CPLSPrintf("POLYGON((%s %s,%s %s,%s %s,%s %s, %s %s))",
    1855             :                                    aosValues[1], aosValues[0], aosValues[3],
    1856             :                                    aosValues[2], aosValues[5], aosValues[4],
    1857             :                                    aosValues[7], aosValues[6], aosValues[1],
    1858          11 :                                    aosValues[0]));
    1859             :                 }
    1860             :             }
    1861             :         }
    1862             :     }
    1863             : 
    1864             :     /* -------------------------------------------------------------------- */
    1865             :     /*      Initialize any PAM information.                                 */
    1866             :     /* -------------------------------------------------------------------- */
    1867          22 :     const CPLString osDescription = osMDFilename;
    1868             : 
    1869             :     /* -------------------------------------------------------------------- */
    1870             :     /*      Initialize any PAM information.                                 */
    1871             :     /* -------------------------------------------------------------------- */
    1872          11 :     poDS->SetDescription(osDescription);
    1873             : 
    1874          11 :     poDS->SetPhysicalFilename(osMDFilename);
    1875          11 :     if (!osSubdatasetName.empty())
    1876             :     {
    1877           5 :         poDS->SetDescription(poOpenInfo->pszFilename);
    1878           5 :         poDS->SetSubdatasetName(osSubdatasetName);
    1879             :     }
    1880             : 
    1881          11 :     poDS->TryLoadXML();
    1882             : 
    1883             :     /* -------------------------------------------------------------------- */
    1884             :     /*      Check for overviews.                                            */
    1885             :     /* -------------------------------------------------------------------- */
    1886          11 :     poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
    1887             : 
    1888          11 :     return poDS.release();
    1889             : }
    1890             : 
    1891             : /************************************************************************/
    1892             : /*                           AddSubDataset()                            */
    1893             : /************************************************************************/
    1894          25 : void SAFEDataset::AddSubDataset(const CPLString &osName,
    1895             :                                 const CPLString &osDesc)
    1896             : {
    1897          25 :     ++m_nSubDSNum;
    1898          25 :     papszSubDatasets = CSLAddNameValue(
    1899             :         papszSubDatasets, CPLSPrintf("SUBDATASET_%d_NAME", m_nSubDSNum),
    1900             :         osName.c_str());
    1901          25 :     papszSubDatasets = CSLAddNameValue(
    1902             :         papszSubDatasets, CPLSPrintf("SUBDATASET_%d_DESC", m_nSubDSNum),
    1903             :         osDesc.c_str());
    1904          25 : }
    1905             : 
    1906             : /************************************************************************/
    1907             : /*                            GetGCPCount()                             */
    1908             : /************************************************************************/
    1909             : 
    1910           2 : int SAFEDataset::GetGCPCount()
    1911             : {
    1912           2 :     return nGCPCount;
    1913             : }
    1914             : 
    1915             : /************************************************************************/
    1916             : /*                          GetGCPSpatialRef()                          */
    1917             : /************************************************************************/
    1918             : 
    1919           0 : const OGRSpatialReference *SAFEDataset::GetGCPSpatialRef() const
    1920             : 
    1921             : {
    1922           0 :     return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
    1923             : }
    1924             : 
    1925             : /************************************************************************/
    1926             : /*                              GetGCPs()                               */
    1927             : /************************************************************************/
    1928             : 
    1929           2 : const GDAL_GCP *SAFEDataset::GetGCPs()
    1930             : 
    1931             : {
    1932           2 :     return pasGCPList;
    1933             : }
    1934             : 
    1935             : /************************************************************************/
    1936             : /*                       GetMetadataDomainList()                        */
    1937             : /************************************************************************/
    1938             : 
    1939           0 : char **SAFEDataset::GetMetadataDomainList()
    1940             : {
    1941           0 :     return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
    1942           0 :                                    "SUBDATASETS", nullptr);
    1943             : }
    1944             : 
    1945             : /************************************************************************/
    1946             : /*                            GetMetadata()                             */
    1947             : /************************************************************************/
    1948             : 
    1949           3 : CSLConstList SAFEDataset::GetMetadata(const char *pszDomain)
    1950             : {
    1951           3 :     if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS"))
    1952           3 :         return papszSubDatasets;
    1953             : 
    1954           0 :     return GDALDataset::GetMetadata(pszDomain);
    1955             : }
    1956             : 
    1957             : /************************************************************************/
    1958             : /*                         GDALRegister_SAFE()                          */
    1959             : /************************************************************************/
    1960             : 
    1961        2059 : void GDALRegister_SAFE()
    1962             : {
    1963        2059 :     if (GDALGetDriverByName("SAFE") != nullptr)
    1964         283 :         return;
    1965             : 
    1966        1776 :     GDALDriver *poDriver = new GDALDriver();
    1967             : 
    1968        1776 :     poDriver->SetDescription("SAFE");
    1969        1776 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1970        1776 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1971        1776 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Sentinel-1 SAR SAFE Product");
    1972        1776 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/safe.html");
    1973             : 
    1974        1776 :     poDriver->pfnOpen = SAFEDataset::Open;
    1975        1776 :     poDriver->pfnIdentify = SAFEDataset::Identify;
    1976             : 
    1977        1776 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1978             : }
    1979             : 
    1980             : /************************************************************************/
    1981             : /*                   Azimuth time handling functions                    */
    1982             : /************************************************************************/
    1983             : 
    1984             : SAFECalibratedRasterBand::TimePoint
    1985           0 : SAFECalibratedRasterBand::getTimePoint(const char *pszTime)
    1986             : {
    1987             :     int nYear, nMonth, nDay, nHour, nMinute, nSeconds;
    1988             :     long nMicroSeconds;
    1989           0 :     sscanf(pszTime, "%d-%d-%dT%d:%d:%d.%ld", &nYear, &nMonth, &nDay, &nHour,
    1990             :            &nMinute, &nSeconds, &nMicroSeconds);
    1991             : 
    1992             :     struct tm oTm;
    1993           0 :     oTm.tm_sec = nSeconds;
    1994           0 :     oTm.tm_min = nMinute;
    1995           0 :     oTm.tm_hour = nHour;
    1996           0 :     oTm.tm_mday = nDay;
    1997           0 :     oTm.tm_mon = nMonth - 1;
    1998           0 :     oTm.tm_year = nYear - 1900;
    1999           0 :     oTm.tm_isdst = -1;
    2000             : 
    2001           0 :     std::time_t oTt = static_cast<std::time_t>(CPLYMDHMSToUnixTime(&oTm));
    2002             : 
    2003           0 :     TimePoint oTp = std::chrono::system_clock::from_time_t(oTt);
    2004           0 :     TimePoint oTp1 = oTp + std::chrono::microseconds(nMicroSeconds);
    2005           0 :     return oTp1;
    2006             : }
    2007             : 
    2008           0 : double SAFECalibratedRasterBand::getTimeDiff(TimePoint oT1, TimePoint oT2)
    2009             : {
    2010           0 :     std::chrono::duration<double> oResult = (oT2 - oT1);
    2011           0 :     return oResult.count();
    2012             : }
    2013             : 
    2014             : SAFECalibratedRasterBand::TimePoint
    2015           0 : SAFECalibratedRasterBand::getazTime(TimePoint oStart, TimePoint oStop,
    2016             :                                     long nNumOfLines, int nOffset)
    2017             : {
    2018           0 :     double dfTemp = getTimeDiff(oStart, oStop);
    2019           0 :     dfTemp /= (nNumOfLines - 1);
    2020           0 :     unsigned long nTimeDiffMicro = static_cast<unsigned long>(dfTemp * 1000000);
    2021             :     TimePoint oResult =
    2022           0 :         oStart + (nOffset * std::chrono::microseconds(nTimeDiffMicro));
    2023           0 :     return oResult;
    2024             : }
    2025             : 
    2026             : /************************************************************************/
    2027             : /*               Utility functions used in interpolation                */
    2028             : /************************************************************************/
    2029             : 
    2030           0 : int SAFECalibratedRasterBand::getCalibrationVectorIndex(int nLineNo)
    2031             : {
    2032           0 :     for (size_t i = 1; i < m_anLineLUT.size(); i++)
    2033             :     {
    2034           0 :         if (nLineNo < m_anLineLUT[i])
    2035           0 :             return static_cast<int>(i - 1);
    2036             :     }
    2037           0 :     return 0;
    2038             : }
    2039             : 
    2040           0 : int SAFECalibratedRasterBand::getPixelIndex(int nPixelNo)
    2041             : {
    2042           0 :     for (int i = 1; i < m_nNumPixels; i++)
    2043             :     {
    2044           0 :         if (nPixelNo < m_anPixelLUT[i])
    2045           0 :             return i - 1;
    2046             :     }
    2047           0 :     return 0;
    2048             : }

Generated by: LCOV version 1.14