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

Generated by: LCOV version 1.14