LCOV - code coverage report
Current view: top level - frmts/ceos2 - sar_ceosdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 467 965 48.4 %
Date: 2026-01-23 20:24:11 Functions: 11 23 47.8 %

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

Generated by: LCOV version 1.14