LCOV - code coverage report
Current view: top level - frmts/envisat - envisatdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 17 475 3.6 %
Date: 2025-10-21 22:35:35 Functions: 2 21 9.5 %

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

Generated by: LCOV version 1.14