ultimatepp/bazaar/plugin/gdal/frmts/elas/elasdataset.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

689 lines
24 KiB
C++

/******************************************************************************
* $Id: elasdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $
*
* Project: ELAS Translator
* Purpose: Complete implementation of ELAS translator module for GDAL.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 1999, Frank Warmerdam
* Copyright (c) 2008-2011, Even Rouault <even dot rouault at mines-paris dot org>
*
* 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 "gdal_pam.h"
CPL_CVSID("$Id: elasdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $");
CPL_C_START
void GDALRegister_ELAS(void);
CPL_C_END
typedef struct {
GInt32 NBIH; /* bytes in header, normaly 1024 */
GInt32 NBPR; /* bytes per data record (all bands of scanline) */
GInt32 IL; /* initial line - normally 1 */
GInt32 LL; /* last line */
GInt32 IE; /* initial element (pixel), normally 1 */
GInt32 LE; /* last element (pixel) */
GInt32 NC; /* number of channels (bands) */
GInt32 H4321; /* header record identifier - always 4321. */
char YLabel[4]; /* Should be "NOR" for UTM */
GInt32 YOffset;/* topleft pixel center northing */
char XLabel[4]; /* Should be "EAS" for UTM */
GInt32 XOffset;/* topleft pixel center easting */
float YPixSize;/* height of pixel in georef units */
float XPixSize;/* width of pixel in georef units */
float Matrix[4]; /* 2x2 transformation matrix. Should be
1,0,0,1 for pixel/line, or
1,0,0,-1 for UTM */
GByte IH19[4];/* data type, and size flags */
GInt32 IH20; /* number of secondary headers */
char unused1[8];
GInt32 LABL; /* used by LABL module */
char HEAD; /* used by HEAD module */
char Comment1[64];
char Comment2[64];
char Comment3[64];
char Comment4[64];
char Comment5[64];
char Comment6[64];
GUInt16 ColorTable[256]; /* RGB packed with 4 bits each */
char unused2[32];
} ELASHeader;
/************************************************************************/
/* ==================================================================== */
/* ELASDataset */
/* ==================================================================== */
/************************************************************************/
class ELASRasterBand;
class ELASDataset : public GDALPamDataset
{
friend class ELASRasterBand;
VSILFILE *fp;
ELASHeader sHeader;
int bHeaderModified;
GDALDataType eRasterDataType;
int nLineOffset;
int nBandOffset; // within a line.
double adfGeoTransform[6];
public:
ELASDataset();
~ELASDataset();
virtual CPLErr GetGeoTransform( double * );
virtual CPLErr SetGeoTransform( double * );
static GDALDataset *Open( GDALOpenInfo * );
static int Identify( GDALOpenInfo * );
static GDALDataset *Create( const char * pszFilename,
int nXSize, int nYSize, int nBands,
GDALDataType eType, char ** papszParmList );
virtual void FlushCache( void );
};
/************************************************************************/
/* ==================================================================== */
/* ELASRasterBand */
/* ==================================================================== */
/************************************************************************/
class ELASRasterBand : public GDALPamRasterBand
{
friend class ELASDataset;
public:
ELASRasterBand( ELASDataset *, int );
// should override RasterIO eventually.
virtual CPLErr IReadBlock( int, int, void * );
virtual CPLErr IWriteBlock( int, int, void * );
};
/************************************************************************/
/* ELASRasterBand() */
/************************************************************************/
ELASRasterBand::ELASRasterBand( ELASDataset *poDS, int nBand )
{
this->poDS = poDS;
this->nBand = nBand;
this->eAccess = poDS->eAccess;
eDataType = poDS->eRasterDataType;
nBlockXSize = poDS->GetRasterXSize();
nBlockYSize = 1;
}
/************************************************************************/
/* IReadBlock() */
/************************************************************************/
CPLErr ELASRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff,
int nBlockYOff,
void * pImage )
{
ELASDataset *poGDS = (ELASDataset *) poDS;
CPLErr eErr = CE_None;
long nOffset;
int nDataSize;
CPLAssert( nBlockXOff == 0 );
nDataSize = GDALGetDataTypeSize(eDataType) * poGDS->GetRasterXSize() / 8;
nOffset = poGDS->nLineOffset * nBlockYOff + 1024 + (nBand-1) * nDataSize;
/* -------------------------------------------------------------------- */
/* If we can't seek to the data, we will assume this is a newly */
/* created file, and that the file hasn't been extended yet. */
/* Just read as zeros. */
/* -------------------------------------------------------------------- */
if( VSIFSeekL( poGDS->fp, nOffset, SEEK_SET ) != 0
|| VSIFReadL( pImage, 1, nDataSize, poGDS->fp ) != (size_t) nDataSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Seek or read of %d bytes at %ld failed.\n",
nDataSize, nOffset );
eErr = CE_Failure;
}
return eErr;
}
/************************************************************************/
/* IWriteBlock() */
/************************************************************************/
CPLErr ELASRasterBand::IWriteBlock( CPL_UNUSED int nBlockXOff,
int nBlockYOff,
void * pImage )
{
ELASDataset *poGDS = (ELASDataset *) poDS;
CPLErr eErr = CE_None;
long nOffset;
int nDataSize;
CPLAssert( nBlockXOff == 0 );
CPLAssert( eAccess == GA_Update );
nDataSize = GDALGetDataTypeSize(eDataType) * poGDS->GetRasterXSize() / 8;
nOffset = poGDS->nLineOffset * nBlockYOff + 1024 + (nBand-1) * nDataSize;
if( VSIFSeekL( poGDS->fp, nOffset, SEEK_SET ) != 0
|| VSIFWriteL( pImage, 1, nDataSize, poGDS->fp ) != (size_t) nDataSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Seek or write of %d bytes at %ld failed.\n",
nDataSize, nOffset );
eErr = CE_Failure;
}
return eErr;
}
/************************************************************************/
/* ==================================================================== */
/* ELASDataset */
/* ==================================================================== */
/************************************************************************/
/************************************************************************/
/* ELASDataset() */
/************************************************************************/
ELASDataset::ELASDataset()
{
fp = NULL;
adfGeoTransform[0] = 0.0;
adfGeoTransform[1] = 1.0;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = 0.0;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = 1.0;
}
/************************************************************************/
/* ~ELASDataset() */
/************************************************************************/
ELASDataset::~ELASDataset()
{
FlushCache();
if( fp != NULL )
{
VSIFCloseL( fp );
fp = NULL;
}
}
/************************************************************************/
/* FlushCache() */
/* */
/* We also write out the header, if it is modified. */
/************************************************************************/
void ELASDataset::FlushCache()
{
GDALDataset::FlushCache();
if( bHeaderModified )
{
VSIFSeekL( fp, 0, SEEK_SET );
VSIFWriteL( &sHeader, 1024, 1, fp );
bHeaderModified = FALSE;
}
}
/************************************************************************/
/* Identify() */
/************************************************************************/
int ELASDataset::Identify( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* First we check to see if the file has the expected header */
/* bytes. */
/* -------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes < 256 )
return FALSE;
if( CPL_MSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+0))) != 1024
|| CPL_MSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+28))) != 4321 )
{
return FALSE;
}
return TRUE;
}
/************************************************************************/
/* Open() */
/************************************************************************/
GDALDataset *ELASDataset::Open( GDALOpenInfo * poOpenInfo )
{
if( !Identify(poOpenInfo) )
return NULL;
/* -------------------------------------------------------------------- */
/* Create a corresponding GDALDataset. */
/* -------------------------------------------------------------------- */
ELASDataset *poDS;
const char *pszAccess;
if( poOpenInfo->eAccess == GA_Update )
pszAccess = "r+b";
else
pszAccess = "rb";
poDS = new ELASDataset();
poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, pszAccess );
if( poDS->fp == NULL )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Attempt to open `%s' with acces `%s' failed.\n",
poOpenInfo->pszFilename, pszAccess );
delete poDS;
return NULL;
}
poDS->eAccess = poOpenInfo->eAccess;
/* -------------------------------------------------------------------- */
/* Read the header information. */
/* -------------------------------------------------------------------- */
poDS->bHeaderModified = FALSE;
if( VSIFReadL( &(poDS->sHeader), 1024, 1, poDS->fp ) != 1 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Attempt to read 1024 byte header filed on file %s\n",
poOpenInfo->pszFilename );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Extract information of interest from the header. */
/* -------------------------------------------------------------------- */
int nStart, nEnd, nELASDataType, nBytesPerSample;
poDS->nLineOffset = CPL_MSBWORD32( poDS->sHeader.NBPR );
nStart = CPL_MSBWORD32( poDS->sHeader.IL );
nEnd = CPL_MSBWORD32( poDS->sHeader.LL );
poDS->nRasterYSize = nEnd - nStart + 1;
nStart = CPL_MSBWORD32( poDS->sHeader.IE );
nEnd = CPL_MSBWORD32( poDS->sHeader.LE );
poDS->nRasterXSize = nEnd - nStart + 1;
poDS->nBands = CPL_MSBWORD32( poDS->sHeader.NC );
if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
!GDALCheckBandCount(poDS->nBands, FALSE))
{
delete poDS;
return NULL;
}
nELASDataType = (poDS->sHeader.IH19[2] & 0x7e) >> 2;
nBytesPerSample = poDS->sHeader.IH19[3];
if( nELASDataType == 0 && nBytesPerSample == 1 )
poDS->eRasterDataType = GDT_Byte;
else if( nELASDataType == 1 && nBytesPerSample == 1 )
poDS->eRasterDataType = GDT_Byte;
else if( nELASDataType == 16 && nBytesPerSample == 4 )
poDS->eRasterDataType = GDT_Float32;
else if( nELASDataType == 17 && nBytesPerSample == 8 )
poDS->eRasterDataType = GDT_Float64;
else
{
delete poDS;
CPLError( CE_Failure, CPLE_AppDefined,
"Unrecognised image data type %d, with BytesPerSample=%d.\n",
nELASDataType, nBytesPerSample );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Band offsets are always multiples of 256 within a multi-band */
/* scanline of data. */
/* -------------------------------------------------------------------- */
poDS->nBandOffset =
(poDS->nRasterXSize * GDALGetDataTypeSize(poDS->eRasterDataType)/8);
if( poDS->nBandOffset % 256 != 0 )
{
poDS->nBandOffset =
poDS->nBandOffset - (poDS->nBandOffset % 256) + 256;
}
/* -------------------------------------------------------------------- */
/* Create band information objects. */
/* -------------------------------------------------------------------- */
int iBand;
for( iBand = 0; iBand < poDS->nBands; iBand++ )
{
poDS->SetBand( iBand+1, new ELASRasterBand( poDS, iBand+1 ) );
}
/* -------------------------------------------------------------------- */
/* Extract the projection coordinates, if present. */
/* -------------------------------------------------------------------- */
if( poDS->sHeader.XOffset != 0 )
{
CPL_MSBPTR32(&(poDS->sHeader.XPixSize));
CPL_MSBPTR32(&(poDS->sHeader.YPixSize));
poDS->adfGeoTransform[0] =
(GInt32) CPL_MSBWORD32(poDS->sHeader.XOffset);
poDS->adfGeoTransform[1] = poDS->sHeader.XPixSize;
poDS->adfGeoTransform[2] = 0.0;
poDS->adfGeoTransform[3] =
(GInt32) CPL_MSBWORD32(poDS->sHeader.YOffset);
poDS->adfGeoTransform[4] = 0.0;
poDS->adfGeoTransform[5] = -1.0 * ABS(poDS->sHeader.YPixSize);
CPL_MSBPTR32(&(poDS->sHeader.XPixSize));
CPL_MSBPTR32(&(poDS->sHeader.YPixSize));
poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5;
poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5;
}
else
{
poDS->adfGeoTransform[0] = 0.0;
poDS->adfGeoTransform[1] = 1.0;
poDS->adfGeoTransform[2] = 0.0;
poDS->adfGeoTransform[3] = 0.0;
poDS->adfGeoTransform[4] = 0.0;
poDS->adfGeoTransform[5] = 1.0;
}
/* -------------------------------------------------------------------- */
/* Initialize any PAM information. */
/* -------------------------------------------------------------------- */
poDS->SetDescription( poOpenInfo->pszFilename );
poDS->TryLoadXML();
/* -------------------------------------------------------------------- */
/* Check for external overviews. */
/* -------------------------------------------------------------------- */
poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() );
return( poDS );
}
/************************************************************************/
/* Create() */
/* */
/* Create a new GeoTIFF or TIFF file. */
/************************************************************************/
GDALDataset *ELASDataset::Create( const char * pszFilename,
int nXSize, int nYSize, int nBands,
GDALDataType eType,
char ** /* notdef: papszParmList */ )
{
int nBandOffset;
/* -------------------------------------------------------------------- */
/* Verify input options. */
/* -------------------------------------------------------------------- */
if (nBands <= 0)
{
CPLError( CE_Failure, CPLE_NotSupported,
"ELAS driver does not support %d bands.\n", nBands);
return NULL;
}
if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_Float64 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Attempt to create an ELAS dataset with an illegal\n"
"data type (%d).\n",
eType );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Try to create the file. */
/* -------------------------------------------------------------------- */
FILE *fp;
fp = VSIFOpen( pszFilename, "w" );
if( fp == NULL )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Attempt to create file `%s' failed.\n",
pszFilename );
return NULL;
}
/* -------------------------------------------------------------------- */
/* How long will each band of a scanline be? */
/* -------------------------------------------------------------------- */
nBandOffset = nXSize * GDALGetDataTypeSize(eType)/8;
if( nBandOffset % 256 != 0 )
{
nBandOffset = nBandOffset - (nBandOffset % 256) + 256;
}
/* -------------------------------------------------------------------- */
/* Setup header data block. */
/* */
/* Note that CPL_MSBWORD32() will swap little endian words to */
/* big endian on little endian platforms. */
/* -------------------------------------------------------------------- */
ELASHeader sHeader;
memset( &sHeader, 0, 1024 );
sHeader.NBIH = CPL_MSBWORD32(1024);
sHeader.NBPR = CPL_MSBWORD32(nBands * nBandOffset);
sHeader.IL = CPL_MSBWORD32(1);
sHeader.LL = CPL_MSBWORD32(nYSize);
sHeader.IE = CPL_MSBWORD32(1);
sHeader.LE = CPL_MSBWORD32(nXSize);
sHeader.NC = CPL_MSBWORD32(nBands);
sHeader.H4321 = CPL_MSBWORD32(4321);
sHeader.IH19[0] = 0x04;
sHeader.IH19[1] = 0xd2;
sHeader.IH19[3] = (GByte) (GDALGetDataTypeSize(eType) / 8);
if( eType == GDT_Byte )
sHeader.IH19[2] = 1 << 2;
else if( eType == GDT_Float32 )
sHeader.IH19[2] = 16 << 2;
else if( eType == GDT_Float64 )
sHeader.IH19[2] = 17 << 2;
/* -------------------------------------------------------------------- */
/* Write the header data. */
/* -------------------------------------------------------------------- */
VSIFWrite( &sHeader, 1024, 1, fp );
/* -------------------------------------------------------------------- */
/* Now write out zero data for all the imagery. This is */
/* inefficient, but simplies the IReadBlock() / IWriteBlock() logic.*/
/* -------------------------------------------------------------------- */
GByte *pabyLine;
pabyLine = (GByte *) CPLCalloc(nBandOffset,nBands);
for( int iLine = 0; iLine < nYSize; iLine++ )
{
if( VSIFWrite( pabyLine, 1, nBandOffset, fp ) != (size_t) nBandOffset )
{
CPLError( CE_Failure, CPLE_FileIO,
"Error writing ELAS image data ... likely insufficient"
" disk space.\n" );
VSIFClose( fp );
CPLFree( pabyLine );
return NULL;
}
}
CPLFree( pabyLine );
VSIFClose( fp );
/* -------------------------------------------------------------------- */
/* Try to return a regular handle on the file. */
/* -------------------------------------------------------------------- */
return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
}
/************************************************************************/
/* GetGeoTransform() */
/************************************************************************/
CPLErr ELASDataset::GetGeoTransform( double * padfTransform )
{
memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
return( CE_None );
}
/************************************************************************/
/* SetGeoTransform() */
/************************************************************************/
CPLErr ELASDataset::SetGeoTransform( double * padfTransform )
{
/* -------------------------------------------------------------------- */
/* I don't think it supports rotation, but perhaps it is possible */
/* for us to use the 2x2 transform matrix to accomplish some */
/* sort of rotation. */
/* -------------------------------------------------------------------- */
if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Attempt to set rotated geotransform on ELAS file.\n"
"ELAS does not support rotation.\n" );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Remember the new transform, and update the header. */
/* -------------------------------------------------------------------- */
int nXOff, nYOff;
memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
bHeaderModified = TRUE;
nXOff = (int) (adfGeoTransform[0] + adfGeoTransform[1]*0.5);
nYOff = (int) (adfGeoTransform[3] + adfGeoTransform[5]*0.5);
sHeader.XOffset = CPL_MSBWORD32(nXOff);
sHeader.YOffset = CPL_MSBWORD32(nYOff);
sHeader.XPixSize = (float) ABS(adfGeoTransform[1]);
sHeader.YPixSize = (float) ABS(adfGeoTransform[5]);
CPL_MSBPTR32(&(sHeader.XPixSize));
CPL_MSBPTR32(&(sHeader.YPixSize));
strncpy( sHeader.YLabel, "NOR ", 4 );
strncpy( sHeader.XLabel, "EAS ", 4 );
sHeader.Matrix[0] = 1.0;
sHeader.Matrix[1] = 0.0;
sHeader.Matrix[2] = 0.0;
sHeader.Matrix[3] = -1.0;
CPL_MSBPTR32(&(sHeader.Matrix[0]));
CPL_MSBPTR32(&(sHeader.Matrix[1]));
CPL_MSBPTR32(&(sHeader.Matrix[2]));
CPL_MSBPTR32(&(sHeader.Matrix[3]));
return( CE_None );
}
/************************************************************************/
/* GDALRegister_ELAS() */
/************************************************************************/
void GDALRegister_ELAS()
{
GDALDriver *poDriver;
if( GDALGetDriverByName( "ELAS" ) == NULL )
{
poDriver = new GDALDriver();
poDriver->SetDescription( "ELAS" );
poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
"ELAS" );
poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
"Byte Float32 Float64" );
poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
poDriver->pfnOpen = ELASDataset::Open;
poDriver->pfnIdentify = ELASDataset::Identify;
poDriver->pfnCreate = ELASDataset::Create;
GetGDALDriverManager()->RegisterDriver( poDriver );
}
}