LCOV - code coverage report
Current view: top level - frmts/ceos2 - sar_ceosdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 474 964 49.2 %
Date: 2024-05-06 18:28:20 Functions: 12 23 52.2 %

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

Generated by: LCOV version 1.14