LCOV - code coverage report
Current view: top level - frmts/envisat - envisatdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 187 478 39.1 %
Date: 2026-07-24 18:27:47 Functions: 12 21 57.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  APP ENVISAT Support
       4             :  * Purpose:  Reader for ENVISAT format image data.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Atlantis Scientific, Inc.
       9             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "adsrange.hpp"
      15             : #include "rawdataset.h"
      16             : #include "cpl_string.h"
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_driver.h"
      19             : #include "gdal_drivermanager.h"
      20             : #include "gdal_openinfo.h"
      21             : #include "gdal_cpp_functions.h"
      22             : #include "ogr_srs_api.h"
      23             : #include "timedelta.hpp"
      24             : 
      25             : CPL_C_START
      26             : #include "EnvisatFile.h"
      27             : #include "records.h"
      28             : CPL_C_END
      29             : 
      30             : #include <algorithm>
      31             : 
      32             : /************************************************************************/
      33             : /* ==================================================================== */
      34             : /*                        MerisL2FlagBand                         */
      35             : /* ==================================================================== */
      36             : /************************************************************************/
      37             : class MerisL2FlagBand final : public GDALPamRasterBand
      38             : {
      39             :   public:
      40             :     MerisL2FlagBand(GDALDataset *, int, VSILFILE *, vsi_l_offset, int);
      41             :     ~MerisL2FlagBand() override;
      42             :     CPLErr IReadBlock(int, int, void *) override;
      43             : 
      44             :   private:
      45             :     vsi_l_offset nImgOffset;
      46             :     int nPrefixBytes;
      47             :     size_t nBytePerPixel;
      48             :     size_t nRecordSize;
      49             :     size_t nDataSize;
      50             :     GByte *pReadBuf;
      51             :     VSILFILE *fpImage;
      52             : };
      53             : 
      54             : /************************************************************************/
      55             : /*                          MerisL2FlagBand()                           */
      56             : /************************************************************************/
      57           0 : MerisL2FlagBand::MerisL2FlagBand(GDALDataset *poDSIn, int nBandIn,
      58             :                                  VSILFILE *fpImageIn, vsi_l_offset nImgOffsetIn,
      59           0 :                                  int nPrefixBytesIn)
      60             :     : nImgOffset(nImgOffsetIn), nPrefixBytes(nPrefixBytesIn), nBytePerPixel(3),
      61           0 :       nRecordSize(0), nDataSize(0), pReadBuf(nullptr)
      62             : {
      63           0 :     poDS = poDSIn;
      64           0 :     nBand = nBandIn;
      65             : 
      66           0 :     fpImage = fpImageIn;
      67             : 
      68           0 :     eDataType = GDT_UInt32;
      69             : 
      70           0 :     nBlockXSize = poDS->GetRasterXSize();
      71           0 :     nBlockYSize = 1;
      72           0 :     nRecordSize = nPrefixBytesIn + nBlockXSize * nBytePerPixel;
      73           0 :     nDataSize = nBlockXSize * nBytePerPixel;
      74           0 :     pReadBuf = static_cast<GByte *>(CPLMalloc(nRecordSize));
      75           0 : }
      76             : 
      77             : /************************************************************************/
      78             : /*                          ~MerisL2FlagBand()                          */
      79             : /************************************************************************/
      80           0 : MerisL2FlagBand::~MerisL2FlagBand()
      81             : {
      82           0 :     CPLFree(pReadBuf);
      83           0 : }
      84             : 
      85             : /************************************************************************/
      86             : /*                             IReadBlock()                             */
      87             : /************************************************************************/
      88           0 : CPLErr MerisL2FlagBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
      89             :                                    void *pImage)
      90             : {
      91           0 :     CPLAssert(nBlockXOff == 0);
      92           0 :     CPLAssert(pReadBuf != nullptr);
      93             : 
      94           0 :     vsi_l_offset nOffset =
      95           0 :         nImgOffset + nPrefixBytes +
      96           0 :         static_cast<vsi_l_offset>(nBlockYOff) * nBlockYSize * nRecordSize;
      97             : 
      98           0 :     if (VSIFSeekL(fpImage, nOffset, SEEK_SET) != 0)
      99             :     {
     100           0 :         CPLError(CE_Failure, CPLE_FileIO,
     101             :                  "Seek to %d for scanline %d failed.\n", (int)nOffset,
     102             :                  nBlockYOff);
     103           0 :         return CE_Failure;
     104             :     }
     105             : 
     106           0 :     if (VSIFReadL(pReadBuf, 1, nDataSize, fpImage) != nDataSize)
     107             :     {
     108           0 :         CPLError(CE_Failure, CPLE_FileIO,
     109           0 :                  "Read of %d bytes for scanline %d failed.\n", (int)nDataSize,
     110             :                  nBlockYOff);
     111           0 :         return CE_Failure;
     112             :     }
     113             : 
     114           0 :     const unsigned int nUInt32Size = 4;
     115           0 :     for (unsigned iImg = 0, iBuf = 0; iImg < nBlockXSize * nUInt32Size;
     116           0 :          iImg += nUInt32Size, iBuf += (unsigned)nBytePerPixel)
     117             :     {
     118             : #ifdef CPL_LSB
     119           0 :         ((GByte *)pImage)[iImg] = pReadBuf[iBuf + 2];
     120           0 :         ((GByte *)pImage)[iImg + 1] = pReadBuf[iBuf + 1];
     121           0 :         ((GByte *)pImage)[iImg + 2] = pReadBuf[iBuf];
     122           0 :         ((GByte *)pImage)[iImg + 3] = 0;
     123             : #else
     124             :         ((GByte *)pImage)[iImg] = 0;
     125             :         ((GByte *)pImage)[iImg + 1] = pReadBuf[iBuf];
     126             :         ((GByte *)pImage)[iImg + 2] = pReadBuf[iBuf + 1];
     127             :         ((GByte *)pImage)[iImg + 3] = pReadBuf[iBuf + 2];
     128             : #endif
     129             :     }
     130             : 
     131           0 :     return CE_None;
     132             : }
     133             : 
     134             : /************************************************************************/
     135             : /* ==================================================================== */
     136             : /*                              EnvisatDataset                          */
     137             : /* ==================================================================== */
     138             : /************************************************************************/
     139             : 
     140             : class EnvisatDataset final : public RawDataset
     141             : {
     142             :     EnvisatFile *hEnvisatFile;
     143             :     VSILFILE *fpImage;
     144             : 
     145             :     OGRSpatialReference m_oGCPSRS{};
     146             :     int nGCPCount;
     147             :     GDAL_GCP *pasGCPList;
     148             : 
     149             :     char **papszTempMD;
     150             : 
     151             :     void ScanForGCPs_ASAR();
     152             :     void ScanForGCPs_MERIS();
     153             : 
     154             :     void UnwrapGCPs();
     155             : 
     156             :     void CollectMetadata(EnvisatFile_HeaderFlag);
     157             :     void CollectDSDMetadata();
     158             :     void CollectADSMetadata();
     159             : 
     160             :     CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
     161             : 
     162             :   public:
     163             :     EnvisatDataset();
     164             :     ~EnvisatDataset() override;
     165             : 
     166             :     int GetGCPCount() override;
     167             :     const OGRSpatialReference *GetGCPSpatialRef() const override;
     168             :     const GDAL_GCP *GetGCPs() override;
     169             :     char **GetMetadataDomainList() override;
     170             :     CSLConstList GetMetadata(const char *pszDomain) override;
     171             : 
     172             :     static GDALDataset *Open(GDALOpenInfo *);
     173             : };
     174             : 
     175             : /************************************************************************/
     176             : /* ==================================================================== */
     177             : /*                              EnvisatDataset                          */
     178             : /* ==================================================================== */
     179             : /************************************************************************/
     180             : 
     181             : /************************************************************************/
     182             : /*                           EnvisatDataset()                           */
     183             : /************************************************************************/
     184             : 
     185           2 : EnvisatDataset::EnvisatDataset()
     186             :     : hEnvisatFile(nullptr), fpImage(nullptr), nGCPCount(0),
     187           2 :       pasGCPList(nullptr), papszTempMD(nullptr)
     188             : {
     189           2 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     190           2 :     m_oGCPSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
     191           2 : }
     192             : 
     193             : /************************************************************************/
     194             : /*                          ~EnvisatDataset()                           */
     195             : /************************************************************************/
     196             : 
     197           4 : EnvisatDataset::~EnvisatDataset()
     198             : 
     199             : {
     200           2 :     EnvisatDataset::Close();
     201           4 : }
     202             : 
     203             : /************************************************************************/
     204             : /*                               Close()                                */
     205             : /************************************************************************/
     206             : 
     207           4 : CPLErr EnvisatDataset::Close(GDALProgressFunc, void *)
     208             : {
     209           4 :     CPLErr eErr = CE_None;
     210           4 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     211             :     {
     212           2 :         if (EnvisatDataset::FlushCache(true) != CE_None)
     213           0 :             eErr = CE_Failure;
     214             : 
     215           2 :         if (hEnvisatFile != nullptr)
     216           2 :             EnvisatFile_Close(hEnvisatFile);
     217             : 
     218           2 :         if (fpImage != nullptr)
     219           2 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
     220             : 
     221           2 :         if (nGCPCount > 0)
     222             :         {
     223           1 :             GDALDeinitGCPs(nGCPCount, pasGCPList);
     224           1 :             CPLFree(pasGCPList);
     225             :         }
     226             : 
     227           2 :         CSLDestroy(papszTempMD);
     228             : 
     229           2 :         if (GDALPamDataset::Close() != CE_None)
     230           0 :             eErr = CE_Failure;
     231             :     }
     232           4 :     return eErr;
     233             : }
     234             : 
     235             : /************************************************************************/
     236             : /*                            GetGCPCount()                             */
     237             : /************************************************************************/
     238             : 
     239           2 : int EnvisatDataset::GetGCPCount()
     240             : 
     241             : {
     242           2 :     return nGCPCount;
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                          GetGCPSpatialRef()                          */
     247             : /************************************************************************/
     248             : 
     249           0 : const OGRSpatialReference *EnvisatDataset::GetGCPSpatialRef() const
     250             : 
     251             : {
     252           0 :     if (nGCPCount > 0)
     253           0 :         return &m_oGCPSRS;
     254             : 
     255           0 :     return nullptr;
     256             : }
     257             : 
     258             : /************************************************************************/
     259             : /*                               GetGCP()                               */
     260             : /************************************************************************/
     261             : 
     262           0 : const GDAL_GCP *EnvisatDataset::GetGCPs()
     263             : 
     264             : {
     265           0 :     return pasGCPList;
     266             : }
     267             : 
     268             : /************************************************************************/
     269             : /*                             UnwrapGCPs()                             */
     270             : /************************************************************************/
     271             : 
     272             : /* external C++ implementation of the in-place unwrapper */
     273             : void EnvisatUnwrapGCPs(int nGCPCount, GDAL_GCP *pasGCPList);
     274             : 
     275           2 : void EnvisatDataset::UnwrapGCPs()
     276             : {
     277           2 :     EnvisatUnwrapGCPs(nGCPCount, pasGCPList);
     278           2 : }
     279             : 
     280             : /************************************************************************/
     281             : /*                          ScanForGCPs_ASAR()                          */
     282             : /************************************************************************/
     283             : 
     284           2 : void EnvisatDataset::ScanForGCPs_ASAR()
     285             : 
     286             : {
     287             :     /* -------------------------------------------------------------------- */
     288             :     /*      Do we have a meaningful geolocation grid?                       */
     289             :     /* -------------------------------------------------------------------- */
     290             :     int nDatasetIndex =
     291           2 :         EnvisatFile_GetDatasetIndex(hEnvisatFile, "GEOLOCATION GRID ADS");
     292           2 :     if (nDatasetIndex == -1)
     293           1 :         return;
     294             : 
     295             :     int nNumDSR, nDSRSize, nDSSize;
     296           2 :     if (EnvisatFile_GetDatasetInfo(hEnvisatFile, nDatasetIndex, nullptr,
     297             :                                    nullptr, nullptr, nullptr, &nDSSize,
     298           2 :                                    &nNumDSR, &nDSRSize) != SUCCESS)
     299           0 :         return;
     300             : 
     301           2 :     if (nNumDSR == 0 || nDSRSize != 521)
     302           0 :         return;
     303             : 
     304             :     // nNumDSR is taken verbatim from the dataset descriptor. Reject a record
     305             :     // count that cannot fit the declared dataset size before using it to size
     306             :     // the GCP array: (nNumDSR + 1) * 11 is otherwise evaluated as int and
     307             :     // overflows for a large nNumDSR, under-allocating the array while the loop
     308             :     // below still writes 11 GCPs per record (heap buffer overflow).
     309           2 :     if (nNumDSR < 0 || nNumDSR > nDSSize / nDSRSize)
     310           1 :         return;
     311             : 
     312             :     /* -------------------------------------------------------------------- */
     313             :     /*      Collect the first GCP set from each record.                     */
     314             :     /* -------------------------------------------------------------------- */
     315             :     GByte abyRecord[521];
     316           1 :     int nRange = 0;
     317           1 :     int nRangeOffset = 0;
     318             :     GUInt32 unValue;
     319             : 
     320           1 :     nGCPCount = 0;
     321           2 :     pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP),
     322           1 :                                        (static_cast<size_t>(nNumDSR) + 1) * 11);
     323             : 
     324           3 :     for (int iRecord = 0; iRecord < nNumDSR; iRecord++)
     325             :     {
     326           2 :         if (EnvisatFile_ReadDatasetRecord(hEnvisatFile, nDatasetIndex, iRecord,
     327           2 :                                           abyRecord) != SUCCESS)
     328           0 :             continue;
     329             : 
     330           2 :         memcpy(&unValue, abyRecord + 13, 4);
     331           2 :         nRange = CPL_MSBWORD32(unValue) + nRangeOffset;
     332             : 
     333           2 :         if ((iRecord > 1) &&
     334           0 :             (int(pasGCPList[nGCPCount - 1].dfGCPLine + 0.5) > nRange))
     335             :         {
     336           0 :             int delta = (int)(pasGCPList[nGCPCount - 1].dfGCPLine -
     337           0 :                               pasGCPList[nGCPCount - 12].dfGCPLine);
     338           0 :             nRange = int(pasGCPList[nGCPCount - 1].dfGCPLine + 0.5) + delta;
     339           0 :             nRangeOffset = nRange - 1;
     340             :         }
     341             : 
     342          24 :         for (int iGCP = 0; iGCP < 11; iGCP++)
     343             :         {
     344          22 :             GDALInitGCPs(1, pasGCPList + nGCPCount);
     345             : 
     346          22 :             CPLFree(pasGCPList[nGCPCount].pszId);
     347             : 
     348             :             char szId[128];
     349          22 :             snprintf(szId, sizeof(szId), "%d", nGCPCount + 1);
     350          22 :             pasGCPList[nGCPCount].pszId = CPLStrdup(szId);
     351             : 
     352          22 :             memcpy(&unValue, abyRecord + 25 + iGCP * 4, 4);
     353          22 :             int nSample = CPL_MSBWORD32(unValue);
     354             : 
     355          22 :             memcpy(&unValue, abyRecord + 25 + 176 + iGCP * 4, 4);
     356          22 :             pasGCPList[nGCPCount].dfGCPX =
     357          22 :                 ((int)CPL_MSBWORD32(unValue)) * 0.000001;
     358             : 
     359          22 :             memcpy(&unValue, abyRecord + 25 + 132 + iGCP * 4, 4);
     360          22 :             pasGCPList[nGCPCount].dfGCPY =
     361          22 :                 ((int)CPL_MSBWORD32(unValue)) * 0.000001;
     362             : 
     363          22 :             pasGCPList[nGCPCount].dfGCPZ = 0.0;
     364             : 
     365          22 :             pasGCPList[nGCPCount].dfGCPLine = nRange - 0.5;
     366          22 :             pasGCPList[nGCPCount].dfGCPPixel = nSample - 0.5;
     367             : 
     368          22 :             nGCPCount++;
     369             :         }
     370             :     }
     371             : 
     372             :     /* -------------------------------------------------------------------- */
     373             :     /*      We also collect the bottom GCPs from the last granule.          */
     374             :     /* -------------------------------------------------------------------- */
     375           1 :     memcpy(&unValue, abyRecord + 17, 4);
     376           1 :     nRange = nRange + CPL_MSBWORD32(unValue) - 1;
     377             : 
     378          12 :     for (int iGCP = 0; iGCP < 11; iGCP++)
     379             :     {
     380          11 :         GDALInitGCPs(1, pasGCPList + nGCPCount);
     381             : 
     382          11 :         CPLFree(pasGCPList[nGCPCount].pszId);
     383             : 
     384             :         char szId[128];
     385          11 :         snprintf(szId, sizeof(szId), "%d", nGCPCount + 1);
     386          11 :         pasGCPList[nGCPCount].pszId = CPLStrdup(szId);
     387             : 
     388          11 :         memcpy(&unValue, abyRecord + 279 + iGCP * 4, 4);
     389          11 :         int nSample = CPL_MSBWORD32(unValue);
     390             : 
     391          11 :         memcpy(&unValue, abyRecord + 279 + 176 + iGCP * 4, 4);
     392          11 :         pasGCPList[nGCPCount].dfGCPX = ((int)CPL_MSBWORD32(unValue)) * 0.000001;
     393             : 
     394          11 :         memcpy(&unValue, abyRecord + 279 + 132 + iGCP * 4, 4);
     395          11 :         pasGCPList[nGCPCount].dfGCPY = ((int)CPL_MSBWORD32(unValue)) * 0.000001;
     396             : 
     397          11 :         pasGCPList[nGCPCount].dfGCPZ = 0.0;
     398             : 
     399          11 :         pasGCPList[nGCPCount].dfGCPLine = nRange - 0.5;
     400          11 :         pasGCPList[nGCPCount].dfGCPPixel = nSample - 0.5;
     401             : 
     402          11 :         nGCPCount++;
     403             :     }
     404             : }
     405             : 
     406             : /************************************************************************/
     407             : /*                         ScanForGCPs_MERIS()                          */
     408             : /************************************************************************/
     409             : 
     410           0 : void EnvisatDataset::ScanForGCPs_MERIS()
     411             : 
     412             : {
     413             :     /* -------------------------------------------------------------------- */
     414             :     /*      Do we have a meaningful geolocation grid?  Search for a         */
     415             :     /*      DS_TYPE=A and a name containing "geolocation" or "tie           */
     416             :     /*      points".                                                        */
     417             :     /* -------------------------------------------------------------------- */
     418             :     int nDatasetIndex =
     419           0 :         EnvisatFile_GetDatasetIndex(hEnvisatFile, "Tie points ADS");
     420           0 :     if (nDatasetIndex == -1)
     421           0 :         return;
     422             : 
     423             :     int nNumDSR, nDSRSize;
     424           0 :     if (EnvisatFile_GetDatasetInfo(hEnvisatFile, nDatasetIndex, nullptr,
     425             :                                    nullptr, nullptr, nullptr, nullptr, &nNumDSR,
     426           0 :                                    &nDSRSize) != SUCCESS)
     427           0 :         return;
     428             : 
     429           0 :     if (nNumDSR == 0)
     430           0 :         return;
     431             : 
     432             :     /* -------------------------------------------------------------------- */
     433             :     /*      Figure out the tiepoint space, and how many we have.            */
     434             :     /* -------------------------------------------------------------------- */
     435             :     int nLinesPerTiePoint =
     436           0 :         EnvisatFile_GetKeyValueAsInt(hEnvisatFile, SPH, "LINES_PER_TIE_PT", 0);
     437           0 :     int nSamplesPerTiePoint = EnvisatFile_GetKeyValueAsInt(
     438             :         hEnvisatFile, SPH, "SAMPLES_PER_TIE_PT", 0);
     439             : 
     440           0 :     if (nLinesPerTiePoint == 0 || nSamplesPerTiePoint == 0)
     441           0 :         return;
     442             : 
     443           0 :     int nTPPerColumn = nNumDSR;
     444           0 :     int nTPPerLine = DIV_ROUND_UP(GetRasterXSize(), nSamplesPerTiePoint);
     445             : 
     446             :     /* -------------------------------------------------------------------- */
     447             :     /*      Find a measurement type dataset to use as a reference raster    */
     448             :     /*      band.                                                           */
     449             :     /* -------------------------------------------------------------------- */
     450             : 
     451           0 :     int nMDSIndex = 0;
     452             : 
     453           0 :     for (; true; nMDSIndex++)
     454             :     {
     455           0 :         const char *pszDSType = nullptr;
     456           0 :         if (EnvisatFile_GetDatasetInfo(hEnvisatFile, nMDSIndex, nullptr,
     457             :                                        &pszDSType, nullptr, nullptr, nullptr,
     458           0 :                                        nullptr, nullptr) == FAILURE)
     459             :         {
     460           0 :             CPLDebug("EnvisatDataset", "Unable to find MDS in Envisat file.");
     461           0 :             return;
     462             :         }
     463           0 :         if (EQUAL(pszDSType, "M"))
     464           0 :             break;
     465           0 :     }
     466             : 
     467             :     /* -------------------------------------------------------------------- */
     468             :     /*      Get subset of TP ADS records matching the MDS records           */
     469             :     /* -------------------------------------------------------------------- */
     470             : 
     471             :     /* get the MDS line sampling time interval */
     472             :     TimeDelta tdMDSSamplingInterval(
     473             :         0, 0,
     474             :         EnvisatFile_GetKeyValueAsInt(hEnvisatFile, SPH, "LINE_TIME_INTERVAL",
     475           0 :                                      0));
     476             : 
     477             :     /* get range of TiePoint ADS records matching the measurements */
     478           0 :     ADSRangeLastAfter arTP(*hEnvisatFile, nDatasetIndex, nMDSIndex,
     479           0 :                            tdMDSSamplingInterval);
     480             : 
     481             :     /* check if there are any TPs to be used */
     482           0 :     if (arTP.getDSRCount() <= 0)
     483             :     {
     484           0 :         CPLDebug("EnvisatDataset", "No tiepoint covering "
     485             :                                    "the measurement records.");
     486           0 :         return; /* No TPs - no extraction. */
     487             :     }
     488             : 
     489             :     /* check if TPs cover the whole range of MDSRs */
     490           0 :     if ((arTP.getFirstOffset() < 0) || (arTP.getLastOffset() < 0))
     491             :     {
     492           0 :         CPLDebug("EnvisatDataset", "The tiepoints do not cover "
     493             :                                    "whole range of measurement records.");
     494             :         /* Not good but we can still extract some of the TPS, can we? */
     495             :     }
     496             : 
     497             :     /* Check TP record spacing */
     498           0 :     if ((1 +
     499           0 :          (arTP.getFirstOffset() + arTP.getLastOffset() + GetRasterYSize() - 1) /
     500           0 :              nLinesPerTiePoint) != arTP.getDSRCount())
     501             :     {
     502           0 :         CPLDebug("EnvisatDataset",
     503             :                  "Not enough tiepoints per column! "
     504             :                  "received=%d expected=%d",
     505             :                  nTPPerColumn,
     506           0 :                  1 + (arTP.getFirstOffset() + arTP.getLastOffset() +
     507           0 :                       GetRasterYSize() - 1) /
     508             :                          nLinesPerTiePoint);
     509           0 :         return;  // That is far more serious - we risk misplacing TPs.
     510             :     }
     511             : 
     512             :     bool isBrowseProduct;
     513           0 :     if (50 * nTPPerLine + 13 == nDSRSize) /* regular product */
     514             :     {
     515           0 :         isBrowseProduct = false;
     516             :     }
     517           0 :     else if (8 * nTPPerLine + 13 == nDSRSize) /* browse product */
     518             :     {
     519             :         /* although BPs are rare there is no reason not to support them */
     520           0 :         isBrowseProduct = true;
     521             :     }
     522             :     else
     523             :     {
     524           0 :         CPLDebug("EnvisatDataset",
     525             :                  "Unexpected size of 'Tie points ADS' !"
     526             :                  " received=%d expected=%d or %d",
     527           0 :                  nDSRSize, 50 * nTPPerLine + 13, 8 * nTPPerLine + 13);
     528           0 :         return;
     529             :     }
     530             : 
     531             :     /* -------------------------------------------------------------------- */
     532             :     /*      Collect the first GCP set from each record.                     */
     533             :     /* -------------------------------------------------------------------- */
     534             : 
     535           0 :     GByte *pabyRecord = (GByte *)CPLMalloc(nDSRSize - 13);
     536             : 
     537           0 :     GUInt32 *tpLat =
     538             :         reinterpret_cast<GUInt32 *>(pabyRecord) + nTPPerLine * 0; /* latitude */
     539           0 :     GUInt32 *tpLon = reinterpret_cast<GUInt32 *>(pabyRecord) +
     540             :                      nTPPerLine * 1; /* longitude */
     541           0 :     GUInt32 *tpLtc = reinterpret_cast<GUInt32 *>(pabyRecord) +
     542             :                      nTPPerLine * 4; /* lat. DEM correction */
     543           0 :     GUInt32 *tpLnc = reinterpret_cast<GUInt32 *>(pabyRecord) +
     544             :                      nTPPerLine * 5; /* lon. DEM correction */
     545             : 
     546           0 :     nGCPCount = 0;
     547           0 :     pasGCPList = (GDAL_GCP *)CPLCalloc(
     548           0 :         sizeof(GDAL_GCP), static_cast<size_t>(arTP.getDSRCount()) * nTPPerLine);
     549             : 
     550           0 :     for (int ir = 0; ir < arTP.getDSRCount(); ir++)
     551             :     {
     552           0 :         int iRecord = ir + arTP.getFirstIndex();
     553             : 
     554             :         double dfGCPLine =
     555           0 :             0.5 + (iRecord * nLinesPerTiePoint - arTP.getFirstOffset());
     556             : 
     557           0 :         if (EnvisatFile_ReadDatasetRecordChunk(hEnvisatFile, nDatasetIndex,
     558             :                                                iRecord, pabyRecord, 13,
     559           0 :                                                -1) != SUCCESS)
     560           0 :             continue;
     561             : 
     562           0 :         for (int iGCP = 0; iGCP < nTPPerLine; iGCP++)
     563             :         {
     564           0 :             GDALInitGCPs(1, pasGCPList + nGCPCount);
     565             : 
     566           0 :             CPLFree(pasGCPList[nGCPCount].pszId);
     567             : 
     568             :             char szId[128];
     569           0 :             snprintf(szId, sizeof(szId), "%d", nGCPCount + 1);
     570           0 :             pasGCPList[nGCPCount].pszId = CPLStrdup(szId);
     571             : 
     572             : #define INT32(x) ((GInt32)CPL_MSBWORD32(x))
     573             : 
     574           0 :             pasGCPList[nGCPCount].dfGCPX = 1e-6 * INT32(tpLon[iGCP]);
     575           0 :             pasGCPList[nGCPCount].dfGCPY = 1e-6 * INT32(tpLat[iGCP]);
     576           0 :             pasGCPList[nGCPCount].dfGCPZ = 0.0;
     577             : 
     578           0 :             if (!isBrowseProduct) /* add DEM corrections */
     579             :             {
     580           0 :                 pasGCPList[nGCPCount].dfGCPX += 1e-6 * INT32(tpLnc[iGCP]);
     581           0 :                 pasGCPList[nGCPCount].dfGCPY += 1e-6 * INT32(tpLtc[iGCP]);
     582             :             }
     583             : 
     584             : #undef INT32
     585             : 
     586           0 :             pasGCPList[nGCPCount].dfGCPLine = dfGCPLine;
     587           0 :             pasGCPList[nGCPCount].dfGCPPixel = iGCP * nSamplesPerTiePoint + 0.5;
     588             : 
     589           0 :             nGCPCount++;
     590             :         }
     591             :     }
     592           0 :     CPLFree(pabyRecord);
     593             : }
     594             : 
     595             : /************************************************************************/
     596             : /*                       GetMetadataDomainList()                        */
     597             : /************************************************************************/
     598             : 
     599           0 : char **EnvisatDataset::GetMetadataDomainList()
     600             : {
     601           0 :     return CSLAddString(GDALDataset::GetMetadataDomainList(), "envisat-ds-*-*");
     602             : }
     603             : 
     604             : /************************************************************************/
     605             : /*                            GetMetadata()                             */
     606             : /************************************************************************/
     607             : 
     608           0 : CSLConstList EnvisatDataset::GetMetadata(const char *pszDomain)
     609             : 
     610             : {
     611           0 :     if (pszDomain == nullptr || !STARTS_WITH_CI(pszDomain, "envisat-ds-"))
     612           0 :         return GDALDataset::GetMetadata(pszDomain);
     613             : 
     614             :     /* -------------------------------------------------------------------- */
     615             :     /*      Get the dataset name and record number.                         */
     616             :     /* -------------------------------------------------------------------- */
     617             :     char szDSName[128];
     618           0 :     strncpy(szDSName, pszDomain + 11, sizeof(szDSName));
     619           0 :     szDSName[sizeof(szDSName) - 1] = 0;
     620             : 
     621           0 :     int nRecord = -1;
     622           0 :     for (int i = 0; i < (int)sizeof(szDSName) - 1; i++)
     623             :     {
     624           0 :         if (szDSName[i] == '-')
     625             :         {
     626           0 :             szDSName[i] = '\0';
     627           0 :             nRecord = atoi(szDSName + 1);
     628           0 :             break;
     629             :         }
     630             :     }
     631             : 
     632           0 :     if (nRecord == -1)
     633           0 :         return nullptr;
     634             : 
     635             :     /* -------------------------------------------------------------------- */
     636             :     /*      Get the dataset index and info.                                 */
     637             :     /* -------------------------------------------------------------------- */
     638           0 :     int nDSIndex = EnvisatFile_GetDatasetIndex(hEnvisatFile, szDSName);
     639           0 :     if (nDSIndex == -1)
     640           0 :         return nullptr;
     641             : 
     642             :     int nDSRSize, nNumDSR;
     643           0 :     EnvisatFile_GetDatasetInfo(hEnvisatFile, nDSIndex, nullptr, nullptr,
     644             :                                nullptr, nullptr, nullptr, &nNumDSR, &nDSRSize);
     645             : 
     646           0 :     if (nDSRSize == -1 || nRecord < 0 || nRecord >= nNumDSR)
     647           0 :         return nullptr;
     648             : 
     649             :     /* -------------------------------------------------------------------- */
     650             :     /*      Read the requested record.                                      */
     651             :     /* -------------------------------------------------------------------- */
     652           0 :     char *pszRecord = (char *)CPLMalloc(nDSRSize + 1);
     653             : 
     654           0 :     if (EnvisatFile_ReadDatasetRecord(hEnvisatFile, nDSIndex, nRecord,
     655           0 :                                       pszRecord) == FAILURE)
     656             :     {
     657           0 :         CPLFree(pszRecord);
     658           0 :         return nullptr;
     659             :     }
     660             : 
     661             :     /* -------------------------------------------------------------------- */
     662             :     /*      Massage the data into a safe textual format.  For now we        */
     663             :     /*      just turn zero bytes into spaces.                               */
     664             :     /* -------------------------------------------------------------------- */
     665           0 :     CSLDestroy(papszTempMD);
     666             : 
     667             :     char *pszEscapedRecord =
     668           0 :         CPLEscapeString(pszRecord, nDSRSize, CPLES_BackslashQuotable);
     669           0 :     papszTempMD = CSLSetNameValue(nullptr, "EscapedRecord", pszEscapedRecord);
     670           0 :     CPLFree(pszEscapedRecord);
     671             : 
     672           0 :     for (int i = 0; i < nDSRSize; i++)
     673           0 :         if (pszRecord[i] == '\0')
     674           0 :             pszRecord[i] = ' ';
     675             : 
     676           0 :     papszTempMD = CSLSetNameValue(papszTempMD, "RawRecord", pszRecord);
     677             : 
     678           0 :     CPLFree(pszRecord);
     679             : 
     680           0 :     return papszTempMD;
     681             : }
     682             : 
     683             : /************************************************************************/
     684             : /*                         CollectDSDMetadata()                         */
     685             : /*                                                                      */
     686             : /*      Collect metadata based on any DSD entries with filenames        */
     687             : /*      associated.                                                     */
     688             : /************************************************************************/
     689             : 
     690           2 : void EnvisatDataset::CollectDSDMetadata()
     691             : 
     692             : {
     693             :     const char *pszDSName;
     694             :     const char *pszFilename;
     695             : 
     696           6 :     for (int iDSD = 0;
     697           6 :          EnvisatFile_GetDatasetInfo(hEnvisatFile, iDSD, &pszDSName, nullptr,
     698             :                                     &pszFilename, nullptr, nullptr, nullptr,
     699           6 :                                     nullptr) == SUCCESS;
     700             :          iDSD++)
     701             :     {
     702           4 :         if (pszFilename == nullptr || strlen(pszFilename) == 0 ||
     703           4 :             STARTS_WITH_CI(pszFilename, "NOT USED") ||
     704           2 :             STARTS_WITH_CI(pszFilename, "        "))
     705           4 :             continue;
     706             : 
     707           0 :         std::string osKey("DS_");
     708           0 :         osKey += pszDSName;
     709             :         // strip trailing spaces.
     710             :         {
     711           0 :             const auto nPos = osKey.rfind(' ');
     712           0 :             if (nPos != std::string::npos)
     713           0 :                 osKey.resize(nPos);
     714             :         }
     715             : 
     716             :         // convert spaces into underscores.
     717           0 :         for (char &ch : osKey)
     718             :         {
     719           0 :             if (ch == ' ')
     720           0 :                 ch = '_';
     721             :         }
     722             : 
     723           0 :         osKey += "_NAME";
     724             : 
     725           0 :         std::string osTrimmedName(pszFilename);
     726             :         {
     727           0 :             const auto nPos = osTrimmedName.rfind(' ');
     728           0 :             if (nPos != std::string::npos)
     729           0 :                 osTrimmedName.resize(nPos);
     730             :         }
     731             : 
     732           0 :         SetMetadataItem(osKey.c_str(), osTrimmedName.c_str());
     733             :     }
     734           2 : }
     735             : 
     736             : /************************************************************************/
     737             : /*                         CollectADSMetadata()                         */
     738             : /*                                                                      */
     739             : /*      Collect metadata from envisat ADS and GADS.                     */
     740             : /************************************************************************/
     741             : 
     742           2 : void EnvisatDataset::CollectADSMetadata()
     743             : {
     744             :     int nNumDsr, nDSRSize;
     745             :     const char *pszDSName;
     746             :     const char *pszDSType;
     747             :     const char *pszDSFilename;
     748             : 
     749             :     const char *pszProduct =
     750           2 :         EnvisatFile_GetKeyValueAsString(hEnvisatFile, MPH, "PRODUCT", "");
     751             : 
     752           6 :     for (int nDSIndex = 0;
     753           6 :          EnvisatFile_GetDatasetInfo(hEnvisatFile, nDSIndex, &pszDSName,
     754             :                                     &pszDSType, &pszDSFilename, nullptr,
     755           6 :                                     nullptr, &nNumDsr, &nDSRSize) == SUCCESS;
     756             :          ++nDSIndex)
     757             :     {
     758           4 :         if (STARTS_WITH_CI(pszDSFilename, "NOT USED") || (nNumDsr <= 0))
     759           2 :             continue;
     760           2 :         if (!EQUAL(pszDSType, "A") && !EQUAL(pszDSType, "G"))
     761           2 :             continue;
     762             : 
     763           0 :         for (int nRecord = 0; nRecord < nNumDsr; ++nRecord)
     764             :         {
     765             :             char szPrefix[128];
     766           0 :             strncpy(szPrefix, pszDSName, sizeof(szPrefix) - 1);
     767           0 :             szPrefix[sizeof(szPrefix) - 1] = '\0';
     768             : 
     769             :             // strip trailing spaces
     770           0 :             for (int i = static_cast<int>(strlen(szPrefix)) - 1;
     771           0 :                  i && szPrefix[i] == ' '; --i)
     772           0 :                 szPrefix[i] = '\0';
     773             : 
     774             :             // convert spaces into underscores
     775           0 :             for (int i = 0; szPrefix[i] != '\0'; i++)
     776             :             {
     777           0 :                 if (szPrefix[i] == ' ')
     778           0 :                     szPrefix[i] = '_';
     779             :             }
     780             : 
     781           0 :             char *pszRecord = (char *)CPLMalloc(nDSRSize + 1);
     782             : 
     783           0 :             if (EnvisatFile_ReadDatasetRecord(hEnvisatFile, nDSIndex, nRecord,
     784           0 :                                               pszRecord) == FAILURE)
     785             :             {
     786           0 :                 CPLFree(pszRecord);
     787           0 :                 return;
     788             :             }
     789             : 
     790             :             const EnvisatRecordDescr *pRecordDescr =
     791           0 :                 EnvisatFile_GetRecordDescriptor(pszProduct, pszDSName);
     792           0 :             if (pRecordDescr)
     793             :             {
     794           0 :                 const EnvisatFieldDescr *pField = pRecordDescr->pFields;
     795           0 :                 while (pField && pField->szName)
     796             :                 {
     797             :                     char szValue[1024];
     798           0 :                     if (CE_None == EnvisatFile_GetFieldAsString(
     799             :                                        pszRecord, nDSRSize, pField, szValue,
     800             :                                        sizeof(szValue)))
     801             :                     {
     802             :                         char szKey[256];
     803           0 :                         if (nNumDsr == 1)
     804           0 :                             snprintf(szKey, sizeof(szKey), "%s_%s", szPrefix,
     805           0 :                                      pField->szName);
     806             :                         else
     807             :                             // sprintf(szKey, "%s_%02d_%s", szPrefix, nRecord,
     808           0 :                             snprintf(szKey, sizeof(szKey), "%s_%d_%s", szPrefix,
     809           0 :                                      nRecord, pField->szName);
     810           0 :                         SetMetadataItem(szKey, szValue, "RECORDS");
     811             :                     }
     812             :                     // silently ignore conversion errors
     813             : 
     814           0 :                     ++pField;
     815             :                 }
     816             :             }
     817           0 :             CPLFree(pszRecord);
     818             :         }
     819             :     }
     820             : }
     821             : 
     822             : /************************************************************************/
     823             : /*                          CollectMetadata()                           */
     824             : /*                                                                      */
     825             : /*      Collect metadata from the SPH or MPH header fields.             */
     826             : /************************************************************************/
     827             : 
     828           4 : void EnvisatDataset::CollectMetadata(EnvisatFile_HeaderFlag eMPHOrSPH)
     829             : 
     830             : {
     831           4 :     for (int iKey = 0; true; iKey++)
     832             :     {
     833             :         const char *pszKey =
     834          20 :             EnvisatFile_GetKeyByIndex(hEnvisatFile, eMPHOrSPH, iKey);
     835          20 :         if (pszKey == nullptr)
     836           4 :             break;
     837             : 
     838          16 :         const char *pszValue = EnvisatFile_GetKeyValueAsString(
     839             :             hEnvisatFile, eMPHOrSPH, pszKey, nullptr);
     840             : 
     841          16 :         if (pszValue == nullptr)
     842           6 :             continue;
     843             : 
     844             :         // skip some uninteresting structural information.
     845          16 :         if (EQUAL(pszKey, "TOT_SIZE") || EQUAL(pszKey, "SPH_SIZE") ||
     846          14 :             EQUAL(pszKey, "NUM_DSD") || EQUAL(pszKey, "DSD_SIZE") ||
     847          10 :             EQUAL(pszKey, "NUM_DATA_SETS"))
     848           6 :             continue;
     849             : 
     850             :         char szHeaderKey[128];
     851          10 :         if (eMPHOrSPH == MPH)
     852           4 :             snprintf(szHeaderKey, sizeof(szHeaderKey), "MPH_%s", pszKey);
     853             :         else
     854           6 :             snprintf(szHeaderKey, sizeof(szHeaderKey), "SPH_%s", pszKey);
     855             : 
     856          10 :         SetMetadataItem(szHeaderKey, pszValue);
     857          16 :     }
     858           4 : }
     859             : 
     860             : /************************************************************************/
     861             : /*                                Open()                                */
     862             : /************************************************************************/
     863             : 
     864       40740 : GDALDataset *EnvisatDataset::Open(GDALOpenInfo *poOpenInfo)
     865             : 
     866             : {
     867             :     /* -------------------------------------------------------------------- */
     868             :     /*      Check the header.                                               */
     869             :     /* -------------------------------------------------------------------- */
     870       40740 :     if (poOpenInfo->nHeaderBytes < 8 || poOpenInfo->fpL == nullptr)
     871       33241 :         return nullptr;
     872             : 
     873        7499 :     if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "PRODUCT="))
     874        7497 :         return nullptr;
     875             : 
     876             :     /* -------------------------------------------------------------------- */
     877             :     /*      Try opening the dataset.                                        */
     878             :     /* -------------------------------------------------------------------- */
     879           2 :     EnvisatFile *hEnvisatFile = nullptr;
     880           2 :     if (EnvisatFile_Open(&hEnvisatFile, poOpenInfo->pszFilename, "r") ==
     881             :         FAILURE)
     882           0 :         return nullptr;
     883             : 
     884             :     /* -------------------------------------------------------------------- */
     885             :     /*      Find a measurement type dataset to use as our reference          */
     886             :     /*      raster band.                                                    */
     887             :     /* -------------------------------------------------------------------- */
     888             :     int dsr_size, num_dsr, ds_offset;
     889           2 :     const char *pszDSType = nullptr;
     890             : 
     891           2 :     int ds_index = 0;
     892           0 :     for (; true; ds_index++)
     893             :     {
     894           2 :         if (EnvisatFile_GetDatasetInfo(hEnvisatFile, ds_index, nullptr,
     895             :                                        &pszDSType, nullptr, &ds_offset, nullptr,
     896           2 :                                        &num_dsr, &dsr_size) == FAILURE)
     897             :         {
     898           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     899             :                      "Unable to find \"MDS1\" measurement dataset in "
     900             :                      "Envisat file.");
     901           0 :             EnvisatFile_Close(hEnvisatFile);
     902           0 :             return nullptr;
     903             :         }
     904             : 
     905             :         /* Have we found what we are looking for?  A Measurement ds. */
     906           2 :         if (EQUAL(pszDSType, "M"))
     907           2 :             break;
     908             :     }
     909             : 
     910             :     /* -------------------------------------------------------------------- */
     911             :     /*      Confirm the requested access is supported.                      */
     912             :     /* -------------------------------------------------------------------- */
     913           2 :     if (poOpenInfo->eAccess == GA_Update)
     914             :     {
     915           0 :         EnvisatFile_Close(hEnvisatFile);
     916           0 :         ReportUpdateNotSupportedByDriver("ENVISAT");
     917           0 :         return nullptr;
     918             :     }
     919             :     /* -------------------------------------------------------------------- */
     920             :     /*      Create a corresponding GDALDataset.                             */
     921             :     /* -------------------------------------------------------------------- */
     922           4 :     auto poDS = std::make_unique<EnvisatDataset>();
     923             : 
     924           2 :     poDS->hEnvisatFile = hEnvisatFile;
     925             : 
     926             :     /* -------------------------------------------------------------------- */
     927             :     /*      Setup image definition.                                         */
     928             :     /* -------------------------------------------------------------------- */
     929           2 :     EnvisatFile_GetDatasetInfo(hEnvisatFile, ds_index, nullptr, nullptr,
     930             :                                nullptr, &ds_offset, nullptr, &num_dsr,
     931             :                                &dsr_size);
     932             : 
     933           4 :     poDS->nRasterXSize =
     934           2 :         EnvisatFile_GetKeyValueAsInt(hEnvisatFile, SPH, "LINE_LENGTH", 0);
     935           2 :     poDS->nRasterYSize = num_dsr;
     936           2 :     poDS->eAccess = GA_ReadOnly;
     937             : 
     938             :     const char *pszProduct =
     939           2 :         EnvisatFile_GetKeyValueAsString(hEnvisatFile, MPH, "PRODUCT", "");
     940             :     const char *pszDataType =
     941           2 :         EnvisatFile_GetKeyValueAsString(hEnvisatFile, SPH, "DATA_TYPE", "");
     942             :     const char *pszSampleType =
     943           2 :         EnvisatFile_GetKeyValueAsString(hEnvisatFile, SPH, "SAMPLE_TYPE", "");
     944             : 
     945             :     GDALDataType eDataType;
     946           2 :     if (EQUAL(pszDataType, "FLT32") && STARTS_WITH_CI(pszSampleType, "COMPLEX"))
     947           0 :         eDataType = GDT_CFloat32;
     948           2 :     else if (EQUAL(pszDataType, "FLT32"))
     949           0 :         eDataType = GDT_Float32;
     950           2 :     else if (EQUAL(pszDataType, "UWORD"))
     951           2 :         eDataType = GDT_UInt16;
     952           0 :     else if (EQUAL(pszDataType, "SWORD") &&
     953           0 :              STARTS_WITH_CI(pszSampleType, "COMPLEX"))
     954           0 :         eDataType = GDT_CInt16;
     955           0 :     else if (EQUAL(pszDataType, "SWORD"))
     956           0 :         eDataType = GDT_Int16;
     957           0 :     else if (STARTS_WITH_CI(pszProduct, "ATS_TOA_1"))
     958             :     {
     959             :         /* all 16bit data, no line length provided */
     960           0 :         eDataType = GDT_Int16;
     961           0 :         poDS->nRasterXSize = (dsr_size - 20) / 2;
     962             :     }
     963           0 :     else if (poDS->nRasterXSize == 0)
     964             :     {
     965           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     966             :                  "Envisat product format not recognised.  Assuming 8bit\n"
     967             :                  "with no per-record prefix data.  Results may be useless!");
     968           0 :         eDataType = GDT_UInt8;
     969           0 :         poDS->nRasterXSize = dsr_size;
     970             :     }
     971             :     else
     972             :     {
     973           0 :         if (dsr_size >= 2 * poDS->nRasterXSize)
     974           0 :             eDataType = GDT_UInt16;
     975             :         else
     976           0 :             eDataType = GDT_UInt8;
     977             :     }
     978             : 
     979             :     const int nPrefixBytes =
     980           2 :         dsr_size - (GDALGetDataTypeSizeBytes(eDataType) * poDS->nRasterXSize);
     981             : 
     982             :     /* -------------------------------------------------------------------- */
     983             :     /*      Fail out if we didn't get non-zero sizes.                       */
     984             :     /* -------------------------------------------------------------------- */
     985           2 :     if (poDS->nRasterXSize < 1 || poDS->nRasterYSize < 1)
     986             :     {
     987           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     988             :                  "Unable to determine organization of dataset.  It would\n"
     989             :                  "appear this is an Envisat dataset, but an unsupported\n"
     990             :                  "data product.  Unable to utilize.");
     991           0 :         return nullptr;
     992             :     }
     993             : 
     994           2 :     std::swap(poDS->fpImage, poOpenInfo->fpL);
     995             : 
     996             :     /* -------------------------------------------------------------------- */
     997             :     /*      Try to collect GCPs.                                            */
     998             :     /* -------------------------------------------------------------------- */
     999             : 
    1000             :     /* -------------------------------------------------------------------- */
    1001             :     /*      Scan for all datasets matching the reference dataset.           */
    1002             :     /* -------------------------------------------------------------------- */
    1003           2 :     int num_dsr2, dsr_size2, iBand = 0;
    1004           2 :     const char *pszDSName = nullptr;
    1005             :     char szBandName[128];
    1006             :     bool bMiltiChannel;
    1007             : 
    1008           6 :     for (ds_index = 0;
    1009           6 :          EnvisatFile_GetDatasetInfo(hEnvisatFile, ds_index, &pszDSName, nullptr,
    1010             :                                     nullptr, &ds_offset, nullptr, &num_dsr2,
    1011           6 :                                     &dsr_size2) == SUCCESS;
    1012             :          ds_index++)
    1013             :     {
    1014           4 :         if (!EQUAL(pszDSType, "M") || num_dsr2 != num_dsr)
    1015           2 :             continue;
    1016             : 
    1017           2 :         if (STARTS_WITH_CI(pszProduct, "MER") && (pszProduct[8] == '2') &&
    1018           0 :             ((strstr(pszDSName, "MDS(16)") != nullptr) ||
    1019           0 :              (strstr(pszDSName, "MDS(19)") != nullptr)))
    1020           0 :             bMiltiChannel = true;
    1021             :         else
    1022           2 :             bMiltiChannel = false;
    1023             : 
    1024           2 :         if ((dsr_size2 == dsr_size) && !bMiltiChannel)
    1025             :         {
    1026             :             auto poBand = RawRasterBand::Create(
    1027           4 :                 poDS.get(), iBand + 1, poDS->fpImage, ds_offset + nPrefixBytes,
    1028             :                 GDALGetDataTypeSizeBytes(eDataType), dsr_size, eDataType,
    1029             :                 RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    1030           2 :                 RawRasterBand::OwnFP::NO);
    1031           2 :             if (!poBand)
    1032           0 :                 return nullptr;
    1033           2 :             poBand->SetDescription(pszDSName);
    1034           2 :             poDS->SetBand(iBand + 1, std::move(poBand));
    1035           4 :             iBand++;
    1036             :         }
    1037             :         /* --------------------------------------------------------------------
    1038             :          */
    1039             :         /*       Handle MERIS Level 2 datasets with data type different from */
    1040             :         /*       the one declared in the SPH */
    1041             :         /* --------------------------------------------------------------------
    1042             :          */
    1043           0 :         else if (STARTS_WITH_CI(pszProduct, "MER") &&
    1044           0 :                  (strstr(pszDSName, "Flags") != nullptr))
    1045             :         {
    1046           0 :             if (pszProduct[8] == '1')
    1047             :             {
    1048             :                 // Flags
    1049             :                 {
    1050             :                     auto poBand = RawRasterBand::Create(
    1051           0 :                         poDS.get(), iBand + 1, poDS->fpImage,
    1052           0 :                         ds_offset + nPrefixBytes, 3, dsr_size, GDT_UInt8,
    1053             :                         RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    1054           0 :                         RawRasterBand::OwnFP::NO);
    1055           0 :                     if (!poBand)
    1056           0 :                         return nullptr;
    1057           0 :                     poBand->SetDescription(pszDSName);
    1058           0 :                     poDS->SetBand(iBand + 1, std::move(poBand));
    1059           0 :                     iBand++;
    1060             :                 }
    1061             : 
    1062             :                 // Detector indices
    1063             :                 auto poBand = RawRasterBand::Create(
    1064           0 :                     poDS.get(), iBand + 1, poDS->fpImage,
    1065           0 :                     ds_offset + nPrefixBytes + 1, 3, dsr_size, GDT_Int16,
    1066             :                     RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    1067           0 :                     RawRasterBand::OwnFP::NO);
    1068           0 :                 if (!poBand)
    1069           0 :                     return nullptr;
    1070             : 
    1071           0 :                 const char *pszSuffix = strstr(pszDSName, "MDS");
    1072           0 :                 if (pszSuffix != nullptr)
    1073           0 :                     snprintf(szBandName, sizeof(szBandName),
    1074             :                              "Detector index %s", pszSuffix);
    1075             :                 else
    1076           0 :                     snprintf(szBandName, sizeof(szBandName), "%s",
    1077             :                              "Detector index");
    1078           0 :                 poBand->SetDescription(szBandName);
    1079             : 
    1080           0 :                 poDS->SetBand(iBand + 1, std::move(poBand));
    1081           0 :                 iBand++;
    1082             :             }
    1083           0 :             else if ((pszProduct[8] == '2') &&
    1084           0 :                      (dsr_size2 >= 3 * poDS->nRasterXSize))
    1085             :             {
    1086           0 :                 int nFlagPrefixBytes = dsr_size2 - 3 * poDS->nRasterXSize;
    1087             : 
    1088             :                 auto poBand =
    1089           0 :                     new MerisL2FlagBand(poDS.get(), iBand + 1, poDS->fpImage,
    1090           0 :                                         ds_offset, nFlagPrefixBytes);
    1091           0 :                 poBand->SetDescription(pszDSName);
    1092           0 :                 poDS->SetBand(iBand + 1, poBand);
    1093           0 :                 iBand++;
    1094           0 :             }
    1095             :         }
    1096           0 :         else if (STARTS_WITH_CI(pszProduct, "MER") && (pszProduct[8] == '2'))
    1097             :         {
    1098             :             int nPrefixBytes2, nSubBands, nSubBandIdx, nSubBandOffset;
    1099             : 
    1100           0 :             int nPixelSize = 1;
    1101           0 :             GDALDataType eDataType2 = GDT_UInt8;
    1102             : 
    1103           0 :             nSubBands = dsr_size2 / poDS->nRasterXSize;
    1104           0 :             if ((nSubBands < 1) || (nSubBands > 3))
    1105           0 :                 nSubBands = 0;
    1106             : 
    1107           0 :             nPrefixBytes2 =
    1108           0 :                 dsr_size2 - (nSubBands * nPixelSize * poDS->nRasterXSize);
    1109             : 
    1110           0 :             for (nSubBandIdx = 0; nSubBandIdx < nSubBands; ++nSubBandIdx)
    1111             :             {
    1112           0 :                 nSubBandOffset =
    1113           0 :                     ds_offset + nPrefixBytes2 + nSubBandIdx * nPixelSize;
    1114             : 
    1115             :                 auto poBand = RawRasterBand::Create(
    1116           0 :                     poDS.get(), iBand + 1, poDS->fpImage, nSubBandOffset,
    1117             :                     nPixelSize * nSubBands, dsr_size2, eDataType2,
    1118             :                     RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    1119           0 :                     RawRasterBand::OwnFP::NO);
    1120           0 :                 if (!poBand)
    1121           0 :                     return nullptr;
    1122             : 
    1123           0 :                 if (nSubBands > 1)
    1124             :                 {
    1125           0 :                     snprintf(szBandName, sizeof(szBandName), "%s (%d)",
    1126             :                              pszDSName, nSubBandIdx);
    1127           0 :                     poBand->SetDescription(szBandName);
    1128             :                 }
    1129             :                 else
    1130           0 :                     poBand->SetDescription(pszDSName);
    1131             : 
    1132           0 :                 poDS->SetBand(iBand + 1, std::move(poBand));
    1133           0 :                 iBand++;
    1134             :             }
    1135             :         }
    1136             :     }
    1137             : 
    1138             :     /* -------------------------------------------------------------------- */
    1139             :     /*      Collect metadata.                                               */
    1140             :     /* -------------------------------------------------------------------- */
    1141           2 :     poDS->CollectMetadata(MPH);
    1142           2 :     poDS->CollectMetadata(SPH);
    1143           2 :     poDS->CollectDSDMetadata();
    1144           2 :     poDS->CollectADSMetadata();
    1145             : 
    1146           2 :     if (STARTS_WITH_CI(pszProduct, "MER"))
    1147           0 :         poDS->ScanForGCPs_MERIS();
    1148             :     else
    1149           2 :         poDS->ScanForGCPs_ASAR();
    1150             : 
    1151             :     /* unwrap GCPs for products crossing date border */
    1152           2 :     poDS->UnwrapGCPs();
    1153             : 
    1154             :     /* -------------------------------------------------------------------- */
    1155             :     /*      Initialize any PAM information.                                 */
    1156             :     /* -------------------------------------------------------------------- */
    1157           2 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1158           2 :     poDS->TryLoadXML();
    1159             : 
    1160             :     /* -------------------------------------------------------------------- */
    1161             :     /*      Check for overviews.                                            */
    1162             :     /* -------------------------------------------------------------------- */
    1163           2 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
    1164             : 
    1165           2 :     return poDS.release();
    1166             : }
    1167             : 
    1168             : /************************************************************************/
    1169             : /*                        GDALRegister_Envisat()                        */
    1170             : /************************************************************************/
    1171             : 
    1172        2138 : void GDALRegister_Envisat()
    1173             : 
    1174             : {
    1175        2138 :     if (GDALGetDriverByName("ESAT") != nullptr)
    1176         263 :         return;
    1177             : 
    1178        1875 :     GDALDriver *poDriver = new GDALDriver();
    1179             : 
    1180        1875 :     poDriver->SetDescription("ESAT");
    1181        1875 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1182        1875 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Envisat Image Format");
    1183        1875 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/esat.html");
    1184        1875 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "n1");
    1185        1875 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1186             : 
    1187        1875 :     poDriver->pfnOpen = EnvisatDataset::Open;
    1188             : 
    1189        1875 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1190             : }

Generated by: LCOV version 1.14