2359 строки
84 KiB
C++
2359 строки
84 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: ASI CEOS Translator
|
|
* Purpose: GDALDataset driver for CEOS translator.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2000, Atlantis Scientific Inc.
|
|
* Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "ceos.h"
|
|
#include "cpl_string.h"
|
|
#include "gdal_frmts.h"
|
|
#include "gdal_priv.h"
|
|
#include "rawdataset.h"
|
|
#include "ogr_srs_api.h"
|
|
|
|
#include <algorithm>
|
|
|
|
static GInt16 CastToGInt16(float val)
|
|
{
|
|
if (val < -32768.0)
|
|
val = -32768.0;
|
|
|
|
if (val > 32767)
|
|
val = 32767.0;
|
|
|
|
return static_cast<GInt16>(val);
|
|
}
|
|
|
|
static const char *const CeosExtension[][6] = {
|
|
{"vol", "led", "img", "trl", "nul", "ext"},
|
|
{"vol", "lea", "img", "trl", "nul", "ext"},
|
|
{"vol", "led", "img", "tra", "nul", "ext"},
|
|
{"vol", "lea", "img", "tra", "nul", "ext"},
|
|
{"vdf", "slf", "sdf", "stf", "nvd", "ext"},
|
|
|
|
{"vdf", "ldr", "img", "tra", "nul", "ext2"},
|
|
|
|
/* Jers from Japan- not sure if this is generalized as much as it could be
|
|
*/
|
|
{"VOLD", "Sarl_01", "Imop_%02d", "Sart_01", "NULL", "base"},
|
|
|
|
/* Radarsat: basename, not extension */
|
|
{"vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_vdf", "base"},
|
|
|
|
/* Ers-1: basename, not extension */
|
|
{"vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_dat", "base"},
|
|
|
|
/* Ers-2 from Telaviv */
|
|
{"volume", "leader", "image", "trailer", "nul_dat", "whole"},
|
|
|
|
/* Ers-1 from D-PAF */
|
|
{"VDF", "LF", "SLC", "", "", "ext"},
|
|
|
|
/* Radarsat-1 per #2051 */
|
|
{"vol", "sarl", "sard", "sart", "nvol", "ext"},
|
|
|
|
/* Radarsat-1 ASF */
|
|
{"", "L", "D", "", "", "ext"},
|
|
|
|
/* end marker */
|
|
{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
|
|
|
|
static int ProcessData(VSILFILE *fp, int fileid, CeosSARVolume_t *sar,
|
|
int max_records, vsi_l_offset max_bytes);
|
|
|
|
static CeosTypeCode_t QuadToTC(int a, int b, int c, int d)
|
|
{
|
|
CeosTypeCode_t abcd;
|
|
|
|
abcd.UCharCode.Subtype1 = (unsigned char)a;
|
|
abcd.UCharCode.Type = (unsigned char)b;
|
|
abcd.UCharCode.Subtype2 = (unsigned char)c;
|
|
abcd.UCharCode.Subtype3 = (unsigned char)d;
|
|
|
|
return abcd;
|
|
}
|
|
|
|
#define LEADER_DATASET_SUMMARY_TC QuadToTC(18, 10, 18, 20)
|
|
#define LEADER_DATASET_SUMMARY_ERS2_TC QuadToTC(10, 10, 31, 20)
|
|
#define LEADER_RADIOMETRIC_COMPENSATION_TC QuadToTC(18, 51, 18, 20)
|
|
#define VOLUME_DESCRIPTOR_RECORD_TC QuadToTC(192, 192, 18, 18)
|
|
#define IMAGE_HEADER_RECORD_TC QuadToTC(63, 192, 18, 18)
|
|
#define LEADER_RADIOMETRIC_DATA_RECORD_TC QuadToTC(18, 50, 18, 20)
|
|
#define LEADER_MAP_PROJ_RECORD_TC QuadToTC(10, 20, 31, 20)
|
|
|
|
// TODO: recond?
|
|
/* JERS from Japan has MAP_PROJ recond with different identifiers */
|
|
/* see CEOS-SAR-CCT Iss/Rev: 2/0 February 10, 1989 */
|
|
#define LEADER_MAP_PROJ_RECORD_JERS_TC QuadToTC(18, 20, 18, 20)
|
|
|
|
/* Leader from ASF has different identifiers */
|
|
#define LEADER_MAP_PROJ_RECORD_ASF_TC QuadToTC(10, 20, 18, 20)
|
|
#define LEADER_DATASET_SUMMARY_ASF_TC QuadToTC(10, 10, 18, 20)
|
|
#define LEADER_FACILITY_ASF_TC QuadToTC(90, 210, 18, 61)
|
|
|
|
/* For ERS calibration and incidence angle information */
|
|
#define ERS_GENERAL_FACILITY_DATA_TC QuadToTC(10, 200, 31, 50)
|
|
#define ERS_GENERAL_FACILITY_DATA_ALT_TC QuadToTC(10, 216, 31, 50)
|
|
|
|
#define RSAT_PROC_PARAM_TC QuadToTC(18, 120, 18, 20)
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* SAR_CEOSDataset */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class SAR_CEOSRasterBand;
|
|
class CCPRasterBand;
|
|
class PALSARRasterBand;
|
|
|
|
class SAR_CEOSDataset final : public GDALPamDataset
|
|
{
|
|
friend class SAR_CEOSRasterBand;
|
|
friend class CCPRasterBand;
|
|
friend class PALSARRasterBand;
|
|
|
|
CeosSARVolume_t sVolume;
|
|
|
|
VSILFILE *fpImage;
|
|
|
|
char **papszTempMD;
|
|
|
|
OGRSpatialReference m_oSRS{};
|
|
int nGCPCount;
|
|
GDAL_GCP *pasGCPList;
|
|
|
|
void ScanForGCPs();
|
|
void ScanForMetadata();
|
|
int ScanForMapProjection();
|
|
char **papszExtraFiles;
|
|
|
|
public:
|
|
SAR_CEOSDataset();
|
|
~SAR_CEOSDataset() override;
|
|
|
|
int GetGCPCount() override;
|
|
const OGRSpatialReference *GetGCPSpatialRef() const override;
|
|
const GDAL_GCP *GetGCPs() override;
|
|
|
|
char **GetMetadataDomainList() override;
|
|
char **GetMetadata(const char *pszDomain) override;
|
|
|
|
static GDALDataset *Open(GDALOpenInfo *);
|
|
virtual char **GetFileList(void) override;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* CCPRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class CCPRasterBand final : public GDALPamRasterBand
|
|
{
|
|
friend class SAR_CEOSDataset;
|
|
|
|
public:
|
|
CCPRasterBand(SAR_CEOSDataset *, int, GDALDataType);
|
|
|
|
CPLErr IReadBlock(int, int, void *) override;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* PALSARRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class PALSARRasterBand final : public GDALPamRasterBand
|
|
{
|
|
friend class SAR_CEOSDataset;
|
|
|
|
public:
|
|
PALSARRasterBand(SAR_CEOSDataset *, int);
|
|
|
|
CPLErr IReadBlock(int, int, void *) override;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* SAR_CEOSRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
class SAR_CEOSRasterBand final : public GDALPamRasterBand
|
|
{
|
|
friend class SAR_CEOSDataset;
|
|
|
|
public:
|
|
SAR_CEOSRasterBand(SAR_CEOSDataset *, int, GDALDataType);
|
|
|
|
CPLErr IReadBlock(int, int, void *) override;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* SAR_CEOSRasterBand() */
|
|
/************************************************************************/
|
|
|
|
SAR_CEOSRasterBand::SAR_CEOSRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn,
|
|
GDALDataType eType)
|
|
|
|
{
|
|
poDS = poGDSIn;
|
|
nBand = nBandIn;
|
|
|
|
eDataType = eType;
|
|
|
|
nBlockXSize = poGDSIn->nRasterXSize;
|
|
nBlockYSize = 1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr SAR_CEOSRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *)poDS;
|
|
|
|
struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
|
|
|
|
int offset;
|
|
CalcCeosSARImageFilePosition(&(poGDS->sVolume), nBand, nBlockYOff + 1,
|
|
nullptr, &offset);
|
|
|
|
offset += ImageDesc->ImageDataStart;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load all the pixel data associated with this scanline. */
|
|
/* Ensure we handle multiple record scanlines properly. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nPixelsRead = 0;
|
|
|
|
GByte *pabyRecord =
|
|
(GByte *)CPLMalloc(ImageDesc->BytesPerPixel * nBlockXSize);
|
|
|
|
for (int iRecord = 0; iRecord < ImageDesc->RecordsPerLine; iRecord++)
|
|
{
|
|
int nPixelsToRead;
|
|
|
|
if (nPixelsRead + ImageDesc->PixelsPerRecord > nBlockXSize)
|
|
nPixelsToRead = nBlockXSize - nPixelsRead;
|
|
else
|
|
nPixelsToRead = ImageDesc->PixelsPerRecord;
|
|
|
|
CPL_IGNORE_RET_VAL(VSIFSeekL(poGDS->fpImage, offset, SEEK_SET));
|
|
CPL_IGNORE_RET_VAL(VSIFReadL(
|
|
pabyRecord + nPixelsRead * ImageDesc->BytesPerPixel, 1,
|
|
nPixelsToRead * ImageDesc->BytesPerPixel, poGDS->fpImage));
|
|
|
|
nPixelsRead += nPixelsToRead;
|
|
offset += ImageDesc->BytesPerRecord;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Copy the desired band out based on the size of the type, and */
|
|
/* the interleaving mode. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int nBytesPerSample = GDALGetDataTypeSize(eDataType) / 8;
|
|
|
|
if (ImageDesc->ChannelInterleaving == CEOS_IL_PIXEL)
|
|
{
|
|
GDALCopyWords(pabyRecord + (nBand - 1) * nBytesPerSample, eDataType,
|
|
ImageDesc->BytesPerPixel, pImage, eDataType,
|
|
nBytesPerSample, nBlockXSize);
|
|
}
|
|
else if (ImageDesc->ChannelInterleaving == CEOS_IL_LINE)
|
|
{
|
|
GDALCopyWords(pabyRecord + (nBand - 1) * nBytesPerSample * nBlockXSize,
|
|
eDataType, nBytesPerSample, pImage, eDataType,
|
|
nBytesPerSample, nBlockXSize);
|
|
}
|
|
else if (ImageDesc->ChannelInterleaving == CEOS_IL_BAND)
|
|
{
|
|
memcpy(pImage, pabyRecord, nBytesPerSample * nBlockXSize);
|
|
}
|
|
|
|
#ifdef CPL_LSB
|
|
GDALSwapWords(pImage, nBytesPerSample, nBlockXSize, nBytesPerSample);
|
|
#endif
|
|
|
|
CPLFree(pabyRecord);
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* CCPRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
/************************************************************************/
|
|
/* CCPRasterBand() */
|
|
/************************************************************************/
|
|
|
|
CCPRasterBand::CCPRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn,
|
|
GDALDataType eType)
|
|
|
|
{
|
|
poDS = poGDSIn;
|
|
nBand = nBandIn;
|
|
|
|
eDataType = eType;
|
|
|
|
nBlockXSize = poGDSIn->nRasterXSize;
|
|
nBlockYSize = 1;
|
|
|
|
if (nBand == 1)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "HH");
|
|
else if (nBand == 2)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "HV");
|
|
else if (nBand == 3)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "VH");
|
|
else if (nBand == 4)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "VV");
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
/* From: http://southport.jpl.nasa.gov/software/dcomp/dcomp.html
|
|
|
|
ysca = sqrt{ [ (Byte(2) / 254 ) + 1.5] 2Byte(1) }
|
|
|
|
Re(SHH) = byte(3) ysca/127
|
|
|
|
Im(SHH) = byte(4) ysca/127
|
|
|
|
Re(SHV) = byte(5) ysca/127
|
|
|
|
Im(SHV) = byte(6) ysca/127
|
|
|
|
Re(SVH) = byte(7) ysca/127
|
|
|
|
Im(SVH) = byte(8) ysca/127
|
|
|
|
Re(SVV) = byte(9) ysca/127
|
|
|
|
Im(SVV) = byte(10) ysca/127
|
|
|
|
*/
|
|
|
|
CPLErr CCPRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *)poDS;
|
|
|
|
struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
|
|
|
|
int offset = ImageDesc->FileDescriptorLength +
|
|
ImageDesc->BytesPerRecord * nBlockYOff +
|
|
ImageDesc->ImageDataStart;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load all the pixel data associated with this scanline. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
|
|
|
|
GByte *pabyRecord = (GByte *)CPLMalloc(nBytesToRead);
|
|
|
|
if (VSIFSeekL(poGDS->fpImage, offset, SEEK_SET) != 0 ||
|
|
(int)VSIFReadL(pabyRecord, 1, nBytesToRead, poGDS->fpImage) !=
|
|
nBytesToRead)
|
|
{
|
|
CPLError(CE_Failure, CPLE_FileIO,
|
|
"Error reading %d bytes of CEOS record data at offset %d.\n"
|
|
"Reading file %s failed.",
|
|
nBytesToRead, offset, poGDS->GetDescription());
|
|
CPLFree(pabyRecord);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize our power table if this is our first time through. */
|
|
/* -------------------------------------------------------------------- */
|
|
static float afPowTable[256];
|
|
static bool bPowTableInitialized = false;
|
|
|
|
if (!bPowTableInitialized)
|
|
{
|
|
bPowTableInitialized = true;
|
|
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
afPowTable[i] = (float)pow(2.0, i - 128);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Copy the desired band out based on the size of the type, and */
|
|
/* the interleaving mode. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (int iX = 0; iX < nBlockXSize; iX++)
|
|
{
|
|
unsigned char *pabyGroup = pabyRecord + iX * ImageDesc->BytesPerPixel;
|
|
signed char *Byte =
|
|
(signed char *)pabyGroup - 1; /* A ones based alias */
|
|
double dfReSHH, dfImSHH, dfReSHV, dfImSHV, dfReSVH, dfImSVH, dfReSVV,
|
|
dfImSVV;
|
|
|
|
const double dfScale =
|
|
sqrt((Byte[2] / 254.0 + 1.5) * afPowTable[Byte[1] + 128]);
|
|
|
|
if (nBand == 1)
|
|
{
|
|
dfReSHH = Byte[3] * dfScale / 127.0;
|
|
dfImSHH = Byte[4] * dfScale / 127.0;
|
|
|
|
((float *)pImage)[iX * 2] = (float)dfReSHH;
|
|
((float *)pImage)[iX * 2 + 1] = (float)dfImSHH;
|
|
}
|
|
else if (nBand == 2)
|
|
{
|
|
dfReSHV = Byte[5] * dfScale / 127.0;
|
|
dfImSHV = Byte[6] * dfScale / 127.0;
|
|
|
|
((float *)pImage)[iX * 2] = (float)dfReSHV;
|
|
((float *)pImage)[iX * 2 + 1] = (float)dfImSHV;
|
|
}
|
|
else if (nBand == 3)
|
|
{
|
|
dfReSVH = Byte[7] * dfScale / 127.0;
|
|
dfImSVH = Byte[8] * dfScale / 127.0;
|
|
|
|
((float *)pImage)[iX * 2] = (float)dfReSVH;
|
|
((float *)pImage)[iX * 2 + 1] = (float)dfImSVH;
|
|
}
|
|
else if (nBand == 4)
|
|
{
|
|
dfReSVV = Byte[9] * dfScale / 127.0;
|
|
dfImSVV = Byte[10] * dfScale / 127.0;
|
|
|
|
((float *)pImage)[iX * 2] = (float)dfReSVV;
|
|
((float *)pImage)[iX * 2 + 1] = (float)dfImSVV;
|
|
}
|
|
}
|
|
|
|
CPLFree(pabyRecord);
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* PALSARRasterBand */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
/************************************************************************/
|
|
/* PALSARRasterBand() */
|
|
/************************************************************************/
|
|
|
|
PALSARRasterBand::PALSARRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn)
|
|
|
|
{
|
|
poDS = poGDSIn;
|
|
nBand = nBandIn;
|
|
|
|
eDataType = GDT_CInt16;
|
|
|
|
nBlockXSize = poGDSIn->nRasterXSize;
|
|
nBlockYSize = 1;
|
|
|
|
if (nBand == 1)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_11");
|
|
else if (nBand == 2)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_22");
|
|
else if (nBand == 3)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_33");
|
|
else if (nBand == 4)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_12");
|
|
else if (nBand == 5)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_13");
|
|
else if (nBand == 6)
|
|
SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_23");
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/* */
|
|
/* Based on ERSDAC-VX-CEOS-004 */
|
|
/************************************************************************/
|
|
|
|
CPLErr PALSARRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *)poDS;
|
|
|
|
struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
|
|
|
|
int offset = ImageDesc->FileDescriptorLength +
|
|
ImageDesc->BytesPerRecord * nBlockYOff +
|
|
ImageDesc->ImageDataStart;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Load all the pixel data associated with this scanline. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
|
|
|
|
GByte *pabyRecord = (GByte *)CPLMalloc(nBytesToRead);
|
|
|
|
if (VSIFSeekL(poGDS->fpImage, offset, SEEK_SET) != 0 ||
|
|
(int)VSIFReadL(pabyRecord, 1, nBytesToRead, poGDS->fpImage) !=
|
|
nBytesToRead)
|
|
{
|
|
CPLError(CE_Failure, CPLE_FileIO,
|
|
"Error reading %d bytes of CEOS record data at offset %d.\n"
|
|
"Reading file %s failed.",
|
|
nBytesToRead, offset, poGDS->GetDescription());
|
|
CPLFree(pabyRecord);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Copy the desired band out based on the size of the type, and */
|
|
/* the interleaving mode. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nBand == 1 || nBand == 2 || nBand == 3)
|
|
{
|
|
// we need to pre-initialize things to set the imaginary component to 0
|
|
memset(pImage, 0, nBlockXSize * 4);
|
|
|
|
GDALCopyWords(pabyRecord + 4 * (nBand - 1), GDT_Int16, 18, pImage,
|
|
GDT_Int16, 4, nBlockXSize);
|
|
#ifdef CPL_LSB
|
|
GDALSwapWords(pImage, 2, nBlockXSize, 4);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
GDALCopyWords(pabyRecord + 6 + 4 * (nBand - 4), GDT_CInt16, 18, pImage,
|
|
GDT_CInt16, 4, nBlockXSize);
|
|
#ifdef CPL_LSB
|
|
GDALSwapWords(pImage, 2, nBlockXSize * 2, 2);
|
|
#endif
|
|
}
|
|
CPLFree(pabyRecord);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Convert the values into covariance form as per: */
|
|
/* -------------------------------------------------------------------- */
|
|
/*
|
|
** 1) PALSAR- adjust so that it reads bands as a covariance matrix, and
|
|
** set polarimetric interpretation accordingly:
|
|
**
|
|
** Covariance_11=HH*conj(HH): already there
|
|
** Covariance_22=2*HV*conj(HV): need a factor of 2
|
|
** Covariance_33=VV*conj(VV): already there
|
|
** Covariance_12=sqrt(2)*HH*conj(HV): need the sqrt(2) factor
|
|
** Covariance_13=HH*conj(VV): already there
|
|
** Covariance_23=sqrt(2)*HV*conj(VV): need to take the conjugate, then
|
|
** multiply by sqrt(2)
|
|
**
|
|
*/
|
|
|
|
if (nBand == 2)
|
|
{
|
|
GInt16 *panLine = (GInt16 *)pImage;
|
|
|
|
for (int i = 0; i < nBlockXSize * 2; i++)
|
|
{
|
|
panLine[i] = (GInt16)CastToGInt16((float)2.0 * panLine[i]);
|
|
}
|
|
}
|
|
else if (nBand == 4)
|
|
{
|
|
const double sqrt_2 = pow(2.0, 0.5);
|
|
GInt16 *panLine = (GInt16 *)pImage;
|
|
|
|
for (int i = 0; i < nBlockXSize * 2; i++)
|
|
{
|
|
panLine[i] =
|
|
(GInt16)CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5));
|
|
}
|
|
}
|
|
else if (nBand == 6)
|
|
{
|
|
GInt16 *panLine = (GInt16 *)pImage;
|
|
const double sqrt_2 = pow(2.0, 0.5);
|
|
|
|
// real portion - just multiple by sqrt(2)
|
|
for (int i = 0; i < nBlockXSize * 2; i += 2)
|
|
{
|
|
panLine[i] =
|
|
(GInt16)CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5));
|
|
}
|
|
|
|
// imaginary portion - conjugate and multiply
|
|
for (int i = 1; i < nBlockXSize * 2; i += 2)
|
|
{
|
|
panLine[i] =
|
|
(GInt16)CastToGInt16((float)floor(-panLine[i] * sqrt_2 + 0.5));
|
|
}
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* SAR_CEOSDataset */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
/************************************************************************/
|
|
/* SAR_CEOSDataset() */
|
|
/************************************************************************/
|
|
|
|
SAR_CEOSDataset::SAR_CEOSDataset()
|
|
: fpImage(nullptr), papszTempMD(nullptr), nGCPCount(0), pasGCPList(nullptr),
|
|
papszExtraFiles(nullptr)
|
|
{
|
|
m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
|
m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
|
|
|
|
sVolume.Flavor = 0;
|
|
sVolume.Sensor = 0;
|
|
sVolume.ProductType = 0;
|
|
sVolume.FileNamingConvention = 0;
|
|
|
|
sVolume.VolumeDirectoryFile = 0;
|
|
sVolume.SARLeaderFile = 0;
|
|
sVolume.ImagryOptionsFile = 0;
|
|
sVolume.SARTrailerFile = 0;
|
|
sVolume.NullVolumeDirectoryFile = 0;
|
|
|
|
sVolume.ImageDesc.ImageDescValid = 0;
|
|
sVolume.ImageDesc.NumChannels = 0;
|
|
sVolume.ImageDesc.ChannelInterleaving = 0;
|
|
sVolume.ImageDesc.DataType = 0;
|
|
sVolume.ImageDesc.BytesPerRecord = 0;
|
|
sVolume.ImageDesc.Lines = 0;
|
|
sVolume.ImageDesc.TopBorderPixels = 0;
|
|
sVolume.ImageDesc.BottomBorderPixels = 0;
|
|
sVolume.ImageDesc.PixelsPerLine = 0;
|
|
sVolume.ImageDesc.LeftBorderPixels = 0;
|
|
sVolume.ImageDesc.RightBorderPixels = 0;
|
|
sVolume.ImageDesc.BytesPerPixel = 0;
|
|
sVolume.ImageDesc.RecordsPerLine = 0;
|
|
sVolume.ImageDesc.PixelsPerRecord = 0;
|
|
sVolume.ImageDesc.ImageDataStart = 0;
|
|
sVolume.ImageDesc.ImageSuffixData = 0;
|
|
sVolume.ImageDesc.FileDescriptorLength = 0;
|
|
sVolume.ImageDesc.PixelOrder = 0;
|
|
sVolume.ImageDesc.LineOrder = 0;
|
|
sVolume.ImageDesc.PixelDataBytesPerRecord = 0;
|
|
|
|
sVolume.RecordList = nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~SAR_CEOSDataset() */
|
|
/************************************************************************/
|
|
|
|
SAR_CEOSDataset::~SAR_CEOSDataset()
|
|
|
|
{
|
|
FlushCache(true);
|
|
|
|
CSLDestroy(papszTempMD);
|
|
|
|
if (fpImage != nullptr)
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
|
|
|
|
if (nGCPCount > 0)
|
|
{
|
|
GDALDeinitGCPs(nGCPCount, pasGCPList);
|
|
}
|
|
CPLFree(pasGCPList);
|
|
|
|
if (sVolume.RecordList)
|
|
{
|
|
for (Link_t *Links = sVolume.RecordList; Links != nullptr;
|
|
Links = Links->next)
|
|
{
|
|
if (Links->object)
|
|
{
|
|
DeleteCeosRecord((CeosRecord_t *)Links->object);
|
|
Links->object = nullptr;
|
|
}
|
|
}
|
|
DestroyList(sVolume.RecordList);
|
|
}
|
|
FreeRecipes();
|
|
CSLDestroy(papszExtraFiles);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPCount() */
|
|
/************************************************************************/
|
|
|
|
int SAR_CEOSDataset::GetGCPCount()
|
|
|
|
{
|
|
return nGCPCount;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
const OGRSpatialReference *SAR_CEOSDataset::GetGCPSpatialRef() const
|
|
|
|
{
|
|
if (nGCPCount > 0)
|
|
return &m_oSRS;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCP() */
|
|
/************************************************************************/
|
|
|
|
const GDAL_GCP *SAR_CEOSDataset::GetGCPs()
|
|
|
|
{
|
|
return pasGCPList;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadataDomainList() */
|
|
/************************************************************************/
|
|
|
|
char **SAR_CEOSDataset::GetMetadataDomainList()
|
|
{
|
|
return CSLAddString(GDALDataset::GetMetadataDomainList(),
|
|
"ceos-FFF-n-n-n-n:r");
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadata() */
|
|
/* */
|
|
/* We provide our own GetMetadata() so that we can override */
|
|
/* behavior for some very specialized domain names intended to */
|
|
/* give us access to raw record data. */
|
|
/* */
|
|
/* The domain must look like: */
|
|
/* ceos-FFF-n-n-n-n:r */
|
|
/* */
|
|
/* FFF - The file id - one of vol, lea, img, trl or nul. */
|
|
/* n-n-n-n - the record type code such as 18-10-18-20 for the */
|
|
/* dataset summary record in the leader file. */
|
|
/* :r - The zero based record number to fetch (optional) */
|
|
/* */
|
|
/* Note that only records that are pre-loaded will be */
|
|
/* accessible, and this normally means that most image records */
|
|
/* are not available. */
|
|
/************************************************************************/
|
|
|
|
char **SAR_CEOSDataset::GetMetadata(const char *pszDomain)
|
|
|
|
{
|
|
if (pszDomain == nullptr || !STARTS_WITH_CI(pszDomain, "ceos-"))
|
|
return GDALDataset::GetMetadata(pszDomain);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Identify which file to fetch the file from. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nFileId = -1;
|
|
|
|
if (STARTS_WITH_CI(pszDomain, "ceos-vol"))
|
|
{
|
|
nFileId = CEOS_VOLUME_DIR_FILE;
|
|
}
|
|
else if (STARTS_WITH_CI(pszDomain, "ceos-lea"))
|
|
{
|
|
nFileId = CEOS_LEADER_FILE;
|
|
}
|
|
else if (STARTS_WITH_CI(pszDomain, "ceos-img"))
|
|
{
|
|
nFileId = CEOS_IMAGRY_OPT_FILE;
|
|
}
|
|
else if (STARTS_WITH_CI(pszDomain, "ceos-trl"))
|
|
{
|
|
nFileId = CEOS_TRAILER_FILE;
|
|
}
|
|
else if (STARTS_WITH_CI(pszDomain, "ceos-nul"))
|
|
{
|
|
nFileId = CEOS_NULL_VOL_FILE;
|
|
}
|
|
else
|
|
return nullptr;
|
|
|
|
pszDomain += 8;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Identify the record type. */
|
|
/* -------------------------------------------------------------------- */
|
|
int a, b, c, d, nRecordIndex = -1;
|
|
|
|
if (sscanf(pszDomain, "-%d-%d-%d-%d:%d", &a, &b, &c, &d, &nRecordIndex) !=
|
|
5 &&
|
|
sscanf(pszDomain, "-%d-%d-%d-%d", &a, &b, &c, &d) != 4)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
CeosTypeCode_t sTypeCode = QuadToTC(a, b, c, d);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to fetch the record. */
|
|
/* -------------------------------------------------------------------- */
|
|
CeosRecord_t *record = FindCeosRecord(sVolume.RecordList, sTypeCode,
|
|
nFileId, -1, nRecordIndex);
|
|
|
|
if (record == nullptr)
|
|
return nullptr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Massage the data into a safe textual format. The RawRecord */
|
|
/* just has zero bytes turned into spaces while the */
|
|
/* EscapedRecord has regular backslash escaping applied to zero */
|
|
/* chars, double quotes, and backslashes. */
|
|
/* just turn zero bytes into spaces. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
CSLDestroy(papszTempMD);
|
|
|
|
// Escaped version
|
|
char *pszSafeCopy = CPLEscapeString((char *)record->Buffer, record->Length,
|
|
CPLES_BackslashQuotable);
|
|
papszTempMD = CSLSetNameValue(nullptr, "EscapedRecord", pszSafeCopy);
|
|
CPLFree(pszSafeCopy);
|
|
|
|
// Copy with '\0' replaced by spaces.
|
|
|
|
pszSafeCopy = (char *)CPLCalloc(1, record->Length + 1);
|
|
memcpy(pszSafeCopy, record->Buffer, record->Length);
|
|
|
|
for (int i = 0; i < record->Length; i++)
|
|
if (pszSafeCopy[i] == '\0')
|
|
pszSafeCopy[i] = ' ';
|
|
|
|
papszTempMD = CSLSetNameValue(papszTempMD, "RawRecord", pszSafeCopy);
|
|
|
|
CPLFree(pszSafeCopy);
|
|
|
|
return papszTempMD;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ScanForMetadata() */
|
|
/************************************************************************/
|
|
|
|
void SAR_CEOSDataset::ScanForMetadata()
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the volume id (with the sensor name) */
|
|
/* -------------------------------------------------------------------- */
|
|
CeosRecord_t *record =
|
|
FindCeosRecord(sVolume.RecordList, VOLUME_DESCRIPTOR_RECORD_TC,
|
|
CEOS_VOLUME_DIR_FILE, -1, -1);
|
|
|
|
char szVolId[128];
|
|
szVolId[0] = '\0';
|
|
char szField[128];
|
|
szField[0] = '\0';
|
|
if (record != nullptr)
|
|
{
|
|
szVolId[16] = '\0';
|
|
|
|
GetCeosField(record, 61, "A16", szVolId);
|
|
|
|
SetMetadataItem("CEOS_LOGICAL_VOLUME_ID", szVolId);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Processing facility */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
szField[0] = '\0';
|
|
szField[12] = '\0';
|
|
|
|
GetCeosField(record, 149, "A12", szField);
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PROCESSING_FACILITY", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Agency */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
szField[8] = '\0';
|
|
|
|
GetCeosField(record, 141, "A8", szField);
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PROCESSING_AGENCY", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Country */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
szField[12] = '\0';
|
|
|
|
GetCeosField(record, 129, "A12", szField);
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PROCESSING_COUNTRY", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* software id. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
szField[12] = '\0';
|
|
|
|
GetCeosField(record, 33, "A12", szField);
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_SOFTWARE_ID", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* product identifier. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
szField[8] = '\0';
|
|
|
|
GetCeosField(record, 261, "A8", szField);
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PRODUCT_ID", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* volume identifier. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
szField[16] = '\0';
|
|
|
|
GetCeosField(record, 77, "A16", szField);
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_VOLSET_ID", szField);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Dataset summary record. */
|
|
/* ==================================================================== */
|
|
record = FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record == nullptr)
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_ASF_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record == nullptr)
|
|
record = FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
|
|
CEOS_TRAILER_FILE, -1, -1);
|
|
|
|
if (record == nullptr)
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_ERS2_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record != nullptr)
|
|
{
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Get the acquisition date. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
szField[0] = '\0';
|
|
szField[32] = '\0';
|
|
|
|
GetCeosField(record, 69, "A32", szField);
|
|
|
|
SetMetadataItem("CEOS_ACQUISITION_TIME", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Ascending/Descending */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 101, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (strstr(szVolId, "RSAT") != nullptr &&
|
|
!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_ASC_DES", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* True heading - at least for ERS2. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 149, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_TRUE_HEADING", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Ellipsoid */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 165, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_ELLIPSOID", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Semimajor, semiminor axis */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 181, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_SEMI_MAJOR", szField);
|
|
|
|
GetCeosField(record, 197, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_SEMI_MINOR", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* SCENE LENGTH KM */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 341, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_SCENE_LENGTH_KM", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* SCENE WIDTH KM */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 357, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_SCENE_WIDTH_KM", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* MISSION ID */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 397, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_MISSION_ID", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* SENSOR ID */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 413, "A32", szField);
|
|
szField[32] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_SENSOR_ID", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* ORBIT NUMBER */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 445, "A8", szField);
|
|
szField[8] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_ORBIT_NUMBER", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Platform latitude */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 453, "A8", szField);
|
|
szField[8] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PLATFORM_LATITUDE", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Platform longitude */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 461, "A8", szField);
|
|
szField[8] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PLATFORM_LONGITUDE", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Platform heading - at least for ERS2. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 469, "A8", szField);
|
|
szField[8] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PLATFORM_HEADING", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Look Angle. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 477, "A8", szField);
|
|
szField[8] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_SENSOR_CLOCK_ANGLE", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Incidence angle */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 485, "A8", szField);
|
|
szField[8] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_INC_ANGLE", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Facility */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 1047, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_FACILITY", szField);
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Pixel time direction indicator */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 1527, "A8", szField);
|
|
szField[8] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PIXEL_TIME_DIR", szField);
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Line spacing */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 1687, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_LINE_SPACING_METERS", szField);
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Pixel spacing */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
GetCeosField(record, 1703, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PIXEL_SPACING_METERS", szField);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get the beam mode, for radarsat. */
|
|
/* -------------------------------------------------------------------- */
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_COMPENSATION_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (strstr(szVolId, "RSAT") != nullptr && record != nullptr)
|
|
{
|
|
szField[16] = '\0';
|
|
|
|
GetCeosField(record, 4189, "A16", szField);
|
|
|
|
SetMetadataItem("CEOS_BEAM_TYPE", szField);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* ERS calibration and incidence angle info */
|
|
/* ==================================================================== */
|
|
record = FindCeosRecord(sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record == nullptr)
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_ALT_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record != nullptr)
|
|
{
|
|
GetCeosField(record, 13, "A64", szField);
|
|
szField[64] = '\0';
|
|
|
|
/* Avoid PCS records, which don't contain necessary info */
|
|
if (strstr(szField, "GENERAL") == nullptr)
|
|
record = nullptr;
|
|
}
|
|
|
|
if (record != nullptr)
|
|
{
|
|
GetCeosField(record, 583, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_INC_ANGLE_FIRST_RANGE", szField);
|
|
|
|
GetCeosField(record, 599, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_INC_ANGLE_CENTRE_RANGE", szField);
|
|
|
|
GetCeosField(record, 615, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_INC_ANGLE_LAST_RANGE", szField);
|
|
|
|
GetCeosField(record, 663, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_CALIBRATION_CONSTANT_K", szField);
|
|
|
|
GetCeosField(record, 1855, "A20", szField);
|
|
szField[20] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C0", szField);
|
|
|
|
GetCeosField(record, 1875, "A20", szField);
|
|
szField[20] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C1", szField);
|
|
|
|
GetCeosField(record, 1895, "A20", szField);
|
|
szField[20] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C2", szField);
|
|
|
|
GetCeosField(record, 1915, "A20", szField);
|
|
szField[20] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C3", szField);
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
/* Detailed Processing Parameters (Radarsat) */
|
|
/* -------------------------------------------------------------------- */
|
|
record = FindCeosRecord(sVolume.RecordList, RSAT_PROC_PARAM_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record == nullptr)
|
|
record = FindCeosRecord(sVolume.RecordList, RSAT_PROC_PARAM_TC,
|
|
CEOS_TRAILER_FILE, -1, -1);
|
|
|
|
if (record != nullptr)
|
|
{
|
|
GetCeosField(record, 192, "A21", szField);
|
|
szField[21] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PROC_START", szField);
|
|
|
|
GetCeosField(record, 213, "A21", szField);
|
|
szField[21] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_PROC_STOP", szField);
|
|
|
|
GetCeosField(record, 4649, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_EPH_ORB_DATA_0", szField);
|
|
|
|
GetCeosField(record, 4665, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_EPH_ORB_DATA_1", szField);
|
|
|
|
GetCeosField(record, 4681, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_EPH_ORB_DATA_2", szField);
|
|
|
|
GetCeosField(record, 4697, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_EPH_ORB_DATA_3", szField);
|
|
|
|
GetCeosField(record, 4713, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_EPH_ORB_DATA_4", szField);
|
|
|
|
GetCeosField(record, 4729, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_EPH_ORB_DATA_5", szField);
|
|
|
|
GetCeosField(record, 4745, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_EPH_ORB_DATA_6", szField);
|
|
|
|
GetCeosField(record, 4908, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C0", szField);
|
|
|
|
GetCeosField(record, 4924, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C1", szField);
|
|
|
|
GetCeosField(record, 4940, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C2", szField);
|
|
|
|
GetCeosField(record, 4956, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C3", szField);
|
|
|
|
GetCeosField(record, 4972, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C4", szField);
|
|
|
|
GetCeosField(record, 4988, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GROUND_TO_SLANT_C5", szField);
|
|
|
|
GetCeosField(record, 7334, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_INC_ANGLE_FIRST_RANGE", szField);
|
|
|
|
GetCeosField(record, 7350, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_INC_ANGLE_LAST_RANGE", szField);
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get process-to-raw data coordinate translation values. These */
|
|
/* are likely specific to Atlantis APP products. */
|
|
/* -------------------------------------------------------------------- */
|
|
record = FindCeosRecord(sVolume.RecordList, IMAGE_HEADER_RECORD_TC,
|
|
CEOS_IMAGRY_OPT_FILE, -1, -1);
|
|
|
|
if (record != nullptr)
|
|
{
|
|
GetCeosField(record, 449, "A4", szField);
|
|
szField[4] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_CORNER", szField);
|
|
|
|
GetCeosField(record, 453, "A4", szField);
|
|
szField[4] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_TRANSPOSE", szField);
|
|
|
|
GetCeosField(record, 457, "A4", szField);
|
|
szField[4] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_START_SAMPLE", szField);
|
|
|
|
GetCeosField(record, 461, "A5", szField);
|
|
szField[5] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_START_PULSE", szField);
|
|
|
|
GetCeosField(record, 466, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_FAST_ALPHA", szField);
|
|
|
|
GetCeosField(record, 482, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_FAST_BETA", szField);
|
|
|
|
GetCeosField(record, 498, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_SLOW_ALPHA", szField);
|
|
|
|
GetCeosField(record, 514, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_SLOW_BETA", szField);
|
|
|
|
GetCeosField(record, 530, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_DM_FAST_ALPHA_2", szField);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to find calibration information from Radiometric Data */
|
|
/* Record. */
|
|
/* -------------------------------------------------------------------- */
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_DATA_RECORD_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record == nullptr)
|
|
record = FindCeosRecord(sVolume.RecordList,
|
|
LEADER_RADIOMETRIC_DATA_RECORD_TC,
|
|
CEOS_TRAILER_FILE, -1, -1);
|
|
|
|
if (record != nullptr)
|
|
{
|
|
GetCeosField(record, 8317, "A16", szField);
|
|
szField[16] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_CALIBRATION_OFFSET", szField);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* For ERS Standard Format Landsat scenes we pick up the */
|
|
/* calibration offset and gain from the Radiometric Ancillary */
|
|
/* Record. */
|
|
/* -------------------------------------------------------------------- */
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, QuadToTC(0x3f, 0x24, 0x12, 0x09),
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
if (record != nullptr)
|
|
{
|
|
GetCeosField(record, 29, "A20", szField);
|
|
szField[20] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_OFFSET_A0", szField);
|
|
|
|
GetCeosField(record, 49, "A20", szField);
|
|
szField[20] = '\0';
|
|
|
|
if (!STARTS_WITH_CI(szField, " "))
|
|
SetMetadataItem("CEOS_GAIN_A1", szField);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* For ERS Standard Format Landsat scenes we pick up the */
|
|
/* gain setting from the Scene Header Record. */
|
|
/* -------------------------------------------------------------------- */
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, QuadToTC(0x12, 0x12, 0x12, 0x09),
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
if (record != nullptr)
|
|
{
|
|
GetCeosField(record, 1486, "A1", szField);
|
|
szField[1] = '\0';
|
|
|
|
if (szField[0] == 'H' || szField[0] == 'V')
|
|
SetMetadataItem("CEOS_GAIN_SETTING", szField);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ScanForMapProjection() */
|
|
/* */
|
|
/* Try to find a map projection record, and read corner points */
|
|
/* from it. This has only been tested with ERS products. */
|
|
/************************************************************************/
|
|
|
|
int SAR_CEOSDataset::ScanForMapProjection()
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Find record, and try to determine if it has useful GCPs. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
CeosRecord_t *record =
|
|
FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
int gcp_ordering_mode = CEOS_STD_MAPREC_GCP_ORDER;
|
|
/* JERS from Japan */
|
|
if (record == nullptr)
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_JERS_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
|
|
if (record == nullptr)
|
|
{
|
|
record =
|
|
FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_ASF_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
gcp_ordering_mode = CEOS_ASF_MAPREC_GCP_ORDER;
|
|
}
|
|
if (record == nullptr)
|
|
{
|
|
record = FindCeosRecord(sVolume.RecordList, LEADER_FACILITY_ASF_TC,
|
|
CEOS_LEADER_FILE, -1, -1);
|
|
gcp_ordering_mode = CEOS_ASF_FACREC_GCP_ORDER;
|
|
}
|
|
|
|
if (record == nullptr)
|
|
return FALSE;
|
|
|
|
char szField[100];
|
|
memset(szField, 0, 17);
|
|
GetCeosField(record, 29, "A16", szField);
|
|
|
|
int GCPFieldSize = 16;
|
|
int GCPOffset = 1073;
|
|
|
|
if (!STARTS_WITH_CI(szField, "Slant Range") &&
|
|
!STARTS_WITH_CI(szField, "Ground Range") &&
|
|
!STARTS_WITH_CI(szField, "GEOCODED"))
|
|
{
|
|
/* detect ASF map projection record */
|
|
GetCeosField(record, 1079, "A7", szField);
|
|
if (!STARTS_WITH_CI(szField, "Slant") &&
|
|
!STARTS_WITH_CI(szField, "Ground"))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
GCPFieldSize = 17;
|
|
GCPOffset = 157;
|
|
}
|
|
}
|
|
|
|
char FieldSize[4];
|
|
snprintf(FieldSize, sizeof(FieldSize), "A%d", GCPFieldSize);
|
|
|
|
GetCeosField(record, GCPOffset, FieldSize, szField);
|
|
if (STARTS_WITH_CI(szField, " "))
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read corner points. */
|
|
/* -------------------------------------------------------------------- */
|
|
nGCPCount = 4;
|
|
pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), nGCPCount);
|
|
|
|
GDALInitGCPs(nGCPCount, pasGCPList);
|
|
|
|
for (int i = 0; i < nGCPCount; i++)
|
|
{
|
|
char szId[32];
|
|
|
|
snprintf(szId, sizeof(szId), "%d", i + 1);
|
|
CPLFree(pasGCPList[i].pszId);
|
|
pasGCPList[i].pszId = CPLStrdup(szId);
|
|
|
|
GetCeosField(record, GCPOffset + (GCPFieldSize * 2) * i, FieldSize,
|
|
szField);
|
|
pasGCPList[i].dfGCPY = CPLAtof(szField);
|
|
GetCeosField(record, GCPOffset + GCPFieldSize + (GCPFieldSize * 2) * i,
|
|
FieldSize, szField);
|
|
pasGCPList[i].dfGCPX = CPLAtof(szField);
|
|
pasGCPList[i].dfGCPZ = 0.0;
|
|
}
|
|
|
|
/* Map Projection Record has the order UL UR LR LL
|
|
ASF Facility Data Record has the order UL,LL,UR,LR
|
|
ASF Map Projection Record has the order LL, LR, UR, UL */
|
|
|
|
pasGCPList[0].dfGCPLine = 0.5;
|
|
pasGCPList[0].dfGCPPixel = 0.5;
|
|
|
|
switch (gcp_ordering_mode)
|
|
{
|
|
case CEOS_ASF_FACREC_GCP_ORDER:
|
|
pasGCPList[1].dfGCPLine = nRasterYSize - 0.5;
|
|
pasGCPList[1].dfGCPPixel = 0.5;
|
|
|
|
pasGCPList[2].dfGCPLine = 0.5;
|
|
pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
|
|
|
|
pasGCPList[3].dfGCPLine = nRasterYSize - 0.5;
|
|
pasGCPList[3].dfGCPPixel = nRasterXSize - 0.5;
|
|
break;
|
|
case CEOS_STD_MAPREC_GCP_ORDER:
|
|
pasGCPList[1].dfGCPLine = 0.5;
|
|
pasGCPList[1].dfGCPPixel = nRasterXSize - 0.5;
|
|
|
|
pasGCPList[2].dfGCPLine = nRasterYSize - 0.5;
|
|
pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
|
|
|
|
pasGCPList[3].dfGCPLine = nRasterYSize - 0.5;
|
|
pasGCPList[3].dfGCPPixel = 0.5;
|
|
break;
|
|
case CEOS_ASF_MAPREC_GCP_ORDER:
|
|
pasGCPList[0].dfGCPLine = nRasterYSize - 0.5;
|
|
pasGCPList[0].dfGCPPixel = 0.5;
|
|
|
|
pasGCPList[1].dfGCPLine = nRasterYSize - 0.5;
|
|
pasGCPList[1].dfGCPPixel = nRasterXSize - 0.5;
|
|
|
|
pasGCPList[2].dfGCPLine = 0.5;
|
|
pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
|
|
|
|
pasGCPList[3].dfGCPLine = 0.5;
|
|
pasGCPList[3].dfGCPPixel = 0.5;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ScanForGCPs() */
|
|
/************************************************************************/
|
|
|
|
void SAR_CEOSDataset::ScanForGCPs()
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a standard 180 bytes of prefix data (192 bytes */
|
|
/* including the record marker information)? If not, it is */
|
|
/* unlikely that the GCPs are available. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (sVolume.ImageDesc.ImageDataStart < 192)
|
|
{
|
|
ScanForMapProjection();
|
|
return;
|
|
}
|
|
|
|
/* ASF L1 products do not have valid data
|
|
in the lat/long first/mid/last fields */
|
|
const char *pszValue = GetMetadataItem("CEOS_FACILITY");
|
|
if ((pszValue != nullptr) && (strncmp(pszValue, "ASF", 3) == 0))
|
|
{
|
|
ScanForMapProjection();
|
|
return;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Just sample fix scanlines through the image for GCPs, to */
|
|
/* return 15 GCPs. That is an adequate coverage for most */
|
|
/* purposes. A GCP is collected from the beginning, middle and */
|
|
/* end of each scanline. */
|
|
/* -------------------------------------------------------------------- */
|
|
nGCPCount = 0;
|
|
int nGCPMax = 15;
|
|
pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), nGCPMax);
|
|
|
|
int nStep = (GetRasterYSize() - 1) / (nGCPMax / 3 - 1);
|
|
for (int iScanline = 0; iScanline < GetRasterYSize(); iScanline += nStep)
|
|
{
|
|
if (nGCPCount > nGCPMax - 3)
|
|
break;
|
|
|
|
int nFileOffset;
|
|
CalcCeosSARImageFilePosition(&sVolume, 1, iScanline + 1, nullptr,
|
|
&nFileOffset);
|
|
|
|
GInt32 anRecord[192 / 4];
|
|
if (VSIFSeekL(fpImage, nFileOffset, SEEK_SET) != 0 ||
|
|
VSIFReadL(anRecord, 1, 192, fpImage) != 192)
|
|
break;
|
|
|
|
/* loop over first, middle and last pixel gcps */
|
|
|
|
for (int iGCP = 0; iGCP < 3; iGCP++)
|
|
{
|
|
const int nLat = CPL_MSBWORD32(anRecord[132 / 4 + iGCP]);
|
|
const int nLong = CPL_MSBWORD32(anRecord[144 / 4 + iGCP]);
|
|
|
|
if (nLat != 0 || nLong != 0)
|
|
{
|
|
GDALInitGCPs(1, pasGCPList + nGCPCount);
|
|
|
|
CPLFree(pasGCPList[nGCPCount].pszId);
|
|
|
|
char szId[32];
|
|
snprintf(szId, sizeof(szId), "%d", nGCPCount + 1);
|
|
pasGCPList[nGCPCount].pszId = CPLStrdup(szId);
|
|
|
|
pasGCPList[nGCPCount].dfGCPX = nLong / 1000000.0;
|
|
pasGCPList[nGCPCount].dfGCPY = nLat / 1000000.0;
|
|
pasGCPList[nGCPCount].dfGCPZ = 0.0;
|
|
|
|
pasGCPList[nGCPCount].dfGCPLine = iScanline + 0.5;
|
|
|
|
if (iGCP == 0)
|
|
pasGCPList[nGCPCount].dfGCPPixel = 0.5;
|
|
else if (iGCP == 1)
|
|
pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() / 2.0;
|
|
else
|
|
pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() - 0.5;
|
|
|
|
nGCPCount++;
|
|
}
|
|
}
|
|
}
|
|
/* If general GCP's were not found, look for Map Projection (e.g. JERS) */
|
|
if (nGCPCount == 0)
|
|
{
|
|
CPLFree(pasGCPList);
|
|
pasGCPList = nullptr;
|
|
ScanForMapProjection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Open() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *SAR_CEOSDataset::Open(GDALOpenInfo *poOpenInfo)
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Does this appear to be a valid ceos leader record? */
|
|
/* -------------------------------------------------------------------- */
|
|
if (poOpenInfo->nHeaderBytes < CEOS_HEADER_LENGTH ||
|
|
poOpenInfo->fpL == nullptr)
|
|
return nullptr;
|
|
|
|
if ((poOpenInfo->pabyHeader[4] != 0x3f &&
|
|
poOpenInfo->pabyHeader[4] != 0x32) ||
|
|
poOpenInfo->pabyHeader[5] != 0xc0 ||
|
|
poOpenInfo->pabyHeader[6] != 0x12 || poOpenInfo->pabyHeader[7] != 0x12)
|
|
return nullptr;
|
|
|
|
// some products (#1862) have byte swapped record length/number
|
|
// values and will blow stuff up -- explicitly ignore if record index
|
|
// value appears to be little endian.
|
|
if (poOpenInfo->pabyHeader[0] != 0)
|
|
return nullptr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Confirm the requested access is supported. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (poOpenInfo->eAccess == GA_Update)
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_NotSupported,
|
|
"The SAR_CEOS driver does not support update access to existing"
|
|
" datasets.\n");
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a corresponding GDALDataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
auto poDS = cpl::make_unique<SAR_CEOSDataset>();
|
|
std::swap(poDS->fpImage, poOpenInfo->fpL);
|
|
|
|
CeosSARVolume_t *psVolume = &(poDS->sVolume);
|
|
InitCeosSARVolume(psVolume, 0);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to read the current file as an imagery file. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
psVolume->ImagryOptionsFile = TRUE;
|
|
if (ProcessData(poDS->fpImage, CEOS_IMAGRY_OPT_FILE, psVolume, 4,
|
|
VSI_L_OFFSET_MAX) != CE_None)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try the various filenames. */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename));
|
|
char *pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename));
|
|
char *pszExtension = CPLStrdup(CPLGetExtension(poOpenInfo->pszFilename));
|
|
|
|
int nBand;
|
|
if (strlen(pszBasename) > 4)
|
|
nBand = atoi(pszBasename + 4);
|
|
else
|
|
nBand = 0;
|
|
|
|
for (int iFile = 0; iFile < 5; iFile++)
|
|
{
|
|
/* skip image file ... we already did it */
|
|
if (iFile == 2)
|
|
continue;
|
|
|
|
int e = 0;
|
|
while (CeosExtension[e][iFile] != nullptr)
|
|
{
|
|
char *pszFilename = nullptr;
|
|
|
|
/* build filename */
|
|
if (EQUAL(CeosExtension[e][5], "base"))
|
|
{
|
|
char szMadeBasename[32];
|
|
|
|
snprintf(szMadeBasename, sizeof(szMadeBasename),
|
|
CeosExtension[e][iFile], nBand);
|
|
pszFilename = CPLStrdup(
|
|
CPLFormFilename(pszPath, szMadeBasename, pszExtension));
|
|
}
|
|
else if (EQUAL(CeosExtension[e][5], "ext"))
|
|
{
|
|
pszFilename = CPLStrdup(CPLFormFilename(
|
|
pszPath, pszBasename, CeosExtension[e][iFile]));
|
|
}
|
|
else if (EQUAL(CeosExtension[e][5], "whole"))
|
|
{
|
|
pszFilename = CPLStrdup(
|
|
CPLFormFilename(pszPath, CeosExtension[e][iFile], ""));
|
|
}
|
|
|
|
// This is for SAR SLC as per the SAR Toolbox (from ASF).
|
|
else if (EQUAL(CeosExtension[e][5], "ext2"))
|
|
{
|
|
char szThisExtension[32];
|
|
|
|
if (strlen(pszExtension) > 3)
|
|
snprintf(szThisExtension, sizeof(szThisExtension), "%s%s",
|
|
CeosExtension[e][iFile], pszExtension + 3);
|
|
else
|
|
snprintf(szThisExtension, sizeof(szThisExtension), "%s",
|
|
CeosExtension[e][iFile]);
|
|
|
|
pszFilename = CPLStrdup(
|
|
CPLFormFilename(pszPath, pszBasename, szThisExtension));
|
|
}
|
|
|
|
CPLAssert(pszFilename != nullptr);
|
|
if (pszFilename == nullptr)
|
|
return nullptr;
|
|
|
|
/* try to open */
|
|
VSILFILE *process_fp = VSIFOpenL(pszFilename, "rb");
|
|
|
|
/* try upper case */
|
|
if (process_fp == nullptr)
|
|
{
|
|
for (int i = static_cast<int>(strlen(pszFilename)) - 1;
|
|
i >= 0 && pszFilename[i] != '/' && pszFilename[i] != '\\';
|
|
i--)
|
|
{
|
|
if (pszFilename[i] >= 'a' && pszFilename[i] <= 'z')
|
|
pszFilename[i] = pszFilename[i] - 'a' + 'A';
|
|
}
|
|
|
|
process_fp = VSIFOpenL(pszFilename, "rb");
|
|
}
|
|
|
|
if (process_fp != nullptr)
|
|
{
|
|
CPLDebug("CEOS", "Opened %s.\n", pszFilename);
|
|
|
|
poDS->papszExtraFiles =
|
|
CSLAddString(poDS->papszExtraFiles, pszFilename);
|
|
|
|
CPL_IGNORE_RET_VAL(VSIFSeekL(process_fp, 0, SEEK_END));
|
|
if (ProcessData(process_fp, iFile, psVolume, -1,
|
|
VSIFTellL(process_fp)) == 0)
|
|
{
|
|
switch (iFile)
|
|
{
|
|
case 0:
|
|
psVolume->VolumeDirectoryFile = TRUE;
|
|
break;
|
|
case 1:
|
|
psVolume->SARLeaderFile = TRUE;
|
|
break;
|
|
case 3:
|
|
psVolume->SARTrailerFile = TRUE;
|
|
break;
|
|
case 4:
|
|
psVolume->NullVolumeDirectoryFile = TRUE;
|
|
break;
|
|
}
|
|
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(process_fp));
|
|
CPLFree(pszFilename);
|
|
break; /* Exit the while loop, we have this data type*/
|
|
}
|
|
|
|
CPL_IGNORE_RET_VAL(VSIFCloseL(process_fp));
|
|
}
|
|
|
|
CPLFree(pszFilename);
|
|
|
|
e++;
|
|
}
|
|
}
|
|
|
|
CPLFree(pszPath);
|
|
CPLFree(pszBasename);
|
|
CPLFree(pszExtension);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check that we have an image description. */
|
|
/* -------------------------------------------------------------------- */
|
|
GetCeosSARImageDesc(psVolume);
|
|
struct CeosSARImageDesc *psImageDesc = &(psVolume->ImageDesc);
|
|
if (!psImageDesc->ImageDescValid)
|
|
{
|
|
CPLDebug("CEOS",
|
|
"Unable to extract CEOS image description\n"
|
|
"from %s.",
|
|
poOpenInfo->pszFilename);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Establish image type. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALDataType eType;
|
|
|
|
switch (psImageDesc->DataType)
|
|
{
|
|
case CEOS_TYP_CHAR:
|
|
case CEOS_TYP_UCHAR:
|
|
eType = GDT_Byte;
|
|
break;
|
|
|
|
case CEOS_TYP_SHORT:
|
|
eType = GDT_Int16;
|
|
break;
|
|
|
|
case CEOS_TYP_COMPLEX_SHORT:
|
|
case CEOS_TYP_PALSAR_COMPLEX_SHORT:
|
|
eType = GDT_CInt16;
|
|
break;
|
|
|
|
case CEOS_TYP_USHORT:
|
|
eType = GDT_UInt16;
|
|
break;
|
|
|
|
case CEOS_TYP_LONG:
|
|
eType = GDT_Int32;
|
|
break;
|
|
|
|
case CEOS_TYP_ULONG:
|
|
eType = GDT_UInt32;
|
|
break;
|
|
|
|
case CEOS_TYP_FLOAT:
|
|
eType = GDT_Float32;
|
|
break;
|
|
|
|
case CEOS_TYP_DOUBLE:
|
|
eType = GDT_Float64;
|
|
break;
|
|
|
|
case CEOS_TYP_COMPLEX_FLOAT:
|
|
case CEOS_TYP_CCP_COMPLEX_FLOAT:
|
|
eType = GDT_CFloat32;
|
|
break;
|
|
|
|
default:
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Unsupported CEOS image data type %d.\n",
|
|
psImageDesc->DataType);
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Capture some information from the file that is of interest. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->nRasterXSize = psImageDesc->PixelsPerLine +
|
|
psImageDesc->LeftBorderPixels +
|
|
psImageDesc->RightBorderPixels;
|
|
poDS->nRasterYSize = psImageDesc->Lines;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for compressed cross products. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psImageDesc->DataType == CEOS_TYP_CCP_COMPLEX_FLOAT)
|
|
{
|
|
for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
|
|
{
|
|
poDS->SetBand(
|
|
poDS->nBands + 1,
|
|
new CCPRasterBand(poDS.get(), poDS->nBands + 1, eType));
|
|
}
|
|
|
|
/* mark this as a Scattering Matrix product */
|
|
if (poDS->GetRasterCount() == 4)
|
|
{
|
|
poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for PALSAR data. */
|
|
/* -------------------------------------------------------------------- */
|
|
else if (psImageDesc->DataType == CEOS_TYP_PALSAR_COMPLEX_SHORT)
|
|
{
|
|
for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
|
|
{
|
|
poDS->SetBand(poDS->nBands + 1,
|
|
new PALSARRasterBand(poDS.get(), poDS->nBands + 1));
|
|
}
|
|
|
|
/* mark this as a Symmetrized Covariance product if appropriate */
|
|
if (poDS->GetRasterCount() == 6)
|
|
{
|
|
poDS->SetMetadataItem("MATRIX_REPRESENTATION",
|
|
"SYMMETRIZED_COVARIANCE");
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Roll our own ... */
|
|
/* -------------------------------------------------------------------- */
|
|
else if (psImageDesc->RecordsPerLine > 1 ||
|
|
psImageDesc->DataType == CEOS_TYP_CHAR ||
|
|
psImageDesc->DataType == CEOS_TYP_LONG ||
|
|
psImageDesc->DataType == CEOS_TYP_ULONG ||
|
|
psImageDesc->DataType == CEOS_TYP_DOUBLE)
|
|
{
|
|
for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
|
|
{
|
|
poDS->SetBand(
|
|
poDS->nBands + 1,
|
|
new SAR_CEOSRasterBand(poDS.get(), poDS->nBands + 1, eType));
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Use raw services for well behaved files. */
|
|
/* -------------------------------------------------------------------- */
|
|
else
|
|
{
|
|
int StartData;
|
|
CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr, &StartData);
|
|
|
|
/*StartData += psImageDesc->ImageDataStart; */
|
|
|
|
int nLineSize, nLineSize2;
|
|
CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr, &nLineSize);
|
|
CalcCeosSARImageFilePosition(psVolume, 1, 2, nullptr, &nLineSize2);
|
|
|
|
nLineSize = nLineSize2 - nLineSize;
|
|
|
|
for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
|
|
{
|
|
int nStartData, nPixelOffset, nLineOffset;
|
|
|
|
if (psImageDesc->ChannelInterleaving == CEOS_IL_PIXEL)
|
|
{
|
|
CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr,
|
|
&nStartData);
|
|
|
|
nStartData += psImageDesc->ImageDataStart;
|
|
nStartData += psImageDesc->BytesPerPixel * iBand;
|
|
|
|
nPixelOffset =
|
|
psImageDesc->BytesPerPixel * psImageDesc->NumChannels;
|
|
nLineOffset = nLineSize;
|
|
}
|
|
else if (psImageDesc->ChannelInterleaving == CEOS_IL_LINE)
|
|
{
|
|
CalcCeosSARImageFilePosition(psVolume, iBand + 1, 1, nullptr,
|
|
&nStartData);
|
|
|
|
nStartData += psImageDesc->ImageDataStart;
|
|
nPixelOffset = psImageDesc->BytesPerPixel;
|
|
nLineOffset = nLineSize * psImageDesc->NumChannels;
|
|
}
|
|
else if (psImageDesc->ChannelInterleaving == CEOS_IL_BAND)
|
|
{
|
|
CalcCeosSARImageFilePosition(psVolume, iBand + 1, 1, nullptr,
|
|
&nStartData);
|
|
|
|
nStartData += psImageDesc->ImageDataStart;
|
|
nPixelOffset = psImageDesc->BytesPerPixel;
|
|
nLineOffset = nLineSize;
|
|
}
|
|
else
|
|
{
|
|
CPLAssert(false);
|
|
return nullptr;
|
|
}
|
|
|
|
auto poBand = RawRasterBand::Create(
|
|
poDS.get(), poDS->nBands + 1, poDS->fpImage, nStartData,
|
|
nPixelOffset, nLineOffset, eType,
|
|
RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
|
|
RawRasterBand::OwnFP::NO);
|
|
if (!poBand)
|
|
return nullptr;
|
|
poDS->SetBand(poDS->nBands + 1, std::move(poBand));
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Collect metadata. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->ScanForMetadata();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check for GCPs. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->ScanForGCPs();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize any PAM information. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->SetDescription(poOpenInfo->pszFilename);
|
|
poDS->TryLoadXML();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Open overviews. */
|
|
/* -------------------------------------------------------------------- */
|
|
poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
|
|
|
|
return poDS.release();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ProcessData() */
|
|
/************************************************************************/
|
|
static int ProcessData(VSILFILE *fp, int fileid, CeosSARVolume_t *sar,
|
|
int max_records, vsi_l_offset max_bytes)
|
|
|
|
{
|
|
unsigned char temp_buffer[CEOS_HEADER_LENGTH];
|
|
unsigned char *temp_body = nullptr;
|
|
int start = 0;
|
|
int CurrentBodyLength = 0;
|
|
int CurrentType = 0;
|
|
int CurrentSequence = 0;
|
|
int iThisRecord = 0;
|
|
|
|
while (max_records != 0 && max_bytes != 0)
|
|
{
|
|
iThisRecord++;
|
|
|
|
if (VSIFSeekL(fp, start, SEEK_SET) != 0 ||
|
|
VSIFReadL(temp_buffer, 1, CEOS_HEADER_LENGTH, fp) !=
|
|
CEOS_HEADER_LENGTH)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Corrupt CEOS File - cannot read record %d.", iThisRecord);
|
|
CPLFree(temp_body);
|
|
return CE_Failure;
|
|
}
|
|
CeosRecord_t *record = (CeosRecord_t *)CPLMalloc(sizeof(CeosRecord_t));
|
|
record->Length = DetermineCeosRecordBodyLength(temp_buffer);
|
|
|
|
CeosToNative(&(record->Sequence), temp_buffer, 4, 4);
|
|
|
|
if (iThisRecord != record->Sequence)
|
|
{
|
|
if (fileid == CEOS_IMAGRY_OPT_FILE && iThisRecord == 2)
|
|
{
|
|
CPLDebug("SAR_CEOS",
|
|
"Ignoring CEOS file with wrong second record sequence "
|
|
"number - likely it has padded records.");
|
|
CPLFree(record);
|
|
CPLFree(temp_body);
|
|
return CE_Warning;
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Corrupt CEOS File - got record seq# %d instead of "
|
|
"the expected %d.",
|
|
record->Sequence, iThisRecord);
|
|
CPLFree(record);
|
|
CPLFree(temp_body);
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
if (record->Length <= CEOS_HEADER_LENGTH)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Corrupt CEOS File - cannot read record %d.", iThisRecord);
|
|
CPLFree(record);
|
|
CPLFree(temp_body);
|
|
return CE_Failure;
|
|
}
|
|
|
|
if (record->Length > CurrentBodyLength)
|
|
{
|
|
unsigned char *temp_body_new =
|
|
(unsigned char *)VSI_REALLOC_VERBOSE(temp_body, record->Length);
|
|
if (temp_body_new == nullptr)
|
|
{
|
|
CPLFree(record);
|
|
CPLFree(temp_body);
|
|
return CE_Failure;
|
|
}
|
|
temp_body = temp_body_new;
|
|
CurrentBodyLength = record->Length;
|
|
}
|
|
|
|
int nToRead = record->Length - CEOS_HEADER_LENGTH;
|
|
if ((int)VSIFReadL(temp_body, 1, nToRead, fp) != nToRead)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Corrupt CEOS File - cannot read record %d.", iThisRecord);
|
|
CPLFree(record);
|
|
CPLFree(temp_body);
|
|
return CE_Failure;
|
|
}
|
|
|
|
InitCeosRecordWithHeader(record, temp_buffer, temp_body);
|
|
if (record->Length == 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Corrupt CEOS File - invalid record %d.", iThisRecord);
|
|
CPLFree(record);
|
|
CPLFree(temp_body);
|
|
return CE_Failure;
|
|
}
|
|
|
|
if (CurrentType == record->TypeCode.Int32Code)
|
|
record->Subsequence = ++CurrentSequence;
|
|
else
|
|
{
|
|
CurrentType = record->TypeCode.Int32Code;
|
|
record->Subsequence = 0;
|
|
CurrentSequence = 0;
|
|
}
|
|
|
|
record->FileId = fileid;
|
|
|
|
Link_t *TheLink = ceos2CreateLink(record);
|
|
|
|
if (sar->RecordList == nullptr)
|
|
sar->RecordList = TheLink;
|
|
else
|
|
sar->RecordList = InsertLink(sar->RecordList, TheLink);
|
|
|
|
start += record->Length;
|
|
|
|
if (max_records > 0)
|
|
max_records--;
|
|
if (max_bytes > 0)
|
|
{
|
|
if ((vsi_l_offset)record->Length <= max_bytes)
|
|
max_bytes -= record->Length;
|
|
else
|
|
{
|
|
CPLDebug("SAR_CEOS",
|
|
"Partial record found. %d > " CPL_FRMT_GUIB,
|
|
record->Length, max_bytes);
|
|
max_bytes = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
CPLFree(temp_body);
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRegister_SAR_CEOS() */
|
|
/************************************************************************/
|
|
|
|
void GDALRegister_SAR_CEOS()
|
|
|
|
{
|
|
if (GDALGetDriverByName("SAR_CEOS") != nullptr)
|
|
return;
|
|
|
|
GDALDriver *poDriver = new GDALDriver();
|
|
|
|
poDriver->SetDescription("SAR_CEOS");
|
|
poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
|
|
poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "CEOS SAR Image");
|
|
poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
|
|
"drivers/raster/sar_ceos.html");
|
|
poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
|
|
|
|
poDriver->pfnOpen = SAR_CEOSDataset::Open;
|
|
|
|
GetGDALDriverManager()->RegisterDriver(poDriver);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetFileList() */
|
|
/************************************************************************/
|
|
|
|
char **SAR_CEOSDataset::GetFileList()
|
|
|
|
{
|
|
char **papszFileList = GDALPamDataset::GetFileList();
|
|
|
|
papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
|
|
|
|
return papszFileList;
|
|
}
|