ultimatepp/bazaar/plugin/gdal/frmts/aaigrid/aaigriddataset.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

1353 lines
47 KiB
C++

/******************************************************************************
* $Id: aaigriddataset.cpp 28785 2015-03-26 20:46:45Z goatbar $
*
* Project: GDAL
* Purpose: Implements Arc/Info ASCII Grid Format.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2001, Frank Warmerdam (warmerdam@pobox.com)
* Copyright (c) 2007-2012, Even Rouault <even dot rouault at mines-paris dot org>
* Copyright (c) 2014, Kyle Shannon <kyle at pobox dot 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 "gdal_pam.h"
#include <ctype.h>
#include <limits.h>
#include "cpl_string.h"
#include "ogr_spatialref.h"
CPL_CVSID("$Id: aaigriddataset.cpp 28785 2015-03-26 20:46:45Z goatbar $");
CPL_C_START
void GDALRegister_AAIGrid(void);
void GDALRegister_GRASSASCIIGrid(void);
CPL_C_END
static CPLString OSR_GDS( char **papszNV, const char * pszField,
const char *pszDefaultValue );
typedef enum
{
FORMAT_AAIG,
FORMAT_GRASSASCII
} GridFormat;
/************************************************************************/
/* ==================================================================== */
/* AAIGDataset */
/* ==================================================================== */
/************************************************************************/
class AAIGRasterBand;
class CPL_DLL AAIGDataset : public GDALPamDataset
{
friend class AAIGRasterBand;
VSILFILE *fp;
char **papszPrj;
CPLString osPrjFilename;
char *pszProjection;
unsigned char achReadBuf[256];
GUIntBig nBufferOffset;
int nOffsetInBuffer;
char Getc();
GUIntBig Tell();
int Seek( GUIntBig nOffset );
protected:
GDALDataType eDataType;
double adfGeoTransform[6];
int bNoDataSet;
double dfNoDataValue;
virtual int ParseHeader(const char* pszHeader, const char* pszDataType);
public:
AAIGDataset();
~AAIGDataset();
virtual char **GetFileList(void);
static GDALDataset *CommonOpen( GDALOpenInfo * poOpenInfo,
GridFormat eFormat );
static GDALDataset *Open( GDALOpenInfo * );
static int Identify( GDALOpenInfo * );
static CPLErr Delete( const char *pszFilename );
static CPLErr Remove( const char *pszFilename, int bRepError );
static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
int bStrict, char ** papszOptions,
GDALProgressFunc pfnProgress, void * pProgressData );
virtual CPLErr GetGeoTransform( double * );
virtual const char *GetProjectionRef(void);
};
/************************************************************************/
/* ==================================================================== */
/* GRASSASCIIDataset */
/* ==================================================================== */
/************************************************************************/
class GRASSASCIIDataset : public AAIGDataset
{
virtual int ParseHeader(const char* pszHeader, const char* pszDataType);
public:
GRASSASCIIDataset() : AAIGDataset() {}
static GDALDataset *Open( GDALOpenInfo * );
static int Identify( GDALOpenInfo * );
};
/************************************************************************/
/* ==================================================================== */
/* AAIGRasterBand */
/* ==================================================================== */
/************************************************************************/
class AAIGRasterBand : public GDALPamRasterBand
{
friend class AAIGDataset;
GUIntBig *panLineOffset;
public:
AAIGRasterBand( AAIGDataset *, int );
virtual ~AAIGRasterBand();
virtual double GetNoDataValue( int * );
virtual CPLErr SetNoDataValue( double );
virtual CPLErr IReadBlock( int, int, void * );
};
/************************************************************************/
/* AAIGRasterBand() */
/************************************************************************/
AAIGRasterBand::AAIGRasterBand( AAIGDataset *poDS, int nDataStart )
{
this->poDS = poDS;
nBand = 1;
eDataType = poDS->eDataType;
nBlockXSize = poDS->nRasterXSize;
nBlockYSize = 1;
panLineOffset =
(GUIntBig *) VSICalloc( poDS->nRasterYSize, sizeof(GUIntBig) );
if (panLineOffset == NULL)
{
CPLError(CE_Failure, CPLE_OutOfMemory,
"AAIGRasterBand::AAIGRasterBand : Out of memory (nRasterYSize = %d)",
poDS->nRasterYSize);
return;
}
panLineOffset[0] = nDataStart;
}
/************************************************************************/
/* ~AAIGRasterBand() */
/************************************************************************/
AAIGRasterBand::~AAIGRasterBand()
{
CPLFree( panLineOffset );
}
/************************************************************************/
/* IReadBlock() */
/************************************************************************/
CPLErr AAIGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
void * pImage )
{
AAIGDataset *poODS = (AAIGDataset *) poDS;
int iPixel;
if( nBlockYOff < 0 || nBlockYOff > poODS->nRasterYSize - 1
|| nBlockXOff != 0 || panLineOffset == NULL || poODS->fp == NULL )
return CE_Failure;
if( panLineOffset[nBlockYOff] == 0 )
{
int iPrevLine;
for( iPrevLine = 1; iPrevLine <= nBlockYOff; iPrevLine++ )
if( panLineOffset[iPrevLine] == 0 )
IReadBlock( nBlockXOff, iPrevLine-1, NULL );
}
if( panLineOffset[nBlockYOff] == 0 )
return CE_Failure;
if( poODS->Seek( panLineOffset[nBlockYOff] ) != 0 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Can't seek to offset %lu in input file to read data.",
(long unsigned int)panLineOffset[nBlockYOff] );
return CE_Failure;
}
for( iPixel = 0; iPixel < poODS->nRasterXSize; )
{
char szToken[500];
char chNext;
int iTokenChar = 0;
/* suck up any pre-white space. */
do {
chNext = poODS->Getc();
} while( isspace( (unsigned char)chNext ) );
while( chNext != '\0' && !isspace((unsigned char)chNext) )
{
if( iTokenChar == sizeof(szToken)-2 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Token too long at scanline %d.",
nBlockYOff );
return CE_Failure;
}
szToken[iTokenChar++] = chNext;
chNext = poODS->Getc();
}
if( chNext == '\0' &&
(iPixel != poODS->nRasterXSize - 1 ||
nBlockYOff != poODS->nRasterYSize - 1) )
{
CPLError( CE_Failure, CPLE_FileIO,
"File short, can't read line %d.",
nBlockYOff );
return CE_Failure;
}
szToken[iTokenChar] = '\0';
if( pImage != NULL )
{
if( eDataType == GDT_Float64 )
((double *) pImage)[iPixel] = CPLAtofM(szToken);
else if( eDataType == GDT_Float32 )
((float *) pImage)[iPixel] = (float) CPLAtofM(szToken);
else
((GInt32 *) pImage)[iPixel] = (GInt32) atoi(szToken);
}
iPixel++;
}
if( nBlockYOff < poODS->nRasterYSize - 1 )
panLineOffset[nBlockYOff + 1] = poODS->Tell();
return CE_None;
}
/************************************************************************/
/* GetNoDataValue() */
/************************************************************************/
double AAIGRasterBand::GetNoDataValue( int * pbSuccess )
{
AAIGDataset *poODS = (AAIGDataset *) poDS;
if( pbSuccess )
*pbSuccess = poODS->bNoDataSet;
return poODS->dfNoDataValue;
}
/************************************************************************/
/* SetNoDataValue() */
/************************************************************************/
CPLErr AAIGRasterBand::SetNoDataValue( double dfNoData )
{
AAIGDataset *poODS = (AAIGDataset *) poDS;
poODS->bNoDataSet = TRUE;
poODS->dfNoDataValue = dfNoData;
return CE_None;
}
/************************************************************************/
/* ==================================================================== */
/* AAIGDataset */
/* ==================================================================== */
/************************************************************************/
/************************************************************************/
/* AAIGDataset() */
/************************************************************************/
AAIGDataset::AAIGDataset()
{
papszPrj = NULL;
pszProjection = CPLStrdup("");
fp = NULL;
eDataType = GDT_Int32;
bNoDataSet = FALSE;
dfNoDataValue = -9999.0;
adfGeoTransform[0] = 0.0;
adfGeoTransform[1] = 1.0;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = 0.0;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = 1.0;
nOffsetInBuffer = 256;
nBufferOffset = 0;
}
/************************************************************************/
/* ~AAIGDataset() */
/************************************************************************/
AAIGDataset::~AAIGDataset()
{
FlushCache();
if( fp != NULL )
VSIFCloseL( fp );
CPLFree( pszProjection );
CSLDestroy( papszPrj );
}
/************************************************************************/
/* Tell() */
/************************************************************************/
GUIntBig AAIGDataset::Tell()
{
return nBufferOffset + nOffsetInBuffer;
}
/************************************************************************/
/* Seek() */
/************************************************************************/
int AAIGDataset::Seek( GUIntBig nNewOffset )
{
nOffsetInBuffer = sizeof(achReadBuf);
return VSIFSeekL( fp, nNewOffset, SEEK_SET );
}
/************************************************************************/
/* Getc() */
/* */
/* Read a single character from the input file (efficiently we */
/* hope). */
/************************************************************************/
char AAIGDataset::Getc()
{
if( nOffsetInBuffer < (int) sizeof(achReadBuf) )
return achReadBuf[nOffsetInBuffer++];
nBufferOffset = VSIFTellL( fp );
int nRead = VSIFReadL( achReadBuf, 1, sizeof(achReadBuf), fp );
unsigned int i;
for(i=nRead;i<sizeof(achReadBuf);i++)
achReadBuf[i] = '\0';
nOffsetInBuffer = 0;
return achReadBuf[nOffsetInBuffer++];
}
/************************************************************************/
/* GetFileList() */
/************************************************************************/
char **AAIGDataset::GetFileList()
{
char **papszFileList = GDALPamDataset::GetFileList();
if( papszPrj != NULL )
papszFileList = CSLAddString( papszFileList, osPrjFilename );
return papszFileList;
}
/************************************************************************/
/* Identify() */
/************************************************************************/
int AAIGDataset::Identify( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* Does this look like an AI grid file? */
/* -------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes < 40
|| !( EQUALN((const char *) poOpenInfo->pabyHeader,"ncols",5) ||
EQUALN((const char *) poOpenInfo->pabyHeader,"nrows",5) ||
EQUALN((const char *) poOpenInfo->pabyHeader,"xllcorner",9)||
EQUALN((const char *) poOpenInfo->pabyHeader,"yllcorner",9)||
EQUALN((const char *) poOpenInfo->pabyHeader,"xllcenter",9)||
EQUALN((const char *) poOpenInfo->pabyHeader,"yllcenter",9)||
EQUALN((const char *) poOpenInfo->pabyHeader,"dx",2)||
EQUALN((const char *) poOpenInfo->pabyHeader,"dy",2)||
EQUALN((const char *) poOpenInfo->pabyHeader,"cellsize",8)) )
return FALSE;
return TRUE;
}
/************************************************************************/
/* Identify() */
/************************************************************************/
int GRASSASCIIDataset::Identify( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* Does this look like a GRASS ASCII grid file? */
/* -------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes < 40
|| !( EQUALN((const char *) poOpenInfo->pabyHeader,"north:",6) ||
EQUALN((const char *) poOpenInfo->pabyHeader,"south:",6) ||
EQUALN((const char *) poOpenInfo->pabyHeader,"east:",5)||
EQUALN((const char *) poOpenInfo->pabyHeader,"west:",5)||
EQUALN((const char *) poOpenInfo->pabyHeader,"rows:",5)||
EQUALN((const char *) poOpenInfo->pabyHeader,"cols:",5) ) )
return FALSE;
return TRUE;
}
/************************************************************************/
/* Open() */
/************************************************************************/
GDALDataset *AAIGDataset::Open( GDALOpenInfo * poOpenInfo )
{
if (!Identify(poOpenInfo))
return NULL;
return CommonOpen(poOpenInfo, FORMAT_AAIG);
}
/************************************************************************/
/* ParseHeader() */
/************************************************************************/
int AAIGDataset::ParseHeader(const char* pszHeader, const char* pszDataType)
{
int i, j;
char** papszTokens =
CSLTokenizeString2( pszHeader, " \n\r\t" , 0 );
int nTokens = CSLCount(papszTokens);
double dfCellDX = 0;
double dfCellDY = 0;
if ( (i = CSLFindString( papszTokens, "ncols" )) < 0 ||
i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
nRasterXSize = atoi(papszTokens[i + 1]);
if ( (i = CSLFindString( papszTokens, "nrows" )) < 0 ||
i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
nRasterYSize = atoi(papszTokens[i + 1]);
if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
{
CSLDestroy( papszTokens );
return FALSE;
}
if ( (i = CSLFindString( papszTokens, "cellsize" )) < 0 )
{
int iDX, iDY;
if( (iDX = CSLFindString(papszTokens,"dx")) < 0
|| (iDY = CSLFindString(papszTokens,"dy")) < 0
|| iDX+1 >= nTokens
|| iDY+1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
dfCellDX = CPLAtofM( papszTokens[iDX+1] );
dfCellDY = CPLAtofM( papszTokens[iDY+1] );
}
else
{
if (i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
dfCellDX = dfCellDY = CPLAtofM( papszTokens[i + 1] );
}
if ((i = CSLFindString( papszTokens, "xllcorner" )) >= 0 &&
(j = CSLFindString( papszTokens, "yllcorner" )) >= 0 &&
i + 1 < nTokens && j + 1 < nTokens)
{
adfGeoTransform[0] = CPLAtofM( papszTokens[i + 1] );
/* Small hack to compensate from insufficient precision in cellsize */
/* parameter in datasets of http://ccafs-climate.org/data/A2a_2020s/hccpr_hadcm3 */
if ((nRasterXSize % 360) == 0 &&
fabs(adfGeoTransform[0] - (-180.0)) < 1e-12 &&
dfCellDX == dfCellDY &&
fabs(dfCellDX - (360.0 / nRasterXSize)) < 1e-9)
{
dfCellDX = dfCellDY = 360.0 / nRasterXSize;
}
adfGeoTransform[1] = dfCellDX;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = CPLAtofM( papszTokens[j + 1] )
+ nRasterYSize * dfCellDY;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = - dfCellDY;
}
else if ((i = CSLFindString( papszTokens, "xllcenter" )) >= 0 &&
(j = CSLFindString( papszTokens, "yllcenter" )) >= 0 &&
i + 1 < nTokens && j + 1 < nTokens)
{
SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT );
adfGeoTransform[0] = CPLAtofM(papszTokens[i + 1]) - 0.5 * dfCellDX;
adfGeoTransform[1] = dfCellDX;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = CPLAtofM( papszTokens[j + 1] )
- 0.5 * dfCellDY
+ nRasterYSize * dfCellDY;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = - dfCellDY;
}
else
{
adfGeoTransform[0] = 0.0;
adfGeoTransform[1] = dfCellDX;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = 0.0;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = - dfCellDY;
}
if( (i = CSLFindString( papszTokens, "NODATA_value" )) >= 0 &&
i + 1 < nTokens)
{
const char* pszNoData = papszTokens[i + 1];
bNoDataSet = TRUE;
dfNoDataValue = CPLAtofM(pszNoData);
if( pszDataType == NULL &&
(strchr( pszNoData, '.' ) != NULL ||
strchr( pszNoData, ',' ) != NULL ||
INT_MIN > dfNoDataValue || dfNoDataValue > INT_MAX) )
{
eDataType = GDT_Float32;
}
if( eDataType == GDT_Float32 )
{
dfNoDataValue = (double) (float) dfNoDataValue;
}
}
CSLDestroy( papszTokens );
return TRUE;
}
/************************************************************************/
/* Open() */
/************************************************************************/
GDALDataset *GRASSASCIIDataset::Open( GDALOpenInfo * poOpenInfo )
{
if (!Identify(poOpenInfo))
return NULL;
return CommonOpen(poOpenInfo, FORMAT_GRASSASCII);
}
/************************************************************************/
/* ParseHeader() */
/************************************************************************/
int GRASSASCIIDataset::ParseHeader(const char* pszHeader, const char* pszDataType)
{
int i;
char** papszTokens =
CSLTokenizeString2( pszHeader, " \n\r\t:" , 0 );
int nTokens = CSLCount(papszTokens);
if ( (i = CSLFindString( papszTokens, "cols" )) < 0 ||
i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
nRasterXSize = atoi(papszTokens[i + 1]);
if ( (i = CSLFindString( papszTokens, "rows" )) < 0 ||
i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
nRasterYSize = atoi(papszTokens[i + 1]);
if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
{
CSLDestroy( papszTokens );
return FALSE;
}
int iNorth = CSLFindString( papszTokens, "north" );
int iSouth = CSLFindString( papszTokens, "south" );
int iEast = CSLFindString( papszTokens, "east" );
int iWest = CSLFindString( papszTokens, "west" );
if (iNorth == -1 || iSouth == -1 || iEast == -1 || iWest == -1 ||
MAX(MAX(iNorth, iSouth), MAX(iEast, iWest)) + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
double dfNorth = CPLAtofM( papszTokens[iNorth + 1] );
double dfSouth = CPLAtofM( papszTokens[iSouth + 1] );
double dfEast = CPLAtofM( papszTokens[iEast + 1] );
double dfWest = CPLAtofM( papszTokens[iWest + 1] );
double dfPixelXSize = (dfEast - dfWest) / nRasterXSize;
double dfPixelYSize = (dfNorth - dfSouth) / nRasterYSize;
adfGeoTransform[0] = dfWest;
adfGeoTransform[1] = dfPixelXSize;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = dfNorth;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = - dfPixelYSize;
if( (i = CSLFindString( papszTokens, "null" )) >= 0 &&
i + 1 < nTokens)
{
const char* pszNoData = papszTokens[i + 1];
bNoDataSet = TRUE;
dfNoDataValue = CPLAtofM(pszNoData);
if( pszDataType == NULL &&
(strchr( pszNoData, '.' ) != NULL ||
strchr( pszNoData, ',' ) != NULL ||
INT_MIN > dfNoDataValue || dfNoDataValue > INT_MAX) )
{
eDataType = GDT_Float32;
}
if( eDataType == GDT_Float32 )
{
dfNoDataValue = (double) (float) dfNoDataValue;
}
}
if( (i = CSLFindString( papszTokens, "type" )) >= 0 &&
i + 1 < nTokens)
{
const char* pszType = papszTokens[i + 1];
if (EQUAL(pszType, "int"))
eDataType = GDT_Int32;
else if (EQUAL(pszType, "float"))
eDataType = GDT_Float32;
else if (EQUAL(pszType, "double"))
eDataType = GDT_Float64;
else
{
CPLError(CE_Warning, CPLE_AppDefined,
"Invalid value for type parameter : %s", pszType);
}
}
CSLDestroy(papszTokens);
return TRUE;
}
/************************************************************************/
/* CommonOpen() */
/************************************************************************/
GDALDataset *AAIGDataset::CommonOpen( GDALOpenInfo * poOpenInfo,
GridFormat eFormat )
{
int i = 0;
/* -------------------------------------------------------------------- */
/* Create a corresponding GDALDataset. */
/* -------------------------------------------------------------------- */
AAIGDataset *poDS;
if (eFormat == FORMAT_AAIG)
poDS = new AAIGDataset();
else
poDS = new GRASSASCIIDataset();
const char* pszDataTypeOption = (eFormat == FORMAT_AAIG) ? "AAIGRID_DATATYPE":
"GRASSASCIIGRID_DATATYPE";
const char* pszDataType = CPLGetConfigOption(pszDataTypeOption, NULL);
if( pszDataType == NULL )
pszDataType = CSLFetchNameValue( poOpenInfo->papszOpenOptions, "DATATYPE" );
if (pszDataType != NULL)
{
poDS->eDataType = GDALGetDataTypeByName(pszDataType);
if (!(poDS->eDataType == GDT_Int32 || poDS->eDataType == GDT_Float32 ||
poDS->eDataType == GDT_Float64))
{
CPLError(CE_Warning, CPLE_NotSupported,
"Unsupported value for %s : %s", pszDataTypeOption, pszDataType);
poDS->eDataType = GDT_Int32;
pszDataType = NULL;
}
}
/* -------------------------------------------------------------------- */
/* Parse the header. */
/* -------------------------------------------------------------------- */
if (!poDS->ParseHeader((const char *) poOpenInfo->pabyHeader, pszDataType))
{
delete poDS;
return NULL;
}
/* -------------------------------------------------------------------- */
/* Open file with large file API. */
/* -------------------------------------------------------------------- */
poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
if( poDS->fp == NULL )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"VSIFOpenL(%s) failed unexpectedly.",
poOpenInfo->pszFilename );
delete poDS;
return NULL;
}
/* -------------------------------------------------------------------- */
/* Find the start of real data. */
/* -------------------------------------------------------------------- */
int nStartOfData;
for( i = 2; TRUE ; i++ )
{
if( poOpenInfo->pabyHeader[i] == '\0' )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Couldn't find data values in ASCII Grid file.\n" );
delete poDS;
return NULL;
}
if( poOpenInfo->pabyHeader[i-1] == '\n'
|| poOpenInfo->pabyHeader[i-2] == '\n'
|| poOpenInfo->pabyHeader[i-1] == '\r'
|| poOpenInfo->pabyHeader[i-2] == '\r' )
{
if( !isalpha(poOpenInfo->pabyHeader[i])
&& poOpenInfo->pabyHeader[i] != '\n'
&& poOpenInfo->pabyHeader[i] != '\r')
{
nStartOfData = i;
/* Beginning of real data found. */
break;
}
}
}
/* -------------------------------------------------------------------- */
/* Recognize the type of data. */
/* -------------------------------------------------------------------- */
CPLAssert( NULL != poDS->fp );
if( pszDataType == NULL && poDS->eDataType != GDT_Float32)
{
/* Allocate 100K chunk + 1 extra byte for NULL character. */
const size_t nChunkSize = 1024 * 100;
GByte* pabyChunk = (GByte *) VSICalloc( nChunkSize + 1, sizeof(GByte) );
if (pabyChunk == NULL)
{
CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
delete poDS;
return NULL;
}
pabyChunk[nChunkSize] = '\0';
VSIFSeekL( poDS->fp, nStartOfData, SEEK_SET );
/* Scan for dot in subsequent chunks of data. */
while( !VSIFEofL( poDS->fp) )
{
VSIFReadL( pabyChunk, sizeof(GByte), nChunkSize, poDS->fp );
CPLAssert( pabyChunk[nChunkSize] == '\0' );
for(i = 0; i < (int)nChunkSize; i++)
{
GByte ch = pabyChunk[i];
if (ch == '.' || ch == ',' || ch == 'e' || ch == 'E')
{
poDS->eDataType = GDT_Float32;
break;
}
}
}
/* Deallocate chunk. */
VSIFree( pabyChunk );
}
/* -------------------------------------------------------------------- */
/* Create band information objects. */
/* -------------------------------------------------------------------- */
AAIGRasterBand* band = new AAIGRasterBand( poDS, nStartOfData );
poDS->SetBand( 1, band );
if (band->panLineOffset == NULL)
{
delete poDS;
return NULL;
}
/* -------------------------------------------------------------------- */
/* Try to read projection file. */
/* -------------------------------------------------------------------- */
char *pszDirname, *pszBasename;
VSIStatBufL sStatBuf;
pszDirname = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename));
pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename));
poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "prj" );
int nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf );
if( nRet != 0 && VSIIsCaseSensitiveFS(poDS->osPrjFilename) )
{
poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "PRJ" );
nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf );
}
if( nRet == 0 )
{
OGRSpatialReference oSRS;
poDS->papszPrj = CSLLoad( poDS->osPrjFilename );
CPLDebug( "AAIGrid", "Loaded SRS from %s",
poDS->osPrjFilename.c_str() );
if( oSRS.importFromESRI( poDS->papszPrj ) == OGRERR_NONE )
{
// If geographic values are in seconds, we must transform.
// Is there a code for minutes too?
if( oSRS.IsGeographic()
&& EQUAL(OSR_GDS( poDS->papszPrj, "Units", ""), "DS") )
{
poDS->adfGeoTransform[0] /= 3600.0;
poDS->adfGeoTransform[1] /= 3600.0;
poDS->adfGeoTransform[2] /= 3600.0;
poDS->adfGeoTransform[3] /= 3600.0;
poDS->adfGeoTransform[4] /= 3600.0;
poDS->adfGeoTransform[5] /= 3600.0;
}
CPLFree( poDS->pszProjection );
oSRS.exportToWkt( &(poDS->pszProjection) );
}
}
CPLFree( pszDirname );
CPLFree( pszBasename );
/* -------------------------------------------------------------------- */
/* Initialize any PAM information. */
/* -------------------------------------------------------------------- */
poDS->SetDescription( poOpenInfo->pszFilename );
poDS->TryLoadXML();
/* -------------------------------------------------------------------- */
/* Check for external overviews. */
/* -------------------------------------------------------------------- */
poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() );
return( poDS );
}
/************************************************************************/
/* GetGeoTransform() */
/************************************************************************/
CPLErr AAIGDataset::GetGeoTransform( double * padfTransform )
{
memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
return( CE_None );
}
/************************************************************************/
/* GetProjectionRef() */
/************************************************************************/
const char *AAIGDataset::GetProjectionRef()
{
return pszProjection;
}
/************************************************************************/
/* CreateCopy() */
/************************************************************************/
GDALDataset * AAIGDataset::CreateCopy(
const char * pszFilename, GDALDataset *poSrcDS,
CPL_UNUSED int bStrict,
char ** papszOptions,
GDALProgressFunc pfnProgress, void * pProgressData )
{
int nBands = poSrcDS->GetRasterCount();
int nXSize = poSrcDS->GetRasterXSize();
int nYSize = poSrcDS->GetRasterYSize();
/* -------------------------------------------------------------------- */
/* Some rudimentary checks */
/* -------------------------------------------------------------------- */
if( nBands != 1 )
{
CPLError( CE_Failure, CPLE_NotSupported,
"AAIG driver doesn't support %d bands. Must be 1 band.\n",
nBands );
return NULL;
}
if( !pfnProgress( 0.0, NULL, pProgressData ) )
return NULL;
/* -------------------------------------------------------------------- */
/* Create the dataset. */
/* -------------------------------------------------------------------- */
VSILFILE *fpImage;
fpImage = VSIFOpenL( pszFilename, "wt" );
if( fpImage == NULL )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Unable to create file %s.\n",
pszFilename );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Write ASCII Grid file header */
/* -------------------------------------------------------------------- */
double adfGeoTransform[6];
char szHeader[2000];
const char *pszForceCellsize =
CSLFetchNameValue( papszOptions, "FORCE_CELLSIZE" );
poSrcDS->GetGeoTransform( adfGeoTransform );
if( ABS(adfGeoTransform[1]+adfGeoTransform[5]) < 0.0000001
|| ABS(adfGeoTransform[1]-adfGeoTransform[5]) < 0.0000001
|| (pszForceCellsize && CSLTestBoolean(pszForceCellsize)) )
CPLsprintf( szHeader,
"ncols %d\n"
"nrows %d\n"
"xllcorner %.12f\n"
"yllcorner %.12f\n"
"cellsize %.12f\n",
nXSize, nYSize,
adfGeoTransform[0],
adfGeoTransform[3] - nYSize * adfGeoTransform[1],
adfGeoTransform[1] );
else
{
if( pszForceCellsize == NULL )
CPLError( CE_Warning, CPLE_AppDefined,
"Producing a Golden Surfer style file with DX and DY instead\n"
"of CELLSIZE since the input pixels are non-square. Use the\n"
"FORCE_CELLSIZE=TRUE creation option to force use of DX for\n"
"even though this will be distorted. Most ASCII Grid readers\n"
"(ArcGIS included) do not support the DX and DY parameters.\n" );
CPLsprintf( szHeader,
"ncols %d\n"
"nrows %d\n"
"xllcorner %.12f\n"
"yllcorner %.12f\n"
"dx %.12f\n"
"dy %.12f\n",
nXSize, nYSize,
adfGeoTransform[0],
adfGeoTransform[3] + nYSize * adfGeoTransform[5],
adfGeoTransform[1],
fabs(adfGeoTransform[5]) );
}
/* -------------------------------------------------------------------- */
/* Builds the format string used for printing float values. */
/* -------------------------------------------------------------------- */
char szFormatFloat[32];
strcpy(szFormatFloat, " %.20g");
const char *pszDecimalPrecision =
CSLFetchNameValue( papszOptions, "DECIMAL_PRECISION" );
const char *pszSignificantDigits =
CSLFetchNameValue( papszOptions, "SIGNIFICANT_DIGITS" );
int bIgnoreSigDigits = FALSE;
if( pszDecimalPrecision && pszSignificantDigits )
{
CPLError( CE_Warning, CPLE_AppDefined,
"Conflicting precision arguments, using DECIMAL_PRECISION" );
bIgnoreSigDigits = TRUE;
}
int nPrecision;
if ( pszSignificantDigits && !bIgnoreSigDigits )
{
nPrecision = atoi( pszSignificantDigits );
if (nPrecision >= 0)
sprintf( szFormatFloat, " %%.%dg", nPrecision );
CPLDebug( "AAIGrid", "Setting precision format: %s", szFormatFloat );
}
else if( pszDecimalPrecision )
{
nPrecision = atoi( pszDecimalPrecision );
if ( nPrecision >= 0 )
sprintf( szFormatFloat, " %%.%df", nPrecision );
CPLDebug( "AAIGrid", "Setting precision format: %s", szFormatFloat );
}
/* -------------------------------------------------------------------- */
/* Handle nodata (optionally). */
/* -------------------------------------------------------------------- */
GDALRasterBand * poBand = poSrcDS->GetRasterBand( 1 );
double dfNoData;
int bSuccess;
int bReadAsInt;
bReadAsInt = ( poBand->GetRasterDataType() == GDT_Byte
|| poBand->GetRasterDataType() == GDT_Int16
|| poBand->GetRasterDataType() == GDT_UInt16
|| poBand->GetRasterDataType() == GDT_Int32 );
// Write `nodata' value to header if it is exists in source dataset
dfNoData = poBand->GetNoDataValue( &bSuccess );
if ( bSuccess )
{
sprintf( szHeader+strlen( szHeader ), "NODATA_value " );
if( bReadAsInt )
sprintf( szHeader+strlen( szHeader ), "%d", (int)dfNoData );
else
CPLsprintf( szHeader+strlen( szHeader ), szFormatFloat, dfNoData );
sprintf( szHeader+strlen( szHeader ), "\n" );
}
VSIFWriteL( szHeader, 1, strlen(szHeader), fpImage );
/* -------------------------------------------------------------------- */
/* Loop over image, copying image data. */
/* -------------------------------------------------------------------- */
int *panScanline = NULL;
double *padfScanline = NULL;
int iLine, iPixel;
CPLErr eErr = CE_None;
// Write scanlines to output file
if (bReadAsInt)
panScanline = (int *) CPLMalloc( nXSize *
GDALGetDataTypeSize(GDT_Int32) / 8 );
else
padfScanline = (double *) CPLMalloc( nXSize *
GDALGetDataTypeSize(GDT_Float64) / 8 );
for( iLine = 0; eErr == CE_None && iLine < nYSize; iLine++ )
{
CPLString osBuf;
eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
(bReadAsInt) ? (void*)panScanline : (void*)padfScanline,
nXSize, 1, (bReadAsInt) ? GDT_Int32 : GDT_Float64,
0, 0, NULL );
if( bReadAsInt )
{
for ( iPixel = 0; iPixel < nXSize; iPixel++ )
{
sprintf( szHeader, " %d", panScanline[iPixel] );
osBuf += szHeader;
if( (iPixel & 1023) == 0 || iPixel == nXSize - 1 )
{
if ( VSIFWriteL( osBuf, (int)osBuf.size(), 1, fpImage ) != 1 )
{
eErr = CE_Failure;
CPLError( CE_Failure, CPLE_AppDefined,
"Write failed, disk full?\n" );
break;
}
osBuf = "";
}
}
}
else
{
for ( iPixel = 0; iPixel < nXSize; iPixel++ )
{
CPLsprintf( szHeader, szFormatFloat, padfScanline[iPixel] );
osBuf += szHeader;
if( (iPixel & 1023) == 0 || iPixel == nXSize - 1 )
{
if ( VSIFWriteL( osBuf, (int)osBuf.size(), 1, fpImage ) != 1 )
{
eErr = CE_Failure;
CPLError( CE_Failure, CPLE_AppDefined,
"Write failed, disk full?\n" );
break;
}
osBuf = "";
}
}
}
VSIFWriteL( (void *) "\n", 1, 1, fpImage );
if( eErr == CE_None &&
!pfnProgress((iLine + 1) / ((double) nYSize), NULL, pProgressData) )
{
eErr = CE_Failure;
CPLError( CE_Failure, CPLE_UserInterrupt,
"User terminated CreateCopy()" );
}
}
CPLFree( panScanline );
CPLFree( padfScanline );
VSIFCloseL( fpImage );
if( eErr != CE_None )
return NULL;
/* -------------------------------------------------------------------- */
/* Try to write projection file. */
/* -------------------------------------------------------------------- */
const char *pszOriginalProjection;
pszOriginalProjection = (char *)poSrcDS->GetProjectionRef();
if( !EQUAL( pszOriginalProjection, "" ) )
{
char *pszDirname, *pszBasename;
char *pszPrjFilename;
char *pszESRIProjection = NULL;
VSILFILE *fp;
OGRSpatialReference oSRS;
pszDirname = CPLStrdup( CPLGetPath(pszFilename) );
pszBasename = CPLStrdup( CPLGetBasename(pszFilename) );
pszPrjFilename = CPLStrdup( CPLFormFilename( pszDirname, pszBasename, "prj" ) );
fp = VSIFOpenL( pszPrjFilename, "wt" );
if (fp != NULL)
{
oSRS.importFromWkt( (char **) &pszOriginalProjection );
oSRS.morphToESRI();
oSRS.exportToWkt( &pszESRIProjection );
VSIFWriteL( pszESRIProjection, 1, strlen(pszESRIProjection), fp );
VSIFCloseL( fp );
CPLFree( pszESRIProjection );
}
else
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to create file %s.\n", pszPrjFilename );
}
CPLFree( pszDirname );
CPLFree( pszBasename );
CPLFree( pszPrjFilename );
}
/* -------------------------------------------------------------------- */
/* Re-open dataset, and copy any auxiliary pam information. */
/* -------------------------------------------------------------------- */
/* If outputing to stdout, we can't reopen it, so we'll return */
/* a fake dataset to make the caller happy */
CPLPushErrorHandler(CPLQuietErrorHandler);
GDALPamDataset* poDS = (GDALPamDataset*) GDALOpen(pszFilename, GA_ReadOnly);
CPLPopErrorHandler();
if (poDS)
{
poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
return poDS;
}
CPLErrorReset();
AAIGDataset* poAAIG_DS = new AAIGDataset();
poAAIG_DS->nRasterXSize = nXSize;
poAAIG_DS->nRasterYSize = nYSize;
poAAIG_DS->nBands = 1;
poAAIG_DS->SetBand( 1, new AAIGRasterBand( poAAIG_DS, 1 ) );
return poAAIG_DS;
}
/************************************************************************/
/* OSR_GDS() */
/************************************************************************/
static CPLString OSR_GDS( char **papszNV, const char * pszField,
const char *pszDefaultValue )
{
int iLine;
if( papszNV == NULL || papszNV[0] == NULL )
return pszDefaultValue;
for( iLine = 0;
papszNV[iLine] != NULL &&
!EQUALN(papszNV[iLine],pszField,strlen(pszField));
iLine++ ) {}
if( papszNV[iLine] == NULL )
return pszDefaultValue;
else
{
CPLString osResult;
char **papszTokens;
papszTokens = CSLTokenizeString(papszNV[iLine]);
if( CSLCount(papszTokens) > 1 )
osResult = papszTokens[1];
else
osResult = pszDefaultValue;
CSLDestroy( papszTokens );
return osResult;
}
}
/************************************************************************/
/* GDALRegister_AAIGrid() */
/************************************************************************/
void GDALRegister_AAIGrid()
{
GDALDriver *poDriver;
if( GDALGetDriverByName( "AAIGrid" ) == NULL )
{
poDriver = new GDALDriver();
poDriver->SetDescription( "AAIGrid" );
poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
"Arc/Info ASCII Grid" );
poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
"frmt_various.html#AAIGrid" );
poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "asc" );
poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
"Byte UInt16 Int16 Int32 Float32" );
poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
"<CreationOptionList>\n"
" <Option name='FORCE_CELLSIZE' type='boolean' description='Force use of CELLSIZE, default is FALSE.'/>\n"
" <Option name='DECIMAL_PRECISION' type='int' description='Number of decimal when writing floating-point numbers(%f).'/>\n"
" <Option name='SIGNIFICANT_DIGITS' type='int' description='Number of significant digits when writing floating-point numbers(%g).'/>\n"
"</CreationOptionList>\n" );
poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST,
"<OpenOptionLists>\n"
" <Option name='DATATYPE' type='string-select' description='Data type to be used.'>\n"
" <Value>Int32</Value>\n"
" <Value>Float32</Value>\n"
" <Value>Float64</Value>\n"
" </Option>\n"
"</OpenOptionLists>\n" );
poDriver->pfnOpen = AAIGDataset::Open;
poDriver->pfnIdentify = AAIGDataset::Identify;
poDriver->pfnCreateCopy = AAIGDataset::CreateCopy;
GetGDALDriverManager()->RegisterDriver( poDriver );
}
}
/************************************************************************/
/* GDALRegister_GRASSASCIIGrid() */
/************************************************************************/
void GDALRegister_GRASSASCIIGrid()
{
GDALDriver *poDriver;
if( GDALGetDriverByName( "GRASSASCIIGrid" ) == NULL )
{
poDriver = new GDALDriver();
poDriver->SetDescription( "GRASSASCIIGrid" );
poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
"GRASS ASCII Grid" );
poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
"frmt_various.html#GRASSASCIIGrid" );
poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
poDriver->pfnOpen = GRASSASCIIDataset::Open;
poDriver->pfnIdentify = GRASSASCIIDataset::Identify;
GetGDALDriverManager()->RegisterDriver( poDriver );
}
}