LCOV - code coverage report
Current view: top level - frmts/ceos2 - sar_ceosdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 568 992 57.3 %
Date: 2026-04-15 22:10:00 Functions: 13 24 54.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ASI CEOS Translator
       4             :  * Purpose:  GDALDataset driver for CEOS translator.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, 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 "ceos.h"
      15             : #include "cpl_string.h"
      16             : #include "gdal_frmts.h"
      17             : #include "gdal_priv.h"
      18             : #include "rawdataset.h"
      19             : #include "ogr_srs_api.h"
      20             : #include "cpl_vsi_virtual.h"
      21             : 
      22             : #include <algorithm>
      23             : #include <cinttypes>
      24             : 
      25           0 : static GInt16 CastToGInt16(float val)
      26             : {
      27           0 :     if (val < -32768.0)
      28           0 :         val = -32768.0;
      29             : 
      30           0 :     if (val > 32767)
      31           0 :         val = 32767.0;
      32             : 
      33           0 :     return static_cast<GInt16>(val);
      34             : }
      35             : 
      36             : // CeosExtension[][i] ordered by i:
      37             : // CEOS_VOLUME_DIR_FILE = 0
      38             : // CEOS_LEADER_FILE     = 1
      39             : // CEOS_IMAGRY_OPT_FILE = 2
      40             : // CEOS_TRAILER_FILE    = 3
      41             : // CEOS_NULL_VOL_FILE   = 4
      42             : // and last item (idx 5) is the method to derive auxiliary filenames
      43             : static const char *const CeosExtension[][CEOS_FILE_COUNT + 1] = {
      44             :     {"vol", "led", "img", "trl", "nul", "ext"},
      45             :     {"vol", "lea", "img", "trl", "nul", "ext"},
      46             :     {"vol", "led", "img", "tra", "nul", "ext"},
      47             :     {"vol", "lea", "img", "tra", "nul", "ext"},
      48             :     {"vdf", "slf", "sdf", "stf", "nvd", "ext"},
      49             : 
      50             :     {"vdf", "ldr", "img", "tra", "nul", "ext2"},
      51             : 
      52             :     /* Jers from Japan- not sure if this is generalized as much as it could be
      53             :      */
      54             :     {"VOLD", "Sarl_01", "Imop_%02d", "Sart_01", "NULL", "base"},
      55             : 
      56             :     /* Radarsat: basename, not extension */
      57             :     {"vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_vdf", "base"},
      58             : 
      59             :     /* Ers-1: basename, not extension */
      60             :     {"vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_dat", "base"},
      61             : 
      62             :     /* Ers-2 from Telaviv */
      63             :     {"volume", "leader", "image", "trailer", "nul_dat", "whole"},
      64             : 
      65             :     /* Ers-1 from D-PAF */
      66             :     {"VDF", "LF", "SLC", "", "", "ext"},
      67             : 
      68             :     /* Radarsat-1 per #2051 */
      69             :     {"vol", "sarl", "sard", "sart", "nvol", "ext"},
      70             : 
      71             :     /* Radarsat-1 ASF */
      72             :     {"", "L", "D", "", "", "ext"},
      73             : 
      74             :     /* PALSAR-2 ALOS2 / PALSAR-3 ALOS4 */
      75             :     {"VOL", "LED", "", "TRL", "", "ALOS2-ALOS4"},
      76             : 
      77             :     /* end marker */
      78             :     {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
      79             : 
      80             : static int ProcessData(VSILFILE *fp, int fileid, CeosSARVolume_t *sar,
      81             :                        int max_records, vsi_l_offset max_bytes,
      82             :                        bool bSilentWrongRecordNumber);
      83             : 
      84         124 : static CeosTypeCode_t QuadToTC(int a, int b, int c, int d)
      85             : {
      86             :     CeosTypeCode_t abcd;
      87             : 
      88         124 :     abcd.UCharCode.Subtype1 = (unsigned char)a;
      89         124 :     abcd.UCharCode.Type = (unsigned char)b;
      90         124 :     abcd.UCharCode.Subtype2 = (unsigned char)c;
      91         124 :     abcd.UCharCode.Subtype3 = (unsigned char)d;
      92             : 
      93         124 :     return abcd;
      94             : }
      95             : 
      96             : #define LEADER_DATASET_SUMMARY_TC QuadToTC(18, 10, 18, 20)
      97             : #define LEADER_DATASET_SUMMARY_ERS2_TC QuadToTC(10, 10, 31, 20)
      98             : #define LEADER_RADIOMETRIC_COMPENSATION_TC QuadToTC(18, 51, 18, 20)
      99             : #define VOLUME_DESCRIPTOR_RECORD_TC QuadToTC(192, 192, 18, 18)
     100             : #define IMAGE_HEADER_RECORD_TC QuadToTC(63, 192, 18, 18)
     101             : #define LEADER_RADIOMETRIC_DATA_RECORD_TC QuadToTC(18, 50, 18, 20)
     102             : #define LEADER_MAP_PROJ_RECORD_TC QuadToTC(10, 20, 31, 20)
     103             : 
     104             : /* JERS from Japan has MAP_PROJ record with different identifiers */
     105             : /* see CEOS-SAR-CCT Iss/Rev: 2/0 February 10, 1989 */
     106             : #define LEADER_MAP_PROJ_RECORD_JERS_TC QuadToTC(18, 20, 18, 20)
     107             : 
     108             : /* Leader from ASF has different identifiers */
     109             : #define LEADER_MAP_PROJ_RECORD_ASF_TC QuadToTC(10, 20, 18, 20)
     110             : #define LEADER_DATASET_SUMMARY_ASF_TC QuadToTC(10, 10, 18, 20)
     111             : #define LEADER_FACILITY_ASF_TC QuadToTC(90, 210, 18, 61)
     112             : 
     113             : /* For ERS calibration and incidence angle information */
     114             : #define ERS_GENERAL_FACILITY_DATA_TC QuadToTC(10, 200, 31, 50)
     115             : #define ERS_GENERAL_FACILITY_DATA_ALT_TC QuadToTC(10, 216, 31, 50)
     116             : 
     117             : #define RSAT_PROC_PARAM_TC QuadToTC(18, 120, 18, 20)
     118             : 
     119             : /* PALSAR-2 ALOS2 */
     120             : // https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
     121             : // Table 3.2-3 Record Type of Each Record
     122             : #define LEADER_PLATFORM_POSITION_PALSAR_TC QuadToTC(18, 30, 18, 20)
     123             : #define LEADER_ATTITUDE_PALSAR_TC QuadToTC(18, 40, 18, 20)
     124             : #define LEADER_RADIOMETRIC_PALSAR_TC QuadToTC(18, 50, 18, 20)
     125             : #define LEADER_FACILITY_RELATED_PALSAR_TC QuadToTC(18, 200, 18, 70)
     126             : #define IMAGE_PROCESSED_DATA_RECORD_PALSAR_TC QuadToTC(50, 11, 18, 20)
     127             : 
     128             : /************************************************************************/
     129             : /* ==================================================================== */
     130             : /*                              SAR_CEOSDataset                         */
     131             : /* ==================================================================== */
     132             : /************************************************************************/
     133             : 
     134             : class SAR_CEOSRasterBand;
     135             : class CCPRasterBand;
     136             : class PALSARRasterBand;
     137             : 
     138             : class SAR_CEOSDataset final : public GDALPamDataset
     139             : {
     140             :     friend class SAR_CEOSRasterBand;
     141             :     friend class CCPRasterBand;
     142             :     friend class PALSARRasterBand;
     143             : 
     144             :     CeosSARVolume_t sVolume;
     145             : 
     146             :     VSILFILE *fpImage;
     147             : 
     148             :     char **papszTempMD;
     149             : 
     150             :     bool m_bHasScannedForGCP = false;
     151             :     OGRSpatialReference m_oSRS{};
     152             :     int nGCPCount;
     153             :     GDAL_GCP *pasGCPList;
     154             : 
     155             :     void ScanForGCPs();
     156             :     void ScanForMetadata();
     157             :     int ScanForMapProjection();
     158             :     char **papszExtraFiles;
     159             : 
     160             :   public:
     161             :     SAR_CEOSDataset();
     162             :     ~SAR_CEOSDataset() override;
     163             : 
     164             :     int GetGCPCount() override;
     165             :     const OGRSpatialReference *GetGCPSpatialRef() const override;
     166             :     const GDAL_GCP *GetGCPs() override;
     167             : 
     168             :     char **GetMetadataDomainList() override;
     169             :     CSLConstList GetMetadata(const char *pszDomain) override;
     170             : 
     171             :     static GDALDataset *Open(GDALOpenInfo *);
     172             :     char **GetFileList(void) override;
     173             : };
     174             : 
     175             : /************************************************************************/
     176             : /* ==================================================================== */
     177             : /*                          CCPRasterBand                               */
     178             : /* ==================================================================== */
     179             : /************************************************************************/
     180             : 
     181             : class CCPRasterBand final : public GDALPamRasterBand
     182             : {
     183             :     friend class SAR_CEOSDataset;
     184             : 
     185             :   public:
     186             :     CCPRasterBand(SAR_CEOSDataset *, int, GDALDataType);
     187             : 
     188             :     CPLErr IReadBlock(int, int, void *) override;
     189             : };
     190             : 
     191             : /************************************************************************/
     192             : /* ==================================================================== */
     193             : /*                        PALSARRasterBand                              */
     194             : /* ==================================================================== */
     195             : /************************************************************************/
     196             : 
     197             : class PALSARRasterBand final : public GDALPamRasterBand
     198             : {
     199             :     friend class SAR_CEOSDataset;
     200             : 
     201             :   public:
     202             :     PALSARRasterBand(SAR_CEOSDataset *, int);
     203             : 
     204             :     CPLErr IReadBlock(int, int, void *) override;
     205             : };
     206             : 
     207             : /************************************************************************/
     208             : /* ==================================================================== */
     209             : /*                       SAR_CEOSRasterBand                             */
     210             : /* ==================================================================== */
     211             : /************************************************************************/
     212             : 
     213             : class SAR_CEOSRasterBand final : public GDALPamRasterBand
     214             : {
     215             :     friend class SAR_CEOSDataset;
     216             : 
     217             :   public:
     218             :     SAR_CEOSRasterBand(SAR_CEOSDataset *, int, GDALDataType);
     219             : 
     220             :     CPLErr IReadBlock(int, int, void *) override;
     221             : };
     222             : 
     223             : /************************************************************************/
     224             : /*                         SAR_CEOSRasterBand()                         */
     225             : /************************************************************************/
     226             : 
     227           0 : SAR_CEOSRasterBand::SAR_CEOSRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn,
     228           0 :                                        GDALDataType eType)
     229             : 
     230             : {
     231           0 :     poDS = poGDSIn;
     232           0 :     nBand = nBandIn;
     233             : 
     234           0 :     eDataType = eType;
     235             : 
     236           0 :     nBlockXSize = poGDSIn->nRasterXSize;
     237           0 :     nBlockYSize = 1;
     238           0 : }
     239             : 
     240             : /************************************************************************/
     241             : /*                             IReadBlock()                             */
     242             : /************************************************************************/
     243             : 
     244           0 : CPLErr SAR_CEOSRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
     245             :                                       void *pImage)
     246             : {
     247           0 :     SAR_CEOSDataset *poGDS = cpl::down_cast<SAR_CEOSDataset *>(poDS);
     248             : 
     249           0 :     struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
     250             : 
     251           0 :     uint64_t offset = 0;
     252           0 :     CalcCeosSARImageFilePosition(&(poGDS->sVolume), nBand, nBlockYOff + 1,
     253             :                                  nullptr, &offset);
     254             : 
     255           0 :     offset += ImageDesc->ImageDataStart;
     256             : 
     257             :     /* -------------------------------------------------------------------- */
     258             :     /*      Load all the pixel data associated with this scanline.          */
     259             :     /*      Ensure we handle multiple record scanlines properly.            */
     260             :     /* -------------------------------------------------------------------- */
     261           0 :     int nPixelsRead = 0;
     262             : 
     263             :     GByte *pabyRecord =
     264           0 :         (GByte *)VSI_MALLOC2_VERBOSE(ImageDesc->BytesPerPixel, nBlockXSize);
     265           0 :     if (!pabyRecord)
     266           0 :         return CE_Failure;
     267             : 
     268           0 :     for (int iRecord = 0; iRecord < ImageDesc->RecordsPerLine; iRecord++)
     269             :     {
     270             :         int nPixelsToRead;
     271             : 
     272           0 :         if (nPixelsRead + ImageDesc->PixelsPerRecord > nBlockXSize)
     273           0 :             nPixelsToRead = nBlockXSize - nPixelsRead;
     274             :         else
     275           0 :             nPixelsToRead = ImageDesc->PixelsPerRecord;
     276             : 
     277           0 :         CPL_IGNORE_RET_VAL(VSIFSeekL(poGDS->fpImage, offset, SEEK_SET));
     278           0 :         CPL_IGNORE_RET_VAL(VSIFReadL(
     279           0 :             pabyRecord +
     280           0 :                 static_cast<size_t>(nPixelsRead) * ImageDesc->BytesPerPixel,
     281           0 :             1, static_cast<size_t>(nPixelsToRead) * ImageDesc->BytesPerPixel,
     282             :             poGDS->fpImage));
     283             : 
     284           0 :         nPixelsRead += nPixelsToRead;
     285           0 :         offset += ImageDesc->BytesPerRecord;
     286             :     }
     287             : 
     288             :     /* -------------------------------------------------------------------- */
     289             :     /*      Copy the desired band out based on the size of the type, and    */
     290             :     /*      the interleaving mode.                                          */
     291             :     /* -------------------------------------------------------------------- */
     292           0 :     const int nBytesPerSample = GDALGetDataTypeSizeBytes(eDataType);
     293             : 
     294           0 :     if (ImageDesc->ChannelInterleaving == CEOS_IL_PIXEL)
     295             :     {
     296           0 :         GDALCopyWords(pabyRecord + (nBand - 1) * nBytesPerSample, eDataType,
     297             :                       ImageDesc->BytesPerPixel, pImage, eDataType,
     298             :                       nBytesPerSample, nBlockXSize);
     299             :     }
     300           0 :     else if (ImageDesc->ChannelInterleaving == CEOS_IL_LINE)
     301             :     {
     302           0 :         GDALCopyWords(pabyRecord + (nBand - 1) * nBytesPerSample * nBlockXSize,
     303             :                       eDataType, nBytesPerSample, pImage, eDataType,
     304             :                       nBytesPerSample, nBlockXSize);
     305             :     }
     306           0 :     else if (ImageDesc->ChannelInterleaving == CEOS_IL_BAND)
     307             :     {
     308           0 :         memcpy(pImage, pabyRecord,
     309           0 :                static_cast<size_t>(nBytesPerSample) * nBlockXSize);
     310             :     }
     311             : 
     312             : #ifdef CPL_LSB
     313           0 :     GDALSwapWords(pImage, nBytesPerSample, nBlockXSize, nBytesPerSample);
     314             : #endif
     315             : 
     316           0 :     CPLFree(pabyRecord);
     317             : 
     318           0 :     return CE_None;
     319             : }
     320             : 
     321             : /************************************************************************/
     322             : /* ==================================================================== */
     323             : /*                              CCPRasterBand                           */
     324             : /* ==================================================================== */
     325             : /************************************************************************/
     326             : 
     327             : /************************************************************************/
     328             : /*                           CCPRasterBand()                            */
     329             : /************************************************************************/
     330             : 
     331           0 : CCPRasterBand::CCPRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn,
     332           0 :                              GDALDataType eType)
     333             : 
     334             : {
     335           0 :     poDS = poGDSIn;
     336           0 :     nBand = nBandIn;
     337             : 
     338           0 :     eDataType = eType;
     339             : 
     340           0 :     nBlockXSize = poGDSIn->nRasterXSize;
     341           0 :     nBlockYSize = 1;
     342             : 
     343           0 :     if (nBand == 1)
     344           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "HH");
     345           0 :     else if (nBand == 2)
     346           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "HV");
     347           0 :     else if (nBand == 3)
     348           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "VH");
     349           0 :     else if (nBand == 4)
     350           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "VV");
     351           0 : }
     352             : 
     353             : /************************************************************************/
     354             : /*                             IReadBlock()                             */
     355             : /************************************************************************/
     356             : 
     357             : /* From: http://southport.jpl.nasa.gov/software/dcomp/dcomp.html
     358             : 
     359             : ysca = sqrt{ [ (Byte(2) / 254 ) + 1.5] 2Byte(1) }
     360             : 
     361             : Re(SHH) = byte(3) ysca/127
     362             : 
     363             : Im(SHH) = byte(4) ysca/127
     364             : 
     365             : Re(SHV) = byte(5) ysca/127
     366             : 
     367             : Im(SHV) = byte(6) ysca/127
     368             : 
     369             : Re(SVH) = byte(7) ysca/127
     370             : 
     371             : Im(SVH) = byte(8) ysca/127
     372             : 
     373             : Re(SVV) = byte(9) ysca/127
     374             : 
     375             : Im(SVV) = byte(10) ysca/127
     376             : 
     377             : */
     378             : 
     379           0 : CPLErr CCPRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     380             :                                  void *pImage)
     381             : {
     382           0 :     SAR_CEOSDataset *poGDS = cpl::down_cast<SAR_CEOSDataset *>(poDS);
     383             : 
     384           0 :     struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
     385             : 
     386           0 :     const vsi_l_offset offset =
     387           0 :         ImageDesc->FileDescriptorLength +
     388           0 :         static_cast<vsi_l_offset>(ImageDesc->BytesPerRecord) * nBlockYOff +
     389           0 :         ImageDesc->ImageDataStart;
     390             : 
     391             :     /* -------------------------------------------------------------------- */
     392             :     /*      Load all the pixel data associated with this scanline.          */
     393             :     /* -------------------------------------------------------------------- */
     394           0 :     const int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
     395             : 
     396           0 :     GByte *pabyRecord = (GByte *)CPLMalloc(nBytesToRead);
     397             : 
     398           0 :     if (VSIFSeekL(poGDS->fpImage, offset, SEEK_SET) != 0 ||
     399           0 :         (int)VSIFReadL(pabyRecord, 1, nBytesToRead, poGDS->fpImage) !=
     400             :             nBytesToRead)
     401             :     {
     402           0 :         CPLError(CE_Failure, CPLE_FileIO,
     403             :                  "Error reading %d bytes of CEOS record data at offset %" PRIu64
     404             :                  ".\n"
     405             :                  "Reading file %s failed.",
     406             :                  nBytesToRead, static_cast<uint64_t>(offset),
     407           0 :                  poGDS->GetDescription());
     408           0 :         CPLFree(pabyRecord);
     409           0 :         return CE_Failure;
     410             :     }
     411             : 
     412             :     /* -------------------------------------------------------------------- */
     413             :     /*      Initialize our power table if this is our first time through.   */
     414             :     /* -------------------------------------------------------------------- */
     415             :     static float afPowTable[256];
     416             :     static bool bPowTableInitialized = false;
     417             : 
     418           0 :     if (!bPowTableInitialized)
     419             :     {
     420           0 :         bPowTableInitialized = true;
     421             : 
     422           0 :         for (int i = 0; i < 256; i++)
     423             :         {
     424           0 :             afPowTable[i] = (float)pow(2.0, i - 128);
     425             :         }
     426             :     }
     427             : 
     428             :     /* -------------------------------------------------------------------- */
     429             :     /*      Copy the desired band out based on the size of the type, and    */
     430             :     /*      the interleaving mode.                                          */
     431             :     /* -------------------------------------------------------------------- */
     432           0 :     for (int iX = 0; iX < nBlockXSize; iX++)
     433             :     {
     434           0 :         unsigned char *pabyGroup = pabyRecord + iX * ImageDesc->BytesPerPixel;
     435           0 :         signed char *Byte =
     436             :             (signed char *)pabyGroup - 1; /* A ones based alias */
     437             :         double dfReSHH, dfImSHH, dfReSHV, dfImSHV, dfReSVH, dfImSVH, dfReSVV,
     438             :             dfImSVV;
     439             : 
     440             :         const double dfScale =
     441           0 :             sqrt((Byte[2] / 254.0 + 1.5) * afPowTable[Byte[1] + 128]);
     442             : 
     443           0 :         if (nBand == 1)
     444             :         {
     445           0 :             dfReSHH = Byte[3] * dfScale / 127.0;
     446           0 :             dfImSHH = Byte[4] * dfScale / 127.0;
     447             : 
     448           0 :             ((float *)pImage)[iX * 2] = (float)dfReSHH;
     449           0 :             ((float *)pImage)[iX * 2 + 1] = (float)dfImSHH;
     450             :         }
     451           0 :         else if (nBand == 2)
     452             :         {
     453           0 :             dfReSHV = Byte[5] * dfScale / 127.0;
     454           0 :             dfImSHV = Byte[6] * dfScale / 127.0;
     455             : 
     456           0 :             ((float *)pImage)[iX * 2] = (float)dfReSHV;
     457           0 :             ((float *)pImage)[iX * 2 + 1] = (float)dfImSHV;
     458             :         }
     459           0 :         else if (nBand == 3)
     460             :         {
     461           0 :             dfReSVH = Byte[7] * dfScale / 127.0;
     462           0 :             dfImSVH = Byte[8] * dfScale / 127.0;
     463             : 
     464           0 :             ((float *)pImage)[iX * 2] = (float)dfReSVH;
     465           0 :             ((float *)pImage)[iX * 2 + 1] = (float)dfImSVH;
     466             :         }
     467           0 :         else if (nBand == 4)
     468             :         {
     469           0 :             dfReSVV = Byte[9] * dfScale / 127.0;
     470           0 :             dfImSVV = Byte[10] * dfScale / 127.0;
     471             : 
     472           0 :             ((float *)pImage)[iX * 2] = (float)dfReSVV;
     473           0 :             ((float *)pImage)[iX * 2 + 1] = (float)dfImSVV;
     474             :         }
     475             :     }
     476             : 
     477           0 :     CPLFree(pabyRecord);
     478             : 
     479           0 :     return CE_None;
     480             : }
     481             : 
     482             : /************************************************************************/
     483             : /* ==================================================================== */
     484             : /*                            PALSARRasterBand                          */
     485             : /* ==================================================================== */
     486             : /************************************************************************/
     487             : 
     488             : /************************************************************************/
     489             : /*                          PALSARRasterBand()                          */
     490             : /************************************************************************/
     491             : 
     492           0 : PALSARRasterBand::PALSARRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn)
     493             : 
     494             : {
     495           0 :     poDS = poGDSIn;
     496           0 :     nBand = nBandIn;
     497             : 
     498           0 :     eDataType = GDT_CInt16;
     499             : 
     500           0 :     nBlockXSize = poGDSIn->nRasterXSize;
     501           0 :     nBlockYSize = 1;
     502             : 
     503           0 :     if (nBand == 1)
     504           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_11");
     505           0 :     else if (nBand == 2)
     506           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_22");
     507           0 :     else if (nBand == 3)
     508           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_33");
     509           0 :     else if (nBand == 4)
     510           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_12");
     511           0 :     else if (nBand == 5)
     512           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_13");
     513           0 :     else if (nBand == 6)
     514           0 :         SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_23");
     515           0 : }
     516             : 
     517             : /************************************************************************/
     518             : /*                             IReadBlock()                             */
     519             : /*                                                                      */
     520             : /*      Based on ERSDAC-VX-CEOS-004                                     */
     521             : /************************************************************************/
     522             : 
     523           0 : CPLErr PALSARRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
     524             :                                     void *pImage)
     525             : {
     526           0 :     SAR_CEOSDataset *poGDS = cpl::down_cast<SAR_CEOSDataset *>(poDS);
     527             : 
     528           0 :     struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
     529             : 
     530           0 :     const vsi_l_offset offset =
     531           0 :         ImageDesc->FileDescriptorLength +
     532           0 :         static_cast<vsi_l_offset>(ImageDesc->BytesPerRecord) * nBlockYOff +
     533           0 :         ImageDesc->ImageDataStart;
     534             : 
     535             :     /* -------------------------------------------------------------------- */
     536             :     /*      Load all the pixel data associated with this scanline.          */
     537             :     /* -------------------------------------------------------------------- */
     538           0 :     const int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
     539             : 
     540           0 :     GByte *pabyRecord = (GByte *)CPLMalloc(nBytesToRead);
     541             : 
     542           0 :     if (VSIFSeekL(poGDS->fpImage, offset, SEEK_SET) != 0 ||
     543           0 :         (int)VSIFReadL(pabyRecord, 1, nBytesToRead, poGDS->fpImage) !=
     544             :             nBytesToRead)
     545             :     {
     546           0 :         CPLError(CE_Failure, CPLE_FileIO,
     547             :                  "Error reading %d bytes of CEOS record data at offset %" PRIu64
     548             :                  ".\n"
     549             :                  "Reading file %s failed.",
     550             :                  nBytesToRead, static_cast<uint64_t>(offset),
     551           0 :                  poGDS->GetDescription());
     552           0 :         CPLFree(pabyRecord);
     553           0 :         return CE_Failure;
     554             :     }
     555             : 
     556             :     /* -------------------------------------------------------------------- */
     557             :     /*      Copy the desired band out based on the size of the type, and    */
     558             :     /*      the interleaving mode.                                          */
     559             :     /* -------------------------------------------------------------------- */
     560           0 :     if (nBand == 1 || nBand == 2 || nBand == 3)
     561             :     {
     562             :         // we need to pre-initialize things to set the imaginary component to 0
     563           0 :         memset(pImage, 0, nBlockXSize * 4);
     564             : 
     565           0 :         GDALCopyWords(pabyRecord + 4 * (nBand - 1), GDT_Int16, 18, pImage,
     566             :                       GDT_Int16, 4, nBlockXSize);
     567             : #ifdef CPL_LSB
     568           0 :         GDALSwapWords(pImage, 2, nBlockXSize, 4);
     569             : #endif
     570             :     }
     571             :     else
     572             :     {
     573           0 :         GDALCopyWords(pabyRecord + 6 + 4 * (nBand - 4), GDT_CInt16, 18, pImage,
     574             :                       GDT_CInt16, 4, nBlockXSize);
     575             : #ifdef CPL_LSB
     576           0 :         GDALSwapWords(pImage, 2, nBlockXSize * 2, 2);
     577             : #endif
     578             :     }
     579           0 :     CPLFree(pabyRecord);
     580             : 
     581             :     /* -------------------------------------------------------------------- */
     582             :     /*      Convert the values into covariance form as per:                 */
     583             :     /* -------------------------------------------------------------------- */
     584             :     /*
     585             :     ** 1) PALSAR- adjust so that it reads bands as a covariance matrix, and
     586             :     ** set polarimetric interpretation accordingly:
     587             :     **
     588             :     ** Covariance_11=HH*conj(HH): already there
     589             :     ** Covariance_22=2*HV*conj(HV): need a factor of 2
     590             :     ** Covariance_33=VV*conj(VV): already there
     591             :     ** Covariance_12=sqrt(2)*HH*conj(HV): need the sqrt(2) factor
     592             :     ** Covariance_13=HH*conj(VV): already there
     593             :     ** Covariance_23=sqrt(2)*HV*conj(VV): need to take the conjugate, then
     594             :     **               multiply by sqrt(2)
     595             :     **
     596             :     */
     597             : 
     598           0 :     if (nBand == 2)
     599             :     {
     600           0 :         GInt16 *panLine = (GInt16 *)pImage;
     601             : 
     602           0 :         for (int i = 0; i < nBlockXSize * 2; i++)
     603             :         {
     604           0 :             panLine[i] = (GInt16)CastToGInt16((float)2.0 * panLine[i]);
     605             :         }
     606             :     }
     607           0 :     else if (nBand == 4)
     608             :     {
     609           0 :         const double sqrt_2 = pow(2.0, 0.5);
     610           0 :         GInt16 *panLine = (GInt16 *)pImage;
     611             : 
     612           0 :         for (int i = 0; i < nBlockXSize * 2; i++)
     613             :         {
     614           0 :             panLine[i] =
     615           0 :                 (GInt16)CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5));
     616             :         }
     617             :     }
     618           0 :     else if (nBand == 6)
     619             :     {
     620           0 :         GInt16 *panLine = (GInt16 *)pImage;
     621           0 :         const double sqrt_2 = pow(2.0, 0.5);
     622             : 
     623             :         // real portion - just multiple by sqrt(2)
     624           0 :         for (int i = 0; i < nBlockXSize * 2; i += 2)
     625             :         {
     626           0 :             panLine[i] =
     627           0 :                 (GInt16)CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5));
     628             :         }
     629             : 
     630             :         // imaginary portion - conjugate and multiply
     631           0 :         for (int i = 1; i < nBlockXSize * 2; i += 2)
     632             :         {
     633           0 :             panLine[i] =
     634           0 :                 (GInt16)CastToGInt16((float)floor(-panLine[i] * sqrt_2 + 0.5));
     635             :         }
     636             :     }
     637             : 
     638           0 :     return CE_None;
     639             : }
     640             : 
     641             : /************************************************************************/
     642             : /* ==================================================================== */
     643             : /*                              SAR_CEOSDataset                         */
     644             : /* ==================================================================== */
     645             : /************************************************************************/
     646             : 
     647             : /************************************************************************/
     648             : /*                          SAR_CEOSDataset()                           */
     649             : /************************************************************************/
     650             : 
     651           7 : SAR_CEOSDataset::SAR_CEOSDataset()
     652             :     : fpImage(nullptr), papszTempMD(nullptr), nGCPCount(0), pasGCPList(nullptr),
     653           7 :       papszExtraFiles(nullptr)
     654             : {
     655           7 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     656           7 :     m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
     657             : 
     658           7 :     sVolume.Flavor = 0;
     659           7 :     sVolume.Sensor = 0;
     660           7 :     sVolume.ProductType = 0;
     661           7 :     sVolume.FileNamingConvention = 0;
     662             : 
     663           7 :     sVolume.VolumeDirectoryFile = 0;
     664           7 :     sVolume.SARLeaderFile = 0;
     665           7 :     sVolume.ImagryOptionsFile = 0;
     666           7 :     sVolume.SARTrailerFile = 0;
     667           7 :     sVolume.NullVolumeDirectoryFile = 0;
     668             : 
     669           7 :     sVolume.ImageDesc.ImageDescValid = 0;
     670           7 :     sVolume.ImageDesc.NumChannels = 0;
     671           7 :     sVolume.ImageDesc.ChannelInterleaving = 0;
     672           7 :     sVolume.ImageDesc.DataType = 0;
     673           7 :     sVolume.ImageDesc.BytesPerRecord = 0;
     674           7 :     sVolume.ImageDesc.Lines = 0;
     675           7 :     sVolume.ImageDesc.TopBorderPixels = 0;
     676           7 :     sVolume.ImageDesc.BottomBorderPixels = 0;
     677           7 :     sVolume.ImageDesc.PixelsPerLine = 0;
     678           7 :     sVolume.ImageDesc.LeftBorderPixels = 0;
     679           7 :     sVolume.ImageDesc.RightBorderPixels = 0;
     680           7 :     sVolume.ImageDesc.BytesPerPixel = 0;
     681           7 :     sVolume.ImageDesc.RecordsPerLine = 0;
     682           7 :     sVolume.ImageDesc.PixelsPerRecord = 0;
     683           7 :     sVolume.ImageDesc.ImageDataStart = 0;
     684           7 :     sVolume.ImageDesc.ImageSuffixData = 0;
     685           7 :     sVolume.ImageDesc.FileDescriptorLength = 0;
     686           7 :     sVolume.ImageDesc.PixelOrder = 0;
     687           7 :     sVolume.ImageDesc.LineOrder = 0;
     688           7 :     sVolume.ImageDesc.PixelDataBytesPerRecord = 0;
     689             : 
     690           7 :     sVolume.RecordList = nullptr;
     691           7 : }
     692             : 
     693             : /************************************************************************/
     694             : /*                          ~SAR_CEOSDataset()                          */
     695             : /************************************************************************/
     696             : 
     697          14 : SAR_CEOSDataset::~SAR_CEOSDataset()
     698             : 
     699             : {
     700           7 :     FlushCache(true);
     701             : 
     702           7 :     CSLDestroy(papszTempMD);
     703             : 
     704           7 :     if (fpImage != nullptr)
     705           7 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
     706             : 
     707           7 :     if (nGCPCount > 0)
     708             :     {
     709           1 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
     710             :     }
     711           7 :     CPLFree(pasGCPList);
     712             : 
     713           7 :     if (sVolume.RecordList)
     714             :     {
     715         109 :         for (Link_t *Links = sVolume.RecordList; Links != nullptr;
     716         102 :              Links = Links->next)
     717             :         {
     718         102 :             if (Links->object)
     719             :             {
     720         102 :                 DeleteCeosRecord((CeosRecord_t *)Links->object);
     721         102 :                 Links->object = nullptr;
     722             :             }
     723             :         }
     724           7 :         DestroyList(sVolume.RecordList);
     725             :     }
     726           7 :     FreeRecipes();
     727           7 :     CSLDestroy(papszExtraFiles);
     728          14 : }
     729             : 
     730             : /************************************************************************/
     731             : /*                            GetGCPCount()                             */
     732             : /************************************************************************/
     733             : 
     734           3 : int SAR_CEOSDataset::GetGCPCount()
     735             : 
     736             : {
     737           3 :     if (!m_bHasScannedForGCP)
     738           3 :         ScanForGCPs();
     739           3 :     return nGCPCount;
     740             : }
     741             : 
     742             : /************************************************************************/
     743             : /*                          GetGCPSpatialRef()                          */
     744             : /************************************************************************/
     745             : 
     746           0 : const OGRSpatialReference *SAR_CEOSDataset::GetGCPSpatialRef() const
     747             : 
     748             : {
     749           0 :     if (!m_bHasScannedForGCP)
     750           0 :         const_cast<SAR_CEOSDataset *>(this)->ScanForGCPs();
     751           0 :     if (nGCPCount > 0)
     752           0 :         return &m_oSRS;
     753             : 
     754           0 :     return nullptr;
     755             : }
     756             : 
     757             : /************************************************************************/
     758             : /*                               GetGCP()                               */
     759             : /************************************************************************/
     760             : 
     761           0 : const GDAL_GCP *SAR_CEOSDataset::GetGCPs()
     762             : 
     763             : {
     764           0 :     if (!m_bHasScannedForGCP)
     765           0 :         ScanForGCPs();
     766           0 :     return pasGCPList;
     767             : }
     768             : 
     769             : /************************************************************************/
     770             : /*                       GetMetadataDomainList()                        */
     771             : /************************************************************************/
     772             : 
     773           0 : char **SAR_CEOSDataset::GetMetadataDomainList()
     774             : {
     775           0 :     return CSLAddString(GDALDataset::GetMetadataDomainList(),
     776           0 :                         "ceos-FFF-n-n-n-n:r");
     777             : }
     778             : 
     779             : /************************************************************************/
     780             : /*                            GetMetadata()                             */
     781             : /*                                                                      */
     782             : /*      We provide our own GetMetadata() so that we can override        */
     783             : /*      behavior for some very specialized domain names intended to     */
     784             : /*      give us access to raw record data.                              */
     785             : /*                                                                      */
     786             : /*      The domain must look like:                                      */
     787             : /*        ceos-FFF-n-n-n-n:r                                            */
     788             : /*                                                                      */
     789             : /*        FFF - The file id - one of vol, lea, img, trl or nul.         */
     790             : /*        n-n-n-n - the record type code such as 18-10-18-20 for the    */
     791             : /*        dataset summary record in the leader file.                    */
     792             : /*        :r - The zero based record number to fetch (optional)         */
     793             : /*                                                                      */
     794             : /*      Note that only records that are pre-loaded will be              */
     795             : /*      accessible, and this normally means that most image records     */
     796             : /*      are not available.                                              */
     797             : /************************************************************************/
     798             : 
     799           3 : CSLConstList SAR_CEOSDataset::GetMetadata(const char *pszDomain)
     800             : 
     801             : {
     802           3 :     if (pszDomain == nullptr || !STARTS_WITH_CI(pszDomain, "ceos-"))
     803           3 :         return GDALDataset::GetMetadata(pszDomain);
     804             : 
     805             :     /* -------------------------------------------------------------------- */
     806             :     /*      Identify which file to fetch the file from.                     */
     807             :     /* -------------------------------------------------------------------- */
     808           0 :     int nFileId = -1;
     809             : 
     810           0 :     if (STARTS_WITH_CI(pszDomain, "ceos-vol"))
     811             :     {
     812           0 :         nFileId = CEOS_VOLUME_DIR_FILE;
     813             :     }
     814           0 :     else if (STARTS_WITH_CI(pszDomain, "ceos-lea"))
     815             :     {
     816           0 :         nFileId = CEOS_LEADER_FILE;
     817             :     }
     818           0 :     else if (STARTS_WITH_CI(pszDomain, "ceos-img"))
     819             :     {
     820           0 :         nFileId = CEOS_IMAGRY_OPT_FILE;
     821             :     }
     822           0 :     else if (STARTS_WITH_CI(pszDomain, "ceos-trl"))
     823             :     {
     824           0 :         nFileId = CEOS_TRAILER_FILE;
     825             :     }
     826           0 :     else if (STARTS_WITH_CI(pszDomain, "ceos-nul"))
     827             :     {
     828           0 :         nFileId = CEOS_NULL_VOL_FILE;
     829             :     }
     830             :     else
     831           0 :         return nullptr;
     832             : 
     833           0 :     pszDomain += 8;
     834             : 
     835             :     /* -------------------------------------------------------------------- */
     836             :     /*      Identify the record type.                                       */
     837             :     /* -------------------------------------------------------------------- */
     838           0 :     int a, b, c, d, nRecordIndex = -1;
     839             : 
     840           0 :     if (sscanf(pszDomain, "-%d-%d-%d-%d:%d", &a, &b, &c, &d, &nRecordIndex) !=
     841           0 :             5 &&
     842           0 :         sscanf(pszDomain, "-%d-%d-%d-%d", &a, &b, &c, &d) != 4)
     843             :     {
     844           0 :         return nullptr;
     845             :     }
     846             : 
     847           0 :     CeosTypeCode_t sTypeCode = QuadToTC(a, b, c, d);
     848             : 
     849             :     /* -------------------------------------------------------------------- */
     850             :     /*      Try to fetch the record.                                        */
     851             :     /* -------------------------------------------------------------------- */
     852           0 :     CeosRecord_t *record = FindCeosRecord(sVolume.RecordList, sTypeCode,
     853             :                                           nFileId, -1, nRecordIndex);
     854             : 
     855           0 :     if (record == nullptr)
     856           0 :         return nullptr;
     857             : 
     858             :     /* -------------------------------------------------------------------- */
     859             :     /*      Massage the data into a safe textual format.  The RawRecord     */
     860             :     /*      just has zero bytes turned into spaces while the                */
     861             :     /*      EscapedRecord has regular backslash escaping applied to zero    */
     862             :     /*      chars, double quotes, and backslashes.                          */
     863             :     /*      just turn zero bytes into spaces.                               */
     864             :     /* -------------------------------------------------------------------- */
     865             : 
     866           0 :     CSLDestroy(papszTempMD);
     867             : 
     868             :     // Escaped version
     869           0 :     char *pszSafeCopy = CPLEscapeString((char *)record->Buffer, record->Length,
     870             :                                         CPLES_BackslashQuotable);
     871           0 :     papszTempMD = CSLSetNameValue(nullptr, "EscapedRecord", pszSafeCopy);
     872           0 :     CPLFree(pszSafeCopy);
     873             : 
     874             :     // Copy with '\0' replaced by spaces.
     875             : 
     876           0 :     pszSafeCopy = (char *)CPLCalloc(1, record->Length + 1);
     877           0 :     memcpy(pszSafeCopy, record->Buffer, record->Length);
     878             : 
     879           0 :     for (int i = 0; i < record->Length; i++)
     880           0 :         if (pszSafeCopy[i] == '\0')
     881           0 :             pszSafeCopy[i] = ' ';
     882             : 
     883           0 :     papszTempMD = CSLSetNameValue(papszTempMD, "RawRecord", pszSafeCopy);
     884             : 
     885           0 :     CPLFree(pszSafeCopy);
     886             : 
     887           0 :     return papszTempMD;
     888             : }
     889             : 
     890             : /************************************************************************/
     891             : /*                          ScanForMetadata()                           */
     892             : /************************************************************************/
     893             : 
     894           7 : void SAR_CEOSDataset::ScanForMetadata()
     895             : 
     896             : {
     897             :     /* -------------------------------------------------------------------- */
     898             :     /*      Get the volume id (with the sensor name)                        */
     899             :     /* -------------------------------------------------------------------- */
     900             :     const CeosRecord_t *record =
     901           7 :         FindCeosRecord(sVolume.RecordList, VOLUME_DESCRIPTOR_RECORD_TC,
     902             :                        CEOS_VOLUME_DIR_FILE, -1, -1);
     903             : 
     904             :     struct FieldDef
     905             :     {
     906             :         const char *pszMetadataItemName;
     907             :         int nOffsetInRecord;
     908             :         const char *pszFormat;
     909             :     };
     910             : 
     911          14 :     CPLString osVolId;
     912             :     char szField[128];
     913           7 :     szField[0] = '\0';
     914           7 :     if (record != nullptr)
     915             :     {
     916           3 :         GetCeosField(record, 61, "A16", szField);
     917           3 :         osVolId = szField;
     918           3 :         osVolId.Trim();
     919           3 :         SetMetadataItem("CEOS_LOGICAL_VOLUME_ID", osVolId.c_str());
     920             : 
     921           3 :         constexpr FieldDef asFieldDefs[] = {
     922             :             {"CEOS_PROCESSING_FACILITY", 149, "A12"},
     923             :             {"CEOS_PROCESSING_AGENCY", 141, "A8"},
     924             :             {"CEOS_PROCESSING_COUNTRY", 129, "A12"},
     925             :             {"CEOS_SOFTWARE_ID", 33, "A12"},
     926             :             {"CEOS_PRODUCT_ID", 261, "A8"},
     927             :             {"CEOS_VOLSET_ID", 77, "A16"},
     928             :         };
     929          21 :         for (const auto &sDef : asFieldDefs)
     930             :         {
     931          18 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
     932          36 :             CPLString osField(szField);
     933          18 :             osField.Trim();
     934          18 :             if (!osField.empty())
     935          15 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
     936             :         }
     937             :     }
     938             : 
     939             :     /* ==================================================================== */
     940             :     /*      Dataset summary record.                                         */
     941             :     /* ==================================================================== */
     942           7 :     record = FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
     943             :                             CEOS_LEADER_FILE, -1, -1);
     944             : 
     945           7 :     if (record == nullptr)
     946             :         record =
     947           4 :             FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_ASF_TC,
     948             :                            CEOS_LEADER_FILE, -1, -1);
     949             : 
     950           7 :     if (record == nullptr)
     951           2 :         record = FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
     952             :                                 CEOS_TRAILER_FILE, -1, -1);
     953             : 
     954           7 :     if (record == nullptr)
     955             :         record =
     956           2 :             FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_ERS2_TC,
     957             :                            CEOS_LEADER_FILE, -1, -1);
     958             : 
     959           7 :     if (record != nullptr)
     960             :     {
     961           5 :         constexpr FieldDef asFieldDefs[] = {
     962             :             {"CEOS_ACQUISITION_TIME", 69, "A32"},
     963             :             {"CEOS_ASC_DES", 101, "A16"},  // Ascending/Descending (RSAT only)
     964             :             {"CEOS_TRUE_HEADING", 149, "A16"},  //  at least for ERS2
     965             :             {"CEOS_ELLIPSOID", 165, "A16"},
     966             :             {"CEOS_SEMI_MAJOR", 181, "A16"},
     967             :             {"CEOS_SEMI_MINOR", 197, "A16"},
     968             :             {"CEOS_SCENE_LENGTH_KM", 341, "A16"},
     969             :             {"CEOS_SCENE_WIDTH_KM", 357, "A16"},
     970             :             {"CEOS_MISSION_ID", 397, "A16"},
     971             :             {"CEOS_SENSOR_ID", 413, "A32"},
     972             :             {"CEOS_ORBIT_NUMBER", 445, "A8"},
     973             :             {"CEOS_PLATFORM_LATITUDE", 453, "A8"},
     974             :             {"CEOS_PLATFORM_LONGITUDE", 461, "A8"},
     975             :             {"CEOS_PLATFORM_HEADING", 469, "A8"},    //  at least for ERS2
     976             :             {"CEOS_SENSOR_CLOCK_ANGLE", 477, "A8"},  // Look Angle
     977             :             {"CEOS_INC_ANGLE", 485, "A8"},           // Incidence Angle
     978             :             {"CEOS_FACILITY", 1047, "A16"},
     979             :             {"CEOS_PIXEL_TIME_DIR", 1527,
     980             :              "A8"},  // Pixel time direction indicator
     981             :             {"CEOS_LINE_SPACING_METERS", 1687, "A16"},
     982             :             {"CEOS_PIXEL_SPACING_METERS", 1703, "A16"},
     983             :         };
     984         105 :         for (const auto &sDef : asFieldDefs)
     985             :         {
     986         100 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
     987         200 :             CPLString osField(szField);
     988         100 :             osField.Trim();
     989         256 :             if (!osField.empty() &&
     990          78 :                 (osVolId.find("RSAT") != std::string::npos ||
     991          78 :                  !EQUAL(sDef.pszMetadataItemName, "CEOS_ASC_DES")))
     992             :             {
     993          76 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
     994             :             }
     995             :         }
     996             :     }
     997             : 
     998             :     /* -------------------------------------------------------------------- */
     999             :     /*      Get the beam mode, for radarsat.                                */
    1000             :     /* -------------------------------------------------------------------- */
    1001             :     record =
    1002           7 :         FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_COMPENSATION_TC,
    1003             :                        CEOS_LEADER_FILE, -1, -1);
    1004             : 
    1005           7 :     if (osVolId.find("RSAT") != std::string::npos && record != nullptr)
    1006             :     {
    1007           0 :         GetCeosField(record, 4189, "A16", szField);
    1008           0 :         CPLString osField(szField);
    1009           0 :         osField.Trim();
    1010             : 
    1011           0 :         SetMetadataItem("CEOS_BEAM_TYPE", osField.c_str());
    1012             :     }
    1013             : 
    1014             :     /* ==================================================================== */
    1015             :     /*      ERS calibration and incidence angle info                        */
    1016             :     /* ==================================================================== */
    1017           7 :     record = FindCeosRecord(sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_TC,
    1018             :                             CEOS_LEADER_FILE, -1, -1);
    1019             : 
    1020           7 :     if (record == nullptr)
    1021             :         record =
    1022           7 :             FindCeosRecord(sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_ALT_TC,
    1023             :                            CEOS_LEADER_FILE, -1, -1);
    1024             : 
    1025           7 :     if (record != nullptr)
    1026             :     {
    1027           0 :         GetCeosField(record, 13, "A64", szField);
    1028           0 :         CPLString osField(szField);
    1029           0 :         osField.Trim();
    1030             : 
    1031             :         /* Avoid PCS records, which don't contain necessary info */
    1032           0 :         if (osField.find("GENERAL") == std::string::npos)
    1033           0 :             record = nullptr;
    1034             :     }
    1035             : 
    1036           7 :     if (record != nullptr)
    1037             :     {
    1038           0 :         constexpr FieldDef asFieldDefs[] = {
    1039             :             {"CEOS_INC_ANGLE_FIRST_RANGE", 583, "A16"},
    1040             :             {"CEOS_INC_ANGLE_CENTRE_RANGE", 599, "A16"},
    1041             :             {"CEOS_INC_ANGLE_LAST_RANGE", 615, "A16"},
    1042             :             {"CEOS_CALIBRATION_CONSTANT_K", 663, "A16"},
    1043             :             {"CEOS_GROUND_TO_SLANT_C0", 1855, "A20"},
    1044             :             {"CEOS_GROUND_TO_SLANT_C1", 1875, "A20"},
    1045             :             {"CEOS_GROUND_TO_SLANT_C2", 1895, "A20"},
    1046             :             {"CEOS_GROUND_TO_SLANT_C3", 1915, "A20"},
    1047             :         };
    1048           0 :         for (const auto &sDef : asFieldDefs)
    1049             :         {
    1050           0 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
    1051           0 :             CPLString osField(szField);
    1052           0 :             osField.Trim();
    1053           0 :             if (!osField.empty())
    1054             :             {
    1055           0 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
    1056             :             }
    1057             :         }
    1058             :     }
    1059             :     /* -------------------------------------------------------------------- */
    1060             :     /*      Detailed Processing Parameters (Radarsat)                       */
    1061             :     /* -------------------------------------------------------------------- */
    1062           7 :     record = FindCeosRecord(sVolume.RecordList, RSAT_PROC_PARAM_TC,
    1063             :                             CEOS_LEADER_FILE, -1, -1);
    1064             : 
    1065           7 :     if (record == nullptr)
    1066           7 :         record = FindCeosRecord(sVolume.RecordList, RSAT_PROC_PARAM_TC,
    1067             :                                 CEOS_TRAILER_FILE, -1, -1);
    1068             : 
    1069           7 :     if (record != nullptr)
    1070             :     {
    1071           0 :         constexpr FieldDef asFieldDefs[] = {
    1072             :             {"CEOS_PROC_START", 192, "A21"},
    1073             :             {"CEOS_PROC_STOP", 213, "A21"},
    1074             :             {"CEOS_EPH_ORB_DATA_0", 4649, "A16"},
    1075             :             {"CEOS_EPH_ORB_DATA_1", 4665, "A16"},
    1076             :             {"CEOS_EPH_ORB_DATA_2", 4681, "A16"},
    1077             :             {"CEOS_EPH_ORB_DATA_3", 4697, "A16"},
    1078             :             {"CEOS_EPH_ORB_DATA_4", 4713, "A16"},
    1079             :             {"CEOS_EPH_ORB_DATA_5", 4729, "A16"},
    1080             :             {"CEOS_EPH_ORB_DATA_6", 4745, "A16"},
    1081             :             {"CEOS_GROUND_TO_SLANT_C0", 4908, "A16"},
    1082             :             {"CEOS_GROUND_TO_SLANT_C1", 4924, "A16"},
    1083             :             {"CEOS_GROUND_TO_SLANT_C2", 4940, "A16"},
    1084             :             {"CEOS_GROUND_TO_SLANT_C3", 4956, "A16"},
    1085             :             {"CEOS_GROUND_TO_SLANT_C4", 4972, "A16"},
    1086             :             {"CEOS_GROUND_TO_SLANT_C5", 4988, "A16"},
    1087             :             {"CEOS_INC_ANGLE_FIRST_RANGE", 7334, "A16"},
    1088             :             {"CEOS_INC_ANGLE_LAST_RANGE", 7350, "A16"},
    1089             :         };
    1090           0 :         for (const auto &sDef : asFieldDefs)
    1091             :         {
    1092           0 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
    1093           0 :             CPLString osField(szField);
    1094           0 :             osField.Trim();
    1095           0 :             if (!osField.empty())
    1096             :             {
    1097           0 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
    1098             :             }
    1099             :         }
    1100             :     }
    1101             :     /* -------------------------------------------------------------------- */
    1102             :     /*      Get process-to-raw data coordinate translation values.  These   */
    1103             :     /*      are likely specific to Atlantis APP products.                   */
    1104             :     /* -------------------------------------------------------------------- */
    1105           7 :     record = FindCeosRecord(sVolume.RecordList, IMAGE_HEADER_RECORD_TC,
    1106             :                             CEOS_IMAGRY_OPT_FILE, -1, -1);
    1107             : 
    1108           7 :     if (record != nullptr)
    1109             :     {
    1110           4 :         constexpr FieldDef asFieldDefs[] = {
    1111             :             {"CEOS_DM_CORNER", 449, "A4"},
    1112             :             {"CEOS_DM_TRANSPOSE", 453, "A4"},
    1113             :             {"CEOS_DM_START_SAMPLE", 457, "A4"},
    1114             :             {"CEOS_DM_START_PULSE", 461, "A5"},
    1115             :             {"CEOS_DM_FAST_ALPHA", 466, "A16"},
    1116             :             {"CEOS_DM_FAST_BETA", 482, "A16"},
    1117             :             {"CEOS_DM_SLOW_ALPHA", 498, "A16"},
    1118             :             {"CEOS_DM_SLOW_BETA", 514, "A16"},
    1119             :             {"CEOS_DM_FAST_ALPHA_2", 530, "A16"},
    1120             :         };
    1121          40 :         for (const auto &sDef : asFieldDefs)
    1122             :         {
    1123          36 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
    1124          72 :             CPLString osField(szField);
    1125          36 :             osField.Trim();
    1126          36 :             if (!osField.empty())
    1127             :             {
    1128          18 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
    1129             :             }
    1130             :         }
    1131             :     }
    1132             : 
    1133             :     /* -------------------------------------------------------------------- */
    1134             :     /*      Try to find calibration information from Radiometric Data       */
    1135             :     /*      Record.                                                         */
    1136             :     /* -------------------------------------------------------------------- */
    1137             :     record =
    1138           7 :         FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_DATA_RECORD_TC,
    1139             :                        CEOS_LEADER_FILE, -1, -1);
    1140             : 
    1141           7 :     if (record == nullptr)
    1142           4 :         record = FindCeosRecord(sVolume.RecordList,
    1143             :                                 LEADER_RADIOMETRIC_DATA_RECORD_TC,
    1144             :                                 CEOS_TRAILER_FILE, -1, -1);
    1145             : 
    1146           7 :     if (record != nullptr)
    1147             :     {
    1148           3 :         GetCeosField(record, 8317, "A16", szField);
    1149           6 :         CPLString osField(szField);
    1150           3 :         osField.Trim();
    1151           3 :         if (!osField.empty())
    1152           0 :             SetMetadataItem("CEOS_CALIBRATION_OFFSET", osField.c_str());
    1153             :     }
    1154             : 
    1155             :     /* -------------------------------------------------------------------- */
    1156             :     /*      For ERS Standard Format Landsat scenes we pick up the           */
    1157             :     /*      calibration offset and gain from the Radiometric Ancillary      */
    1158             :     /*      Record.                                                         */
    1159             :     /* -------------------------------------------------------------------- */
    1160             :     record =
    1161           7 :         FindCeosRecord(sVolume.RecordList, QuadToTC(0x3f, 0x24, 0x12, 0x09),
    1162             :                        CEOS_LEADER_FILE, -1, -1);
    1163           7 :     if (record != nullptr)
    1164             :     {
    1165           0 :         constexpr FieldDef asFieldDefs[] = {
    1166             :             {"CEOS_OFFSET_A0", 29, "A20"},
    1167             :             {"CEOS_GAIN_A1", 49, "A20"},
    1168             :         };
    1169           0 :         for (const auto &sDef : asFieldDefs)
    1170             :         {
    1171           0 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
    1172           0 :             CPLString osField(szField);
    1173           0 :             osField.Trim();
    1174           0 :             if (!osField.empty())
    1175             :             {
    1176           0 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
    1177             :             }
    1178             :         }
    1179             :     }
    1180             : 
    1181             :     /* -------------------------------------------------------------------- */
    1182             :     /*      For ERS Standard Format Landsat scenes we pick up the           */
    1183             :     /*      gain setting from the Scene Header Record.                      */
    1184             :     /* -------------------------------------------------------------------- */
    1185             :     record =
    1186           7 :         FindCeosRecord(sVolume.RecordList, QuadToTC(0x12, 0x12, 0x12, 0x09),
    1187             :                        CEOS_LEADER_FILE, -1, -1);
    1188           7 :     if (record != nullptr)
    1189             :     {
    1190           0 :         GetCeosField(record, 1486, "A1", szField);
    1191           0 :         szField[1] = '\0';
    1192             : 
    1193           0 :         if (szField[0] == 'H' || szField[0] == 'V')
    1194           0 :             SetMetadataItem("CEOS_GAIN_SETTING", szField);
    1195             :     }
    1196             : 
    1197             :     /* -------------------------------------------------------------------- */
    1198             :     /*      PALSAR-2 ALOS2 Platform position data                           */
    1199             :     /* -------------------------------------------------------------------- */
    1200             :     record =
    1201           7 :         FindCeosRecord(sVolume.RecordList, LEADER_PLATFORM_POSITION_PALSAR_TC,
    1202             :                        CEOS_LEADER_FILE, -1, -1);
    1203           7 :     if (record && record->Length > 387)
    1204             :     {
    1205             :         // Table 3.3-7 Platform position data records
    1206             :         // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
    1207           3 :         constexpr FieldDef asFieldDefs[] = {
    1208             :             {"CEOS_PLATFORM_POS_ORBITAL_ELEMENTS_DESIGNATOR", 13, "A32"},
    1209             :             {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_1", 45, "A16"},
    1210             :             {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_2", 61, "A16"},
    1211             :             {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_3", 77, "A16"},
    1212             :             {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_4", 93, "A16"},
    1213             :             {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_5", 109, "A16"},
    1214             :             {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_6", 125, "A16"},
    1215             :             {"CEOS_PLATFORM_POS_NUMBER_POINTS", 141, "A4"},
    1216             :             {"CEOS_PLATFORM_POS_YEAR_POINT_1", 145, "A4"},
    1217             :             {"CEOS_PLATFORM_POS_MONTH_POINT_1", 149, "A4"},
    1218             :             {"CEOS_PLATFORM_POS_DAY_POINT_1", 153, "A4"},
    1219             :             {"CEOS_PLATFORM_POS_DAY_IN_YEAR_POINT_1", 157, "A4"},
    1220             :             {"CEOS_PLATFORM_POS_SECONDS_OF_DAY_POINT_1", 161, "A22"},
    1221             :             {"CEOS_PLATFORM_POS_TIME_INTERVAL_SECONDS", 183, "A22"},
    1222             :             {"CEOS_PLATFORM_POS_REF_COORD_SYS", 205, "A64"},
    1223             :             {"CEOS_PLATFORM_POS_GREENWICH_MEAN_HOUR_ANGLE_DEG", 269, "A22"},
    1224             :             {"CEOS_PLATFORM_POS_ALONG_TRACK_POS_ERROR_METERS", 291, "A16"},
    1225             :             {"CEOS_PLATFORM_POS_ACROSS_TRACK_POS_ERROR_METERS", 307, "A16"},
    1226             :             {"CEOS_PLATFORM_POS_RADIAL_POS_ERROR_METERS", 323, "A16"},
    1227             :             {"CEOS_PLATFORM_POS_ALONG_TRACK_VELOCITY_ERROR_METERS_PER_SEC", 339,
    1228             :              "A16"},
    1229             :             {"CEOS_PLATFORM_POS_ACROSS_TRACK_VELOCITY_ERROR_METERS_PER_SEC",
    1230             :              355, "A16"},
    1231             :             {"CEOS_PLATFORM_POS_RADIAL_VELOCITY_ERROR_METERS_PER_SEC", 371,
    1232             :              "A16"},
    1233             :         };
    1234           3 :         int nPoints = 0;
    1235           3 :         constexpr int OFFSET_POINT_1 = 387;
    1236           3 :         constexpr int POINT_SIZE = 6 * 22;
    1237          69 :         for (const auto &sDef : asFieldDefs)
    1238             :         {
    1239          66 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
    1240         132 :             CPLString osField(szField);
    1241          66 :             osField.Trim();
    1242          66 :             if (!osField.empty())
    1243             :             {
    1244          63 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
    1245          63 :                 if (EQUAL(sDef.pszMetadataItemName,
    1246             :                           "CEOS_PLATFORM_POS_NUMBER_POINTS"))
    1247             :                 {
    1248           9 :                     nPoints = std::clamp(atoi(osField), 0,
    1249           6 :                                          (record->Length - OFFSET_POINT_1) /
    1250           3 :                                              POINT_SIZE);
    1251             :                 }
    1252             :             }
    1253             :         }
    1254             : 
    1255           3 :         constexpr FieldDef asPointFieldDefs[] = {
    1256             :             {"VECTOR_X_METERS", 0, "A22"},
    1257             :             {"VECTOR_Y_METERS", 22, "A22"},
    1258             :             {"VECTOR_Z_METERS", 44, "A22"},
    1259             :             {"VECTOR_X_DERIV_METERS_PER_SEC", 66, "A22"},
    1260             :             {"VECTOR_Y_DERIV_METERS_PER_SEC", 88, "A22"},
    1261             :             {"VECTOR_Z_DERIV_METERS_PER_SEC", 110, "A22"},
    1262             :         };
    1263          87 :         for (int iPnt = 0; iPnt < nPoints; ++iPnt)
    1264             :         {
    1265         588 :             for (const auto &sDef : asPointFieldDefs)
    1266             :             {
    1267         504 :                 GetCeosField(record,
    1268         504 :                              sDef.nOffsetInRecord + OFFSET_POINT_1 +
    1269         504 :                                  iPnt * POINT_SIZE,
    1270         504 :                              sDef.pszFormat, szField);
    1271        1008 :                 CPLString osField(szField);
    1272         504 :                 osField.Trim();
    1273         504 :                 if (!osField.empty())
    1274             :                 {
    1275        1512 :                     SetMetadataItem(std::string("CEOS_PLATFORM_POS_")
    1276         504 :                                         .append(sDef.pszMetadataItemName)
    1277         504 :                                         .append("_POINT_")
    1278        1008 :                                         .append(std::to_string(iPnt + 1))
    1279             :                                         .c_str(),
    1280         504 :                                     osField.c_str());
    1281             :                 }
    1282             :             }
    1283             :         }
    1284             : 
    1285             :         {
    1286           3 :             GetCeosField(record, 4101, "A1", szField);
    1287           6 :             CPLString osField(szField);
    1288           3 :             osField.Trim();
    1289           3 :             if (!osField.empty())
    1290             :             {
    1291           3 :                 SetMetadataItem("CEOS_PLATFORM_POS_LEAP_SECOND",
    1292           3 :                                 osField.c_str());
    1293             :             }
    1294             :         }
    1295             :     }
    1296             : 
    1297             :     /* -------------------------------------------------------------------- */
    1298             :     /*      PALSAR-2 ALOS2 Attitude data                                    */
    1299             :     /* -------------------------------------------------------------------- */
    1300           7 :     record = FindCeosRecord(sVolume.RecordList, LEADER_ATTITUDE_PALSAR_TC,
    1301             :                             CEOS_LEADER_FILE, -1, -1);
    1302           7 :     if (record)
    1303             :     {
    1304             :         // Table 3.3-8 Attitude data records
    1305             :         // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
    1306             : 
    1307           3 :         int nPoints = 0;
    1308           3 :         constexpr int OFFSET_POINT_1 = 17;
    1309           3 :         constexpr int POINT_SIZE = 120;
    1310             :         {
    1311           3 :             GetCeosField(record, 13, "A4", szField);
    1312           6 :             CPLString osField(szField);
    1313           3 :             osField.Trim();
    1314           3 :             if (!osField.empty())
    1315             :             {
    1316           3 :                 SetMetadataItem("CEOS_PLATFORM_ATT_NUMBER_POINTS",
    1317           3 :                                 osField.c_str());
    1318           3 :                 nPoints =
    1319           9 :                     std::clamp(atoi(osField), 0,
    1320           3 :                                (record->Length - OFFSET_POINT_1) / POINT_SIZE);
    1321             :             }
    1322             :         }
    1323             : 
    1324           3 :         constexpr FieldDef asFieldDefs[] = {
    1325             :             {"DAY_OF_YEAR", 17, "A4"},
    1326             :             {"MILLISECOND_OF_DAY", 21, "A8"},
    1327             :             {"PITCH_QUALITY_FLAG", 29, "A4"},
    1328             :             {"ROLL_QUALITY_FLAG", 33, "A4"},
    1329             :             {"YAW_QUALITY_FLAG", 37, "A4"},
    1330             :             {"PITCH_DEG", 41, "A14"},
    1331             :             {"ROLL_DEG", 55, "A14"},
    1332             :             {"YAW_DEG", 69, "A14"},
    1333             :             {"PITCH_RATE_QUALITY_FLAG", 83, "A4"},
    1334             :             {"ROL_RATE_QUALITY_FLAG", 87, "A4"},
    1335             :             {"YAW_RATE_QUALITY_FLAG", 91, "A4"},
    1336             :             {"PITCH_RATE", 95, "A14"},
    1337             :             {"ROLL_RATE", 109, "A14"},
    1338             :             {"YAW_RATE", 123, "A14"},
    1339             :         };
    1340          59 :         for (int iPnt = 0; iPnt < nPoints; ++iPnt)
    1341             :         {
    1342         840 :             for (const auto &sDef : asFieldDefs)
    1343             :             {
    1344         784 :                 GetCeosField(record, sDef.nOffsetInRecord + iPnt * POINT_SIZE,
    1345         784 :                              sDef.pszFormat, szField);
    1346        1568 :                 CPLString osField(szField);
    1347         784 :                 osField.Trim();
    1348         784 :                 if (!osField.empty())
    1349             :                 {
    1350        2352 :                     SetMetadataItem(std::string("CEOS_PLATFORM_ATT_")
    1351         784 :                                         .append(sDef.pszMetadataItemName)
    1352         784 :                                         .append("_POINT_")
    1353        1568 :                                         .append(std::to_string(iPnt + 1))
    1354             :                                         .c_str(),
    1355         784 :                                     osField.c_str());
    1356             :                 }
    1357             :             }
    1358             :         }
    1359             :     }
    1360             : 
    1361             :     /* -------------------------------------------------------------------- */
    1362             :     /*      PALSAR-2 ALOS2 Radiometric data                                 */
    1363             :     /* -------------------------------------------------------------------- */
    1364           7 :     record = FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_PALSAR_TC,
    1365             :                             CEOS_LEADER_FILE, -1, -1);
    1366           7 :     if (record)
    1367             :     {
    1368             :         // Table 3.3-9 Radiometric data records
    1369             :         // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
    1370           3 :         constexpr FieldDef asFieldDefs[] = {
    1371             :             {"CEOS_RADIOMETRIC_CALIBRATION_FACTOR", 21, "A16"},
    1372             :             {"CEOS_RADIOMETRIC_DT_1_1_REAL", 37, "A16"},
    1373             :             {"CEOS_RADIOMETRIC_DT_1_1_IMAG", 53, "A16"},
    1374             :             {"CEOS_RADIOMETRIC_DT_1_2_REAL", 69, "A16"},
    1375             :             {"CEOS_RADIOMETRIC_DT_1_2_IMAG", 85, "A16"},
    1376             :             {"CEOS_RADIOMETRIC_DT_2_1_REAL", 101, "A16"},
    1377             :             {"CEOS_RADIOMETRIC_DT_2_1_IMAG", 117, "A16"},
    1378             :             {"CEOS_RADIOMETRIC_DT_2_2_REAL", 133, "A16"},
    1379             :             {"CEOS_RADIOMETRIC_DT_2_2_IMAG", 149, "A16"},
    1380             :             {"CEOS_RADIOMETRIC_DR_1_1_REAL", 165, "A16"},
    1381             :             {"CEOS_RADIOMETRIC_DR_1_1_IMAG", 181, "A16"},
    1382             :             {"CEOS_RADIOMETRIC_DR_1_2_REAL", 197, "A16"},
    1383             :             {"CEOS_RADIOMETRIC_DR_1_2_IMAG", 213, "A16"},
    1384             :             {"CEOS_RADIOMETRIC_DR_2_1_REAL", 229, "A16"},
    1385             :             {"CEOS_RADIOMETRIC_DR_2_1_IMAG", 245, "A16"},
    1386             :             {"CEOS_RADIOMETRIC_DR_2_2_REAL", 261, "A16"},
    1387             :             {"CEOS_RADIOMETRIC_DR_2_2_IMAG", 277, "A16"},
    1388             :         };
    1389          54 :         for (const auto &sDef : asFieldDefs)
    1390             :         {
    1391          51 :             GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
    1392         102 :             CPLString osField(szField);
    1393          51 :             osField.Trim();
    1394          51 :             if (!osField.empty())
    1395             :             {
    1396          51 :                 SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
    1397             :             }
    1398             :         }
    1399             :     }
    1400             : 
    1401             :     /* -------------------------------------------------------------------- */
    1402             :     /*      PALSAR-2 ALOS2 Facility related data                            */
    1403             :     /* -------------------------------------------------------------------- */
    1404           7 :     constexpr int FACILITY_RELATED_DATA_5_LAT_LONG_CONV_FACTORS = 5;
    1405           7 :     record = FindCeosRecord(
    1406             :         sVolume.RecordList, LEADER_FACILITY_RELATED_PALSAR_TC, CEOS_LEADER_FILE,
    1407             :         -1, FACILITY_RELATED_DATA_5_LAT_LONG_CONV_FACTORS - 1);
    1408           7 :     if (record && record->Length == 5000)
    1409             :     {
    1410             :         // Table 3-17 Facility related data records 5
    1411             :         // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
    1412             : 
    1413           4 :         std::string coeffs;
    1414          22 :         for (int i = 0; i < 10; ++i)
    1415             :         {
    1416          20 :             GetCeosField(record, 17 + i * 20, "A20", szField);
    1417          40 :             CPLString osField(szField);
    1418          20 :             osField.Trim();
    1419          20 :             if (!coeffs.empty())
    1420           9 :                 coeffs += ' ';
    1421          20 :             coeffs += osField;
    1422             :         }
    1423             :         // 10 coefficients to convert from the map projection (E, N) to pixel (P)
    1424           2 :         SetMetadataItem("CEOS_FACILITY_5_XY_PROJECTED_TO_PIXEL_COEFFICIENTS",
    1425           2 :                         coeffs.c_str());
    1426             : 
    1427           2 :         coeffs.clear();
    1428          22 :         for (int i = 0; i < 10; ++i)
    1429             :         {
    1430          20 :             GetCeosField(record, 17 + 10 * 20 + i * 20, "A20", szField);
    1431          40 :             CPLString osField(szField);
    1432          20 :             osField.Trim();
    1433          20 :             if (!coeffs.empty())
    1434           9 :                 coeffs += ' ';
    1435          20 :             coeffs += osField;
    1436             :         }
    1437             :         // 10 coefficients to convert from the map projection (E, N) to line (L)
    1438           2 :         SetMetadataItem("CEOS_FACILITY_5_XY_PROJECTED_TO_LINE_COEFFICIENTS",
    1439           2 :                         coeffs.c_str());
    1440             : 
    1441           2 :         coeffs.clear();
    1442          52 :         for (int i = 0; i < 25; ++i)
    1443             :         {
    1444          50 :             GetCeosField(record, 1025 + i * 20, "A20", szField);
    1445         100 :             CPLString osField(szField);
    1446          50 :             osField.Trim();
    1447          50 :             if (!coeffs.empty())
    1448          48 :                 coeffs += ' ';
    1449          50 :             coeffs += osField;
    1450             :         }
    1451             :         // 25 coefficients of the 8th polynomial expression to convert from pixel (P) and line (L) to latitude (φ)
    1452           2 :         SetMetadataItem("CEOS_FACILITY_5_PIXEL_LINE_TO_LAT_COEFFICIENTS",
    1453           2 :                         coeffs.c_str());
    1454             : 
    1455           2 :         coeffs.clear();
    1456          52 :         for (int i = 0; i < 25; ++i)
    1457             :         {
    1458          50 :             GetCeosField(record, 1025 + i * 20, "A20", szField);
    1459         100 :             CPLString osField(szField);
    1460          50 :             osField.Trim();
    1461          50 :             if (!coeffs.empty())
    1462          48 :                 coeffs += ' ';
    1463          50 :             coeffs += osField;
    1464             :         }
    1465             :         // 25 coefficients of the 8th polynomial expression to convert from pixel (P) and line (L) to longitude (λ)
    1466           2 :         SetMetadataItem("CEOS_FACILITY_5_PIXEL_LINE_TO_LON_COEFFICIENTS",
    1467           2 :                         coeffs.c_str());
    1468             : 
    1469             :         {
    1470           2 :             GetCeosField(record, 2025, "A20", szField);
    1471           4 :             CPLString osField(szField);
    1472           2 :             osField.Trim();
    1473           2 :             SetMetadataItem("CEOS_FACILITY_5_ORIGIN_PIXEL", osField.c_str());
    1474             :         }
    1475             : 
    1476             :         {
    1477           2 :             GetCeosField(record, 2045, "A20", szField);
    1478           4 :             CPLString osField(szField);
    1479           2 :             osField.Trim();
    1480           2 :             SetMetadataItem("CEOS_FACILITY_5_ORIGIN_LINE", osField.c_str());
    1481             :         }
    1482             : 
    1483           2 :         coeffs.clear();
    1484          52 :         for (int i = 0; i < 25; ++i)
    1485             :         {
    1486          50 :             GetCeosField(record, 2065 + i * 20, "A20", szField);
    1487         100 :             CPLString osField(szField);
    1488          50 :             osField.Trim();
    1489          50 :             if (!coeffs.empty())
    1490          48 :                 coeffs += ' ';
    1491          50 :             coeffs += osField;
    1492             :         }
    1493             :         // 25 coefficients of the 8th polynomial expression to convert from latitude (Φ) and longitude (Λ) to pixel (p)
    1494           2 :         SetMetadataItem("CEOS_FACILITY_5_LAT_LON_TO_PIXEL_COEFFICIENTS",
    1495           2 :                         coeffs.c_str());
    1496             : 
    1497           2 :         coeffs.clear();
    1498          52 :         for (int i = 0; i < 25; ++i)
    1499             :         {
    1500          50 :             GetCeosField(record, 2065 + 25 * 20 + i * 20, "A20", szField);
    1501         100 :             CPLString osField(szField);
    1502          50 :             osField.Trim();
    1503          50 :             if (!coeffs.empty())
    1504          48 :                 coeffs += ' ';
    1505          50 :             coeffs += osField;
    1506             :         }
    1507             :         // 25 coefficients of the 8th polynomial expression to convert from latitude (Φ) and longitude (Λ) to pixel (p)
    1508           2 :         SetMetadataItem("CEOS_FACILITY_5_LAT_LON_TO_LINE_COEFFICIENTS",
    1509           2 :                         coeffs.c_str());
    1510             : 
    1511             :         {
    1512           2 :             GetCeosField(record, 3065, "A20", szField);
    1513           4 :             CPLString osField(szField);
    1514           2 :             osField.Trim();
    1515           2 :             SetMetadataItem("CEOS_FACILITY_5_ORIGIN_LAT", osField.c_str());
    1516             :         }
    1517             : 
    1518             :         {
    1519           2 :             GetCeosField(record, 3085, "A20", szField);
    1520           4 :             CPLString osField(szField);
    1521           2 :             osField.Trim();
    1522           2 :             SetMetadataItem("CEOS_FACILITY_5_ORIGIN_LON", osField.c_str());
    1523             :         }
    1524             :     }
    1525             : 
    1526             :     /* -------------------------------------------------------------------- */
    1527             :     /*      PALSAR Level 1.5/Level 3.1 processed record metadata            */
    1528             :     /* -------------------------------------------------------------------- */
    1529           7 :     record = FindCeosRecord(sVolume.RecordList,
    1530             :                             IMAGE_PROCESSED_DATA_RECORD_PALSAR_TC,
    1531             :                             CEOS_IMAGRY_OPT_FILE, -1, 2);
    1532           7 :     constexpr int NEEDED_SIZE_IN_PROCESSED_DATA_RECORD_PALSAR = 128;
    1533           7 :     if (record && record->Length >= NEEDED_SIZE_IN_PROCESSED_DATA_RECORD_PALSAR)
    1534             :     {
    1535             :         const auto ReadProcessedDataRecord =
    1536           6 :             [this](const CeosRecord_t *curRecord,
    1537          54 :                    const char *pszMDNameSuffix = "")
    1538             :         {
    1539           6 :             int32_t nInt32 = 0;
    1540             : 
    1541           6 :             GetCeosField(curRecord, 65, "B4", &nInt32);
    1542           6 :             SetMetadataItem(CPLSPrintf("CEOS_SLANT_RANGE_FIRST_PIXEL%s_METERS",
    1543             :                                        pszMDNameSuffix),
    1544           6 :                             CPLSPrintf("%d", nInt32));
    1545             : 
    1546           6 :             GetCeosField(curRecord, 69, "B4", &nInt32);
    1547           6 :             SetMetadataItem(CPLSPrintf("CEOS_SLANT_RANGE_MID_PIXEL%s_METERS",
    1548             :                                        pszMDNameSuffix),
    1549           6 :                             CPLSPrintf("%d", nInt32));
    1550             : 
    1551           6 :             GetCeosField(curRecord, 73, "B4", &nInt32);
    1552           6 :             SetMetadataItem(CPLSPrintf("CEOS_SLANT_RANGE_LAST_PIXEL%s_METERS",
    1553             :                                        pszMDNameSuffix),
    1554           6 :                             CPLSPrintf("%d", nInt32));
    1555             : 
    1556           6 :             GetCeosField(curRecord, 77, "B4", &nInt32);
    1557           6 :             SetMetadataItem(CPLSPrintf("CEOS_DOPPLER_CENTROID_FIRST_PIXEL%s_HZ",
    1558             :                                        pszMDNameSuffix),
    1559           6 :                             CPLSPrintf("%.3f", nInt32 / 1000.0));
    1560             : 
    1561           6 :             GetCeosField(curRecord, 81, "B4", &nInt32);
    1562           6 :             SetMetadataItem(CPLSPrintf("CEOS_DOPPLER_CENTROID_MID_PIXEL%s_HZ",
    1563             :                                        pszMDNameSuffix),
    1564           6 :                             CPLSPrintf("%.3f", nInt32 / 1000.0));
    1565             : 
    1566           6 :             GetCeosField(curRecord, 85, "B4", &nInt32);
    1567           6 :             SetMetadataItem(CPLSPrintf("CEOS_DOPPLER_CENTROID_LAST_PIXEL%s_HZ",
    1568             :                                        pszMDNameSuffix),
    1569           6 :                             CPLSPrintf("%.3f", nInt32 / 1000.0));
    1570             : 
    1571           6 :             GetCeosField(curRecord, 89, "B4", &nInt32);
    1572           6 :             SetMetadataItem(
    1573             :                 CPLSPrintf("CEOS_AZIMUTH_FM_RATE_FIRST_PIXEL%s_HZ_PER_MS",
    1574             :                            pszMDNameSuffix),
    1575           6 :                 CPLSPrintf("%d", nInt32));
    1576             : 
    1577           6 :             GetCeosField(curRecord, 93, "B4", &nInt32);
    1578           6 :             SetMetadataItem(
    1579             :                 CPLSPrintf("CEOS_AZIMUTH_FM_RATE_MID_PIXEL%s_HZ_PER_MS",
    1580             :                            pszMDNameSuffix),
    1581           6 :                 CPLSPrintf("%d", nInt32));
    1582             : 
    1583           6 :             GetCeosField(curRecord, 97, "B4", &nInt32);
    1584           6 :             SetMetadataItem(
    1585             :                 CPLSPrintf("CEOS_AZIMUTH_FM_RATE_LAST_PIXEL%s_HZ_PER_MS",
    1586             :                            pszMDNameSuffix),
    1587           6 :                 CPLSPrintf("%d", nInt32));
    1588           6 :         };
    1589             : 
    1590           5 :         ReadProcessedDataRecord(record);
    1591             : 
    1592             :         // The above values are per-record. In practice they seem to be constant
    1593             :         // among all records, but I could not find any statement on that, so
    1594             :         // also read and report them from the last record.
    1595             :         // We cannot use FindCeosRecord() to fetch it because ProcessData() limits
    1596             :         // to 4 records for the image file (for memory and performance reasons)
    1597           5 :         struct CeosSARImageDesc *ImageDesc = &(sVolume.ImageDesc);
    1598           5 :         const vsi_l_offset nOffsetToLastRecordStart =
    1599           5 :             ImageDesc->FileDescriptorLength +
    1600           5 :             static_cast<vsi_l_offset>(ImageDesc->BytesPerRecord) *
    1601           5 :                 (nRasterYSize - 1);
    1602             :         CeosRecord_t lastRecord;
    1603           5 :         memset(&lastRecord, 0, sizeof(lastRecord));
    1604             :         std::vector<GByte> abyLeader(
    1605          10 :             NEEDED_SIZE_IN_PROCESSED_DATA_RECORD_PALSAR);
    1606          10 :         if (fpImage->Seek(nOffsetToLastRecordStart, SEEK_SET) == 0 &&
    1607           5 :             fpImage->Read(abyLeader.data(), abyLeader.size()) ==
    1608           5 :                 abyLeader.size())
    1609             :         {
    1610           1 :             lastRecord.Buffer = abyLeader.data();
    1611           1 :             lastRecord.Length = static_cast<int>(abyLeader.size());
    1612           1 :             ReadProcessedDataRecord(&lastRecord, "_LAST_LINE");
    1613             :         }
    1614             :     }
    1615           7 : }
    1616             : 
    1617             : /************************************************************************/
    1618             : /*                        ScanForMapProjection()                        */
    1619             : /*                                                                      */
    1620             : /*      Try to find a map projection record, and read corner points     */
    1621             : /*      from it.  This has only been tested with ERS products.          */
    1622             : /************************************************************************/
    1623             : 
    1624           0 : int SAR_CEOSDataset::ScanForMapProjection()
    1625             : 
    1626             : {
    1627             :     /* -------------------------------------------------------------------- */
    1628             :     /*      Find record, and try to determine if it has useful GCPs.        */
    1629             :     /* -------------------------------------------------------------------- */
    1630             : 
    1631             :     CeosRecord_t *record =
    1632           0 :         FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_TC,
    1633             :                        CEOS_LEADER_FILE, -1, -1);
    1634             : 
    1635           0 :     int gcp_ordering_mode = CEOS_STD_MAPREC_GCP_ORDER;
    1636             :     /* JERS from Japan */
    1637           0 :     if (record == nullptr)
    1638             :         record =
    1639           0 :             FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_JERS_TC,
    1640             :                            CEOS_LEADER_FILE, -1, -1);
    1641             : 
    1642           0 :     if (record == nullptr)
    1643             :     {
    1644             :         record =
    1645           0 :             FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_ASF_TC,
    1646             :                            CEOS_LEADER_FILE, -1, -1);
    1647           0 :         gcp_ordering_mode = CEOS_ASF_MAPREC_GCP_ORDER;
    1648             :     }
    1649           0 :     if (record == nullptr)
    1650             :     {
    1651           0 :         record = FindCeosRecord(sVolume.RecordList, LEADER_FACILITY_ASF_TC,
    1652             :                                 CEOS_LEADER_FILE, -1, -1);
    1653           0 :         gcp_ordering_mode = CEOS_ASF_FACREC_GCP_ORDER;
    1654             :     }
    1655             : 
    1656           0 :     if (record == nullptr)
    1657           0 :         return FALSE;
    1658             : 
    1659             :     char szField[100];
    1660           0 :     memset(szField, 0, 17);
    1661           0 :     GetCeosField(record, 29, "A16", szField);
    1662             : 
    1663           0 :     int GCPFieldSize = 16;
    1664           0 :     int GCPOffset = 1073;
    1665             : 
    1666           0 :     if (!STARTS_WITH_CI(szField, "Slant Range") &&
    1667           0 :         !STARTS_WITH_CI(szField, "Ground Range") &&
    1668           0 :         !STARTS_WITH_CI(szField, "GEOCODED"))
    1669             :     {
    1670             :         /* detect ASF map projection record */
    1671           0 :         GetCeosField(record, 1079, "A7", szField);
    1672           0 :         if (!STARTS_WITH_CI(szField, "Slant") &&
    1673           0 :             !STARTS_WITH_CI(szField, "Ground"))
    1674             :         {
    1675           0 :             return FALSE;
    1676             :         }
    1677             :         else
    1678             :         {
    1679           0 :             GCPFieldSize = 17;
    1680           0 :             GCPOffset = 157;
    1681             :         }
    1682             :     }
    1683             : 
    1684             :     char FieldSize[4];
    1685           0 :     snprintf(FieldSize, sizeof(FieldSize), "A%d", GCPFieldSize);
    1686             : 
    1687           0 :     GetCeosField(record, GCPOffset, FieldSize, szField);
    1688           0 :     if (STARTS_WITH_CI(szField, "        "))
    1689           0 :         return FALSE;
    1690             : 
    1691             :     /* -------------------------------------------------------------------- */
    1692             :     /*      Read corner points.                                             */
    1693             :     /* -------------------------------------------------------------------- */
    1694           0 :     nGCPCount = 4;
    1695           0 :     pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), nGCPCount);
    1696             : 
    1697           0 :     GDALInitGCPs(nGCPCount, pasGCPList);
    1698             : 
    1699           0 :     for (int i = 0; i < nGCPCount; i++)
    1700             :     {
    1701             :         char szId[32];
    1702             : 
    1703           0 :         snprintf(szId, sizeof(szId), "%d", i + 1);
    1704           0 :         CPLFree(pasGCPList[i].pszId);
    1705           0 :         pasGCPList[i].pszId = CPLStrdup(szId);
    1706             : 
    1707           0 :         GetCeosField(record, GCPOffset + (GCPFieldSize * 2) * i, FieldSize,
    1708             :                      szField);
    1709           0 :         pasGCPList[i].dfGCPY = CPLAtof(szField);
    1710           0 :         GetCeosField(record, GCPOffset + GCPFieldSize + (GCPFieldSize * 2) * i,
    1711             :                      FieldSize, szField);
    1712           0 :         pasGCPList[i].dfGCPX = CPLAtof(szField);
    1713           0 :         pasGCPList[i].dfGCPZ = 0.0;
    1714             :     }
    1715             : 
    1716             :     /* Map Projection Record has the order UL UR LR LL
    1717             :      ASF Facility Data Record has the order UL,LL,UR,LR
    1718             :      ASF Map Projection Record has the order LL, LR, UR, UL */
    1719             : 
    1720           0 :     pasGCPList[0].dfGCPLine = 0.5;
    1721           0 :     pasGCPList[0].dfGCPPixel = 0.5;
    1722             : 
    1723           0 :     switch (gcp_ordering_mode)
    1724             :     {
    1725           0 :         case CEOS_ASF_FACREC_GCP_ORDER:
    1726           0 :             pasGCPList[1].dfGCPLine = nRasterYSize - 0.5;
    1727           0 :             pasGCPList[1].dfGCPPixel = 0.5;
    1728             : 
    1729           0 :             pasGCPList[2].dfGCPLine = 0.5;
    1730           0 :             pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
    1731             : 
    1732           0 :             pasGCPList[3].dfGCPLine = nRasterYSize - 0.5;
    1733           0 :             pasGCPList[3].dfGCPPixel = nRasterXSize - 0.5;
    1734           0 :             break;
    1735           0 :         case CEOS_STD_MAPREC_GCP_ORDER:
    1736           0 :             pasGCPList[1].dfGCPLine = 0.5;
    1737           0 :             pasGCPList[1].dfGCPPixel = nRasterXSize - 0.5;
    1738             : 
    1739           0 :             pasGCPList[2].dfGCPLine = nRasterYSize - 0.5;
    1740           0 :             pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
    1741             : 
    1742           0 :             pasGCPList[3].dfGCPLine = nRasterYSize - 0.5;
    1743           0 :             pasGCPList[3].dfGCPPixel = 0.5;
    1744           0 :             break;
    1745           0 :         case CEOS_ASF_MAPREC_GCP_ORDER:
    1746           0 :             pasGCPList[0].dfGCPLine = nRasterYSize - 0.5;
    1747           0 :             pasGCPList[0].dfGCPPixel = 0.5;
    1748             : 
    1749           0 :             pasGCPList[1].dfGCPLine = nRasterYSize - 0.5;
    1750           0 :             pasGCPList[1].dfGCPPixel = nRasterXSize - 0.5;
    1751             : 
    1752           0 :             pasGCPList[2].dfGCPLine = 0.5;
    1753           0 :             pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
    1754             : 
    1755           0 :             pasGCPList[3].dfGCPLine = 0.5;
    1756           0 :             pasGCPList[3].dfGCPPixel = 0.5;
    1757           0 :             break;
    1758             :     }
    1759             : 
    1760           0 :     return TRUE;
    1761             : }
    1762             : 
    1763             : /************************************************************************/
    1764             : /*                            ScanForGCPs()                             */
    1765             : /************************************************************************/
    1766             : 
    1767           3 : void SAR_CEOSDataset::ScanForGCPs()
    1768             : 
    1769             : {
    1770           3 :     m_bHasScannedForGCP = true;
    1771             : 
    1772             :     /* -------------------------------------------------------------------- */
    1773             :     /*      Do we have a standard 180 bytes of prefix data (192 bytes       */
    1774             :     /*      including the record marker information)?  If not, it is        */
    1775             :     /*      unlikely that the GCPs are available.                           */
    1776             :     /* -------------------------------------------------------------------- */
    1777           3 :     if (sVolume.ImageDesc.ImageDataStart < 192)
    1778             :     {
    1779           0 :         ScanForMapProjection();
    1780           0 :         return;
    1781             :     }
    1782             : 
    1783             :     /* ASF L1 products do not have valid data
    1784             :        in the lat/long first/mid/last fields */
    1785           3 :     const char *pszValue = GetMetadataItem("CEOS_FACILITY");
    1786           3 :     if ((pszValue != nullptr) && (strncmp(pszValue, "ASF", 3) == 0))
    1787             :     {
    1788           0 :         ScanForMapProjection();
    1789           0 :         return;
    1790             :     }
    1791             : 
    1792           3 :     if (GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
    1793             :     {
    1794           2 :         const char *pszVolSetId = GetMetadataItem("CEOS_VOLSET_ID");
    1795           2 :         if (pszVolSetId && (STARTS_WITH(pszVolSetId, "ALOS2  SAR") ||
    1796           1 :                             STARTS_WITH(pszVolSetId, "ALOS4  SAR")))
    1797             :         {
    1798             :             // No GCPs in those products. Helps for performance when
    1799             :             // reading in zip archives (particularly on network)
    1800           2 :             return;
    1801             :         }
    1802             :     }
    1803             : 
    1804             :     /* -------------------------------------------------------------------- */
    1805             :     /*      Just sample fix scanlines through the image for GCPs, to        */
    1806             :     /*      return 15 GCPs.  That is an adequate coverage for most          */
    1807             :     /*      purposes.  A GCP is collected from the beginning, middle and    */
    1808             :     /*      end of each scanline.                                           */
    1809             :     /* -------------------------------------------------------------------- */
    1810           1 :     nGCPCount = 0;
    1811           1 :     int nGCPMax = 15;
    1812           1 :     pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), nGCPMax);
    1813             : 
    1814           1 :     int nStep = (GetRasterYSize() - 1) / (nGCPMax / 3 - 1);
    1815           6 :     for (int iScanline = 0; iScanline < GetRasterYSize(); iScanline += nStep)
    1816             :     {
    1817           5 :         if (nGCPCount > nGCPMax - 3)
    1818           0 :             break;
    1819             : 
    1820             :         uint64_t nFileOffset;
    1821           5 :         CalcCeosSARImageFilePosition(&sVolume, 1, iScanline + 1, nullptr,
    1822             :                                      &nFileOffset);
    1823             : 
    1824             :         GInt32 anRecord[192 / 4];
    1825          10 :         if (VSIFSeekL(fpImage, nFileOffset, SEEK_SET) != 0 ||
    1826           5 :             VSIFReadL(anRecord, 1, 192, fpImage) != 192)
    1827           0 :             break;
    1828             : 
    1829             :         /* loop over first, middle and last pixel gcps */
    1830             : 
    1831          20 :         for (int iGCP = 0; iGCP < 3; iGCP++)
    1832             :         {
    1833          15 :             const int nLat = CPL_MSBWORD32(anRecord[132 / 4 + iGCP]);
    1834          15 :             const int nLong = CPL_MSBWORD32(anRecord[144 / 4 + iGCP]);
    1835             : 
    1836          15 :             if (nLat != 0 || nLong != 0)
    1837             :             {
    1838          15 :                 GDALInitGCPs(1, pasGCPList + nGCPCount);
    1839             : 
    1840          15 :                 CPLFree(pasGCPList[nGCPCount].pszId);
    1841             : 
    1842             :                 char szId[32];
    1843          15 :                 snprintf(szId, sizeof(szId), "%d", nGCPCount + 1);
    1844          15 :                 pasGCPList[nGCPCount].pszId = CPLStrdup(szId);
    1845             : 
    1846          15 :                 pasGCPList[nGCPCount].dfGCPX = nLong / 1000000.0;
    1847          15 :                 pasGCPList[nGCPCount].dfGCPY = nLat / 1000000.0;
    1848          15 :                 pasGCPList[nGCPCount].dfGCPZ = 0.0;
    1849             : 
    1850          15 :                 pasGCPList[nGCPCount].dfGCPLine = iScanline + 0.5;
    1851             : 
    1852          15 :                 if (iGCP == 0)
    1853           5 :                     pasGCPList[nGCPCount].dfGCPPixel = 0.5;
    1854          10 :                 else if (iGCP == 1)
    1855           5 :                     pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() / 2.0;
    1856             :                 else
    1857           5 :                     pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() - 0.5;
    1858             : 
    1859          15 :                 nGCPCount++;
    1860             :             }
    1861             :         }
    1862             :     }
    1863             :     /* If general GCP's were not found, look for Map Projection (e.g. JERS) */
    1864           1 :     if (nGCPCount == 0)
    1865             :     {
    1866           0 :         CPLFree(pasGCPList);
    1867           0 :         pasGCPList = nullptr;
    1868           0 :         ScanForMapProjection();
    1869           0 :         return;
    1870             :     }
    1871             : }
    1872             : 
    1873             : /************************************************************************/
    1874             : /*                                Open()                                */
    1875             : /************************************************************************/
    1876             : 
    1877       41362 : GDALDataset *SAR_CEOSDataset::Open(GDALOpenInfo *poOpenInfo)
    1878             : 
    1879             : {
    1880             :     /* -------------------------------------------------------------------- */
    1881             :     /*      Does this appear to be a valid ceos leader record?              */
    1882             :     /* -------------------------------------------------------------------- */
    1883       41362 :     if (poOpenInfo->nHeaderBytes < CEOS_HEADER_LENGTH ||
    1884        8503 :         poOpenInfo->fpL == nullptr)
    1885       32879 :         return nullptr;
    1886             : 
    1887        8483 :     if ((poOpenInfo->pabyHeader[4] != 0x3f &&
    1888        8475 :          poOpenInfo->pabyHeader[4] != 0x32) ||
    1889          13 :         poOpenInfo->pabyHeader[5] != 0xc0 ||
    1890          10 :         poOpenInfo->pabyHeader[6] != 0x12 || poOpenInfo->pabyHeader[7] != 0x12)
    1891        8473 :         return nullptr;
    1892             : 
    1893             :     // some products (#1862) have byte swapped record length/number
    1894             :     // values and will blow stuff up -- explicitly ignore if record index
    1895             :     // value appears to be little endian.
    1896          10 :     if (poOpenInfo->pabyHeader[0] != 0)
    1897           3 :         return nullptr;
    1898             : 
    1899             :     /* -------------------------------------------------------------------- */
    1900             :     /*      Confirm the requested access is supported.                      */
    1901             :     /* -------------------------------------------------------------------- */
    1902           7 :     if (poOpenInfo->eAccess == GA_Update)
    1903             :     {
    1904           0 :         ReportUpdateNotSupportedByDriver("SAR_CEOS");
    1905           0 :         return nullptr;
    1906             :     }
    1907             : 
    1908             :     /* -------------------------------------------------------------------- */
    1909             :     /*      Create a corresponding GDALDataset.                             */
    1910             :     /* -------------------------------------------------------------------- */
    1911             : 
    1912          14 :     auto poDS = std::make_unique<SAR_CEOSDataset>();
    1913           7 :     std::swap(poDS->fpImage, poOpenInfo->fpL);
    1914             : 
    1915           7 :     CeosSARVolume_t *psVolume = &(poDS->sVolume);
    1916           7 :     InitCeosSARVolume(psVolume, 0);
    1917             : 
    1918             :     /* -------------------------------------------------------------------- */
    1919             :     /*      Try to read the current file as an imagery file.                */
    1920             :     /* -------------------------------------------------------------------- */
    1921             : 
    1922           7 :     psVolume->ImagryOptionsFile = TRUE;
    1923           7 :     if (ProcessData(poDS->fpImage, CEOS_IMAGRY_OPT_FILE, psVolume,
    1924           7 :                     /* max_records = */ 4, VSI_L_OFFSET_MAX, false) != CE_None)
    1925             :     {
    1926           0 :         return nullptr;
    1927             :     }
    1928             : 
    1929             :     /* -------------------------------------------------------------------- */
    1930             :     /*      Try the various filenames.                                      */
    1931             :     /* -------------------------------------------------------------------- */
    1932           7 :     char *pszPath = CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
    1933             :     char *pszBasename =
    1934           7 :         CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str());
    1935             :     char *pszExtension =
    1936           7 :         CPLStrdup(CPLGetExtensionSafe(poOpenInfo->pszFilename).c_str());
    1937             : 
    1938             :     int nBand;
    1939           7 :     if (strlen(pszBasename) > 4)
    1940           7 :         nBand = atoi(pszBasename + 4);
    1941             :     else
    1942           0 :         nBand = 0;
    1943             : 
    1944           7 :     const bool bIsALOS2Or4 =
    1945          14 :         strlen(pszBasename) >= strlen("IMG-HH-ALOS2") &&
    1946          10 :         EQUALN(pszBasename, "IMG-", strlen("IMG-")) &&
    1947           3 :         (EQUALN(pszBasename + strlen("IMG-HH"), "-ALOS2", strlen("-ALOS2")) ||
    1948           1 :          EQUALN(pszBasename + strlen("IMG-HH"), "-ALOS4", strlen("-ALOS4")));
    1949           7 :     if (bIsALOS2Or4 && strlen(pszExtension) >= 3 &&
    1950           3 :         pszExtension[strlen(pszExtension) - 3] == '-' &&
    1951           0 :         pszExtension[strlen(pszExtension) - 2] == 'F')
    1952             :     {
    1953           0 :         pszExtension[strlen(pszExtension) - 3] = 0;
    1954             :     }
    1955             : 
    1956          42 :     for (int iFile = 0; iFile < CEOS_FILE_COUNT; iFile++)
    1957             :     {
    1958             :         /* skip image file ... we already did it */
    1959          35 :         if (iFile == CEOS_IMAGRY_OPT_FILE)
    1960           7 :             continue;
    1961             : 
    1962         409 :         for (int e = 0; CeosExtension[e][iFile] != nullptr; ++e)
    1963             :         {
    1964         390 :             std::string osFilename;
    1965             : 
    1966         390 :             const char *const pszMethod = CeosExtension[e][CEOS_FILE_COUNT];
    1967         390 :             const char *const pszFilePart = CeosExtension[e][iFile];
    1968             : 
    1969             :             /* build filename */
    1970         390 :             if (EQUAL(pszMethod, "base"))
    1971             :             {
    1972             :                 char szMadeBasename[32];
    1973             : 
    1974          84 :                 snprintf(szMadeBasename, sizeof(szMadeBasename), pszFilePart,
    1975             :                          nBand);
    1976             :                 osFilename =
    1977          84 :                     CPLFormFilenameSafe(pszPath, szMadeBasename, pszExtension);
    1978             :             }
    1979         306 :             else if (EQUAL(pszMethod, "ext"))
    1980             :             {
    1981             :                 osFilename =
    1982         224 :                     CPLFormFilenameSafe(pszPath, pszBasename, pszFilePart);
    1983             :             }
    1984          82 :             else if (EQUAL(pszMethod, "whole"))
    1985             :             {
    1986          28 :                 osFilename = CPLFormFilenameSafe(pszPath, pszFilePart, "");
    1987             :             }
    1988             : 
    1989             :             // This is for SAR SLC as per the SAR Toolbox (from ASF).
    1990          54 :             else if (EQUAL(pszMethod, "ext2"))
    1991             :             {
    1992             :                 char szThisExtension[32];
    1993             : 
    1994          28 :                 if (strlen(pszExtension) > 3)
    1995          12 :                     snprintf(szThisExtension, sizeof(szThisExtension), "%s%s",
    1996             :                              pszFilePart, pszExtension + 3);
    1997             :                 else
    1998          16 :                     snprintf(szThisExtension, sizeof(szThisExtension), "%s",
    1999             :                              pszFilePart);
    2000             : 
    2001             :                 osFilename =
    2002          28 :                     CPLFormFilenameSafe(pszPath, pszBasename, szThisExtension);
    2003             :             }
    2004             : 
    2005          26 :             else if (EQUAL(pszMethod, "ALOS2-ALOS4"))
    2006             :             {
    2007          26 :                 if (bIsALOS2Or4 && CeosExtension[e][iFile][0] != 0)
    2008             :                 {
    2009          18 :                     osFilename = CPLFormFilenameSafe(
    2010             :                         pszPath,
    2011          18 :                         std::string(pszFilePart)
    2012           9 :                             .append(pszBasename + strlen("IMG-HH"))
    2013             :                             .c_str(),
    2014           9 :                         pszExtension);
    2015             :                 }
    2016             :                 else
    2017             :                 {
    2018          17 :                     continue;
    2019             :                 }
    2020             :             }
    2021             : 
    2022             :             else
    2023             :             {
    2024           0 :                 CPLError(CE_Fatal, CPLE_AppDefined, "should not happen");
    2025             :             }
    2026             : 
    2027             :             /* try to open */
    2028         373 :             VSILFILE *process_fp = VSIFOpenL(osFilename.c_str(), "rb");
    2029             : 
    2030             :             /* try upper case */
    2031         373 :             if (process_fp == nullptr)
    2032             :             {
    2033        8112 :                 for (int i = static_cast<int>(osFilename.size()) - 1;
    2034        8112 :                      i >= 0 && osFilename[i] != '/' && osFilename[i] != '\\';
    2035             :                      i--)
    2036             :                 {
    2037        7750 :                     if (osFilename[i] >= 'a' && osFilename[i] <= 'z')
    2038        1942 :                         osFilename[i] = osFilename[i] - 'a' + 'A';
    2039             :                 }
    2040             : 
    2041         362 :                 process_fp = VSIFOpenL(osFilename.c_str(), "rb");
    2042             :             }
    2043             : 
    2044         373 :             if (process_fp != nullptr)
    2045             :             {
    2046          11 :                 CPLDebug("CEOS", "Opened %s.\n", osFilename.c_str());
    2047             : 
    2048          22 :                 poDS->papszExtraFiles =
    2049          11 :                     CSLAddString(poDS->papszExtraFiles, osFilename.c_str());
    2050             : 
    2051          11 :                 CPL_IGNORE_RET_VAL(VSIFSeekL(process_fp, 0, SEEK_END));
    2052          11 :                 const bool bSilentWrongRecordNumber =
    2053          11 :                     bIsALOS2Or4 && iFile == CEOS_TRAILER_FILE;
    2054          11 :                 if (ProcessData(process_fp, iFile, psVolume, -1,
    2055             :                                 VSIFTellL(process_fp),
    2056          11 :                                 bSilentWrongRecordNumber) == CE_None)
    2057             :                 {
    2058           9 :                     switch (iFile)
    2059             :                     {
    2060           3 :                         case CEOS_VOLUME_DIR_FILE:
    2061           3 :                             psVolume->VolumeDirectoryFile = TRUE;
    2062           3 :                             break;
    2063           5 :                         case CEOS_LEADER_FILE:
    2064           5 :                             psVolume->SARLeaderFile = TRUE;
    2065           5 :                             break;
    2066           1 :                         case CEOS_TRAILER_FILE:
    2067           1 :                             psVolume->SARTrailerFile = TRUE;
    2068           1 :                             break;
    2069           0 :                         case CEOS_NULL_VOL_FILE:
    2070           0 :                             psVolume->NullVolumeDirectoryFile = TRUE;
    2071           0 :                             break;
    2072             :                     }
    2073             : 
    2074           9 :                     CPL_IGNORE_RET_VAL(VSIFCloseL(process_fp));
    2075           9 :                     break; /* Exit the while loop, we have this data type*/
    2076             :                 }
    2077             : 
    2078           2 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(process_fp));
    2079             :             }
    2080             :         }
    2081             :     }
    2082             : 
    2083           7 :     CPLFree(pszPath);
    2084           7 :     CPLFree(pszBasename);
    2085           7 :     CPLFree(pszExtension);
    2086             : 
    2087             :     /* -------------------------------------------------------------------- */
    2088             :     /*      Check that we have an image description.                        */
    2089             :     /* -------------------------------------------------------------------- */
    2090           7 :     GetCeosSARImageDesc(psVolume);
    2091           7 :     struct CeosSARImageDesc *psImageDesc = &(psVolume->ImageDesc);
    2092           7 :     if (!psImageDesc->ImageDescValid)
    2093             :     {
    2094           0 :         CPLDebug("CEOS",
    2095             :                  "Unable to extract CEOS image description\n"
    2096             :                  "from %s.",
    2097             :                  poOpenInfo->pszFilename);
    2098             : 
    2099           0 :         return nullptr;
    2100             :     }
    2101             : 
    2102             :     /* -------------------------------------------------------------------- */
    2103             :     /*      Establish image type.                                           */
    2104             :     /* -------------------------------------------------------------------- */
    2105             :     GDALDataType eType;
    2106             : 
    2107           7 :     switch (psImageDesc->DataType)
    2108             :     {
    2109           2 :         case CEOS_TYP_CHAR:
    2110             :         case CEOS_TYP_UCHAR:
    2111           2 :             eType = GDT_UInt8;
    2112           2 :             break;
    2113             : 
    2114           0 :         case CEOS_TYP_SHORT:
    2115           0 :             eType = GDT_Int16;
    2116           0 :             break;
    2117             : 
    2118           0 :         case CEOS_TYP_COMPLEX_SHORT:
    2119             :         case CEOS_TYP_PALSAR_COMPLEX_SHORT:
    2120           0 :             eType = GDT_CInt16;
    2121           0 :             break;
    2122             : 
    2123           3 :         case CEOS_TYP_USHORT:
    2124           3 :             eType = GDT_UInt16;
    2125           3 :             break;
    2126             : 
    2127           0 :         case CEOS_TYP_LONG:
    2128           0 :             eType = GDT_Int32;
    2129           0 :             break;
    2130             : 
    2131           0 :         case CEOS_TYP_ULONG:
    2132           0 :             eType = GDT_UInt32;
    2133           0 :             break;
    2134             : 
    2135           0 :         case CEOS_TYP_FLOAT:
    2136           0 :             eType = GDT_Float32;
    2137           0 :             break;
    2138             : 
    2139           0 :         case CEOS_TYP_DOUBLE:
    2140           0 :             eType = GDT_Float64;
    2141           0 :             break;
    2142             : 
    2143           2 :         case CEOS_TYP_COMPLEX_FLOAT:
    2144             :         case CEOS_TYP_CCP_COMPLEX_FLOAT:
    2145           2 :             eType = GDT_CFloat32;
    2146           2 :             break;
    2147             : 
    2148           0 :         default:
    2149           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2150             :                      "Unsupported CEOS image data type %d.\n",
    2151             :                      psImageDesc->DataType);
    2152           0 :             return nullptr;
    2153             :     }
    2154             : 
    2155             :     /* -------------------------------------------------------------------- */
    2156             :     /*      Capture some information from the file that is of interest.     */
    2157             :     /* -------------------------------------------------------------------- */
    2158          14 :     poDS->nRasterXSize = psImageDesc->PixelsPerLine +
    2159           7 :                          psImageDesc->LeftBorderPixels +
    2160           7 :                          psImageDesc->RightBorderPixels;
    2161           7 :     poDS->nRasterYSize = psImageDesc->Lines;
    2162             : 
    2163             :     /* -------------------------------------------------------------------- */
    2164             :     /*      Special case for compressed cross products.                     */
    2165             :     /* -------------------------------------------------------------------- */
    2166           7 :     if (psImageDesc->DataType == CEOS_TYP_CCP_COMPLEX_FLOAT)
    2167             :     {
    2168           0 :         for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
    2169             :         {
    2170           0 :             poDS->SetBand(
    2171           0 :                 poDS->nBands + 1,
    2172           0 :                 new CCPRasterBand(poDS.get(), poDS->nBands + 1, eType));
    2173             :         }
    2174             : 
    2175             :         /* mark this as a Scattering Matrix product */
    2176           0 :         if (poDS->GetRasterCount() == 4)
    2177             :         {
    2178           0 :             poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
    2179             :         }
    2180             :     }
    2181             : 
    2182             :     /* -------------------------------------------------------------------- */
    2183             :     /*      Special case for PALSAR data.                                   */
    2184             :     /* -------------------------------------------------------------------- */
    2185           7 :     else if (psImageDesc->DataType == CEOS_TYP_PALSAR_COMPLEX_SHORT)
    2186             :     {
    2187           0 :         for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
    2188             :         {
    2189           0 :             poDS->SetBand(poDS->nBands + 1,
    2190           0 :                           new PALSARRasterBand(poDS.get(), poDS->nBands + 1));
    2191             :         }
    2192             : 
    2193             :         /* mark this as a Symmetrized Covariance product if appropriate */
    2194           0 :         if (poDS->GetRasterCount() == 6)
    2195             :         {
    2196           0 :             poDS->SetMetadataItem("MATRIX_REPRESENTATION",
    2197           0 :                                   "SYMMETRIZED_COVARIANCE");
    2198             :         }
    2199             :     }
    2200             : 
    2201             :     /* -------------------------------------------------------------------- */
    2202             :     /*      Roll our own ...                                                */
    2203             :     /* -------------------------------------------------------------------- */
    2204           7 :     else if (psImageDesc->RecordsPerLine > 1 ||
    2205           7 :              psImageDesc->DataType == CEOS_TYP_CHAR ||
    2206           7 :              psImageDesc->DataType == CEOS_TYP_LONG ||
    2207           7 :              psImageDesc->DataType == CEOS_TYP_ULONG ||
    2208           7 :              psImageDesc->DataType == CEOS_TYP_DOUBLE)
    2209             :     {
    2210           0 :         for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
    2211             :         {
    2212           0 :             poDS->SetBand(
    2213           0 :                 poDS->nBands + 1,
    2214           0 :                 new SAR_CEOSRasterBand(poDS.get(), poDS->nBands + 1, eType));
    2215           0 :         }
    2216             :     }
    2217             : 
    2218             :     /* -------------------------------------------------------------------- */
    2219             :     /*      Use raw services for well behaved files.                        */
    2220             :     /* -------------------------------------------------------------------- */
    2221             :     else
    2222             :     {
    2223             :         uint64_t StartData;
    2224           7 :         CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr, &StartData);
    2225             : 
    2226             :         /*StartData += psImageDesc->ImageDataStart; */
    2227             : 
    2228             :         uint64_t nLineOff1, nLineOff2;
    2229           7 :         CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr, &nLineOff1);
    2230           7 :         CalcCeosSARImageFilePosition(psVolume, 1, 2, nullptr, &nLineOff2);
    2231             : 
    2232           7 :         const int nLineSize = static_cast<int>(nLineOff2 - nLineOff1);
    2233             : 
    2234          14 :         for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
    2235             :         {
    2236             :             uint64_t nStartData;
    2237             :             int nPixelOffset, nLineOffset;
    2238             : 
    2239           7 :             if (psImageDesc->ChannelInterleaving == CEOS_IL_PIXEL)
    2240             :             {
    2241           0 :                 CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr,
    2242             :                                              &nStartData);
    2243             : 
    2244           0 :                 nStartData += psImageDesc->ImageDataStart;
    2245           0 :                 nStartData +=
    2246           0 :                     cpl::fits_on<int>(psImageDesc->BytesPerPixel * iBand);
    2247             : 
    2248           0 :                 nPixelOffset =
    2249           0 :                     psImageDesc->BytesPerPixel * psImageDesc->NumChannels;
    2250           0 :                 nLineOffset = nLineSize;
    2251             :             }
    2252           7 :             else if (psImageDesc->ChannelInterleaving == CEOS_IL_LINE)
    2253             :             {
    2254           0 :                 CalcCeosSARImageFilePosition(psVolume, iBand + 1, 1, nullptr,
    2255             :                                              &nStartData);
    2256             : 
    2257           0 :                 nStartData += psImageDesc->ImageDataStart;
    2258           0 :                 nPixelOffset = psImageDesc->BytesPerPixel;
    2259           0 :                 nLineOffset = nLineSize * psImageDesc->NumChannels;
    2260             :             }
    2261           7 :             else if (psImageDesc->ChannelInterleaving == CEOS_IL_BAND)
    2262             :             {
    2263           7 :                 CalcCeosSARImageFilePosition(psVolume, iBand + 1, 1, nullptr,
    2264             :                                              &nStartData);
    2265             : 
    2266           7 :                 nStartData += psImageDesc->ImageDataStart;
    2267           7 :                 nPixelOffset = psImageDesc->BytesPerPixel;
    2268           7 :                 nLineOffset = nLineSize;
    2269             :             }
    2270             :             else
    2271             :             {
    2272           0 :                 CPLAssert(false);
    2273           0 :                 return nullptr;
    2274             :             }
    2275             : 
    2276             :             auto poBand = RawRasterBand::Create(
    2277          21 :                 poDS.get(), poDS->nBands + 1, poDS->fpImage, nStartData,
    2278             :                 nPixelOffset, nLineOffset, eType,
    2279             :                 RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    2280           7 :                 RawRasterBand::OwnFP::NO);
    2281           7 :             if (!poBand)
    2282           0 :                 return nullptr;
    2283           7 :             poDS->SetBand(poDS->nBands + 1, std::move(poBand));
    2284             :         }
    2285             :     }
    2286             : 
    2287             :     /* -------------------------------------------------------------------- */
    2288             :     /*      Collect metadata.                                               */
    2289             :     /* -------------------------------------------------------------------- */
    2290           7 :     poDS->ScanForMetadata();
    2291             : 
    2292             :     /* -------------------------------------------------------------------- */
    2293             :     /*      Initialize any PAM information.                                 */
    2294             :     /* -------------------------------------------------------------------- */
    2295           7 :     poDS->SetDescription(poOpenInfo->pszFilename);
    2296           7 :     poDS->TryLoadXML();
    2297             : 
    2298             :     /* -------------------------------------------------------------------- */
    2299             :     /*      Open overviews.                                                 */
    2300             :     /* -------------------------------------------------------------------- */
    2301           7 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
    2302             : 
    2303           7 :     return poDS.release();
    2304             : }
    2305             : 
    2306             : /************************************************************************/
    2307             : /*                            ProcessData()                             */
    2308             : /************************************************************************/
    2309          18 : static int ProcessData(VSILFILE *fp, int fileid, CeosSARVolume_t *sar,
    2310             :                        int max_records, vsi_l_offset max_bytes,
    2311             :                        bool bSilentWrongRecordNumber)
    2312             : 
    2313             : {
    2314             :     unsigned char temp_buffer[CEOS_HEADER_LENGTH];
    2315          18 :     unsigned char *temp_body = nullptr;
    2316          18 :     vsi_l_offset start = 0;
    2317          18 :     int CurrentBodyLength = 0;
    2318          18 :     int CurrentType = 0;
    2319          18 :     int CurrentSequence = 0;
    2320          18 :     int iThisRecord = 0;
    2321             : 
    2322         120 :     while (max_records != 0 && max_bytes != 0)
    2323             :     {
    2324         104 :         iThisRecord++;
    2325             : 
    2326         208 :         if (VSIFSeekL(fp, start, SEEK_SET) != 0 ||
    2327         104 :             VSIFReadL(temp_buffer, 1, CEOS_HEADER_LENGTH, fp) !=
    2328             :                 CEOS_HEADER_LENGTH)
    2329             :         {
    2330           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2331             :                      "Corrupt CEOS File - cannot read record %d.", iThisRecord);
    2332           0 :             CPLFree(temp_body);
    2333           0 :             return CE_Failure;
    2334             :         }
    2335         104 :         CeosRecord_t *record = (CeosRecord_t *)CPLMalloc(sizeof(CeosRecord_t));
    2336         104 :         record->Length = DetermineCeosRecordBodyLength(temp_buffer);
    2337             : 
    2338         104 :         CeosToNative(&(record->Sequence), temp_buffer, 4, 4);
    2339             : 
    2340         104 :         if (iThisRecord != record->Sequence)
    2341             :         {
    2342           2 :             if (fileid == CEOS_IMAGRY_OPT_FILE && iThisRecord == 2)
    2343             :             {
    2344           0 :                 CPLDebug("SAR_CEOS",
    2345             :                          "Ignoring CEOS file with wrong second record sequence "
    2346             :                          "number - likely it has padded records.");
    2347           0 :                 CPLFree(record);
    2348           0 :                 CPLFree(temp_body);
    2349           0 :                 return CE_Warning;
    2350             :             }
    2351           2 :             else if (bSilentWrongRecordNumber && iThisRecord == 2)
    2352             :             {
    2353           2 :                 CPLFree(record);
    2354           2 :                 CPLFree(temp_body);
    2355           2 :                 return CE_Warning;
    2356             :             }
    2357             :             else
    2358             :             {
    2359           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2360             :                          "Corrupted CEOS File - got record seq# %d instead of "
    2361             :                          "the expected %d.",
    2362             :                          record->Sequence, iThisRecord);
    2363           0 :                 CPLFree(record);
    2364           0 :                 CPLFree(temp_body);
    2365           0 :                 return CE_Failure;
    2366             :             }
    2367             :         }
    2368             : 
    2369         102 :         if (record->Length <= CEOS_HEADER_LENGTH)
    2370             :         {
    2371           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2372             :                      "Corrupt CEOS File - cannot read record %d.", iThisRecord);
    2373           0 :             CPLFree(record);
    2374           0 :             CPLFree(temp_body);
    2375           0 :             return CE_Failure;
    2376             :         }
    2377             : 
    2378         102 :         if (record->Length > CurrentBodyLength)
    2379             :         {
    2380             :             unsigned char *temp_body_new =
    2381          47 :                 (unsigned char *)VSI_REALLOC_VERBOSE(temp_body, record->Length);
    2382          47 :             if (temp_body_new == nullptr)
    2383             :             {
    2384           0 :                 CPLFree(record);
    2385           0 :                 CPLFree(temp_body);
    2386           0 :                 return CE_Failure;
    2387             :             }
    2388          47 :             temp_body = temp_body_new;
    2389          47 :             CurrentBodyLength = record->Length;
    2390             :         }
    2391             : 
    2392         102 :         int nToRead = record->Length - CEOS_HEADER_LENGTH;
    2393         102 :         if ((int)VSIFReadL(temp_body, 1, nToRead, fp) != nToRead)
    2394             :         {
    2395           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2396             :                      "Corrupt CEOS File - cannot read record %d.", iThisRecord);
    2397           0 :             CPLFree(record);
    2398           0 :             CPLFree(temp_body);
    2399           0 :             return CE_Failure;
    2400             :         }
    2401             : 
    2402         102 :         InitCeosRecordWithHeader(record, temp_buffer, temp_body);
    2403         102 :         if (record->Length == 0)
    2404             :         {
    2405           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2406             :                      "Corrupt CEOS File - invalid record %d.", iThisRecord);
    2407           0 :             CPLFree(record);
    2408           0 :             CPLFree(temp_body);
    2409           0 :             return CE_Failure;
    2410             :         }
    2411             : 
    2412         102 :         if (CurrentType == record->TypeCode.Int32Code)
    2413          36 :             record->Subsequence = ++CurrentSequence;
    2414             :         else
    2415             :         {
    2416          66 :             CurrentType = record->TypeCode.Int32Code;
    2417          66 :             record->Subsequence = 0;
    2418          66 :             CurrentSequence = 0;
    2419             :         }
    2420             : 
    2421         102 :         record->FileId = fileid;
    2422             : 
    2423         102 :         Link_t *TheLink = ceos2CreateLink(record);
    2424             : 
    2425         102 :         if (sar->RecordList == nullptr)
    2426           7 :             sar->RecordList = TheLink;
    2427             :         else
    2428          95 :             sar->RecordList = InsertLink(sar->RecordList, TheLink);
    2429             : 
    2430         102 :         start += record->Length;
    2431             : 
    2432         102 :         if (max_records > 0)
    2433          28 :             max_records--;
    2434         102 :         if (max_bytes > 0)
    2435             :         {
    2436         102 :             if ((vsi_l_offset)record->Length <= max_bytes)
    2437         102 :                 max_bytes -= record->Length;
    2438             :             else
    2439             :             {
    2440           0 :                 CPLDebug("SAR_CEOS",
    2441             :                          "Partial record found.  %d > " CPL_FRMT_GUIB,
    2442             :                          record->Length, max_bytes);
    2443           0 :                 max_bytes = 0;
    2444             :             }
    2445             :         }
    2446             :     }
    2447             : 
    2448          16 :     CPLFree(temp_body);
    2449             : 
    2450          16 :     return CE_None;
    2451             : }
    2452             : 
    2453             : /************************************************************************/
    2454             : /*                       GDALRegister_SAR_CEOS()                        */
    2455             : /************************************************************************/
    2456             : 
    2457        2066 : void GDALRegister_SAR_CEOS()
    2458             : 
    2459             : {
    2460        2066 :     if (GDALGetDriverByName("SAR_CEOS") != nullptr)
    2461         263 :         return;
    2462             : 
    2463        1803 :     GDALDriver *poDriver = new GDALDriver();
    2464             : 
    2465        1803 :     poDriver->SetDescription("SAR_CEOS");
    2466        1803 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    2467        1803 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "CEOS SAR Image");
    2468        1803 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
    2469        1803 :                               "drivers/raster/sar_ceos.html");
    2470        1803 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    2471             : 
    2472        1803 :     poDriver->pfnOpen = SAR_CEOSDataset::Open;
    2473             : 
    2474        1803 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2475             : }
    2476             : 
    2477             : /************************************************************************/
    2478             : /*                            GetFileList()                             */
    2479             : /************************************************************************/
    2480             : 
    2481           2 : char **SAR_CEOSDataset::GetFileList()
    2482             : 
    2483             : {
    2484           2 :     char **papszFileList = GDALPamDataset::GetFileList();
    2485             : 
    2486           2 :     papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
    2487             : 
    2488           2 :     return papszFileList;
    2489             : }

Generated by: LCOV version 1.14