扩展GDAL,支持CNSDTF格式(一)

时间:2023-07-03 21:39:56

扩展GDAL,支持CNSDTF格式(一)

一、        简介

本文主要根据《*国家标准GB/T17798-2007——地理空间数据交换格式(Geospatialdata transfer format)》(http://www.bzxz.net/bzxz/129804.html)中定义的格网数据和矢量数据格式内容,对GDAL库进行扩充,使之可以直接打开这两种格式。

此外GDAL格式扩展就参考GDAL官方文档中的扩展方式。网址为:www.gdal.org/gdal_drivertut.html

二、        软件和数据

编写代码的软件使用Visual Studio2008 SP1,使用其他的记事本软件应该都可以,此外还需要一个编译器,来编译GDAL,用来检验写的代码的正确性。

数据是按照国标GB/T17798-2007中的示例数据自己写的一个测试数据。栅格数据内容如下,矢量数据比较复杂,后面单独再说,下面先介绍栅格数据。

DataMark:CNSDTF-DEM
Version:1.0
Alpha:0.0
Compress:0
X0:458312.500
Y0:3458762.500
DX:12.500
DY:12.500
Row:10
Col:30
ValueType:Integer
Hzoom:100
XYUnit:M
ZUnit:M
CoordinateSystemType:P
Spheroid:西安(1980),6378.140,298.257
Projection:高斯-克吕格
Parameters:117,0,,,,1,500000,0,3, 2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
100200 355 45 124 4548 147 145 1454 354
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
100200 355 45 124 4548 147 145 1454 354
100200 355 45 124 4548 147 145 1454 354
2387987 1564 1574 785 854 587 747 454 7844
100200 355 45 124 4548 147 145 1454 354
100200 355 45 124 4548 147 145 1454 354
100200 355 45 124 4548 147 145 1454 354
扩展GDAL,支持CNSDTF格式(一)

三、        编写源码

首先按照GDAL官网中的说明,我们要实现一个从GDALDataset类派生的一个子类CNSDTFDataset,该类的定义如下:

/************************************************************************/
/*==================================================================== */
/* CNSDTFDataset */
/*==================================================================== */
/************************************************************************/ classCNSDTFRasterBand; classCPL_DLL CNSDTFDataset : public GDALPamDataset
{
friend class CNSDTFRasterBand; VSILFILE *fp; char **papszPrj;
CPLString osPrjFilename;
char *pszProjection; // DataMark Version Compress Alpha
CPLString osDataMark;
CPLString osDataVersion;
GBool bIsCompress;
double adfAlphaValue;
CPLString osValueType;
int nHZoom;
CPLString osUnitType; 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;
double dfMin;
double dfMax; int ParseHeader(const char* pszHeader); public:
CNSDTFDataset();
~CNSDTFDataset(); virtual char **GetFileList(void); static GDALDataset *Open( GDALOpenInfo *poOpenInfo);
static int Identify( GDALOpenInfo * poOpenInfo);
static GDALDataset *CreateCopy( const char* pszFilename,
GDALDataset *poSrcDS, int bStrict,char ** papszOptions,
GDALProgressFunc pfnProgress, void *pProgressData ); virtual CPLErr GetGeoTransform( double *padfTransform);
virtual const char*GetProjectionRef(void);
};

下面是该类的实现,没有实现Create方法,只实现了CreateCopy方式,以后有时间把Create方法加上。

/************************************************************************/
/* CNSDTFDataset() */
/************************************************************************/ CNSDTFDataset::CNSDTFDataset()
{
papszPrj = NULL;
pszProjection = CPLStrdup("");
fp = NULL;
eDataType = GDT_Int32;
bNoDataSet = FALSE;
dfNoDataValue = -99999.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; osDataMark = "CNSDTF-RAS";
osDataVersion = "GB/T17798-2007";
bIsCompress = FALSE;
adfAlphaValue = 0.0;
osValueType = "Integer";
nHZoom = 1;
dfMin = 0.0;
dfMax = 0.0;
} /************************************************************************/
/* ~CNSDTFDataset() */
/************************************************************************/ CNSDTFDataset::~CNSDTFDataset() {
FlushCache(); if( fp != NULL )
VSIFCloseL( fp ); CPLFree( pszProjection );
CSLDestroy( papszPrj );
} /************************************************************************/
/* Tell() */
/************************************************************************/ GUIntBigCNSDTFDataset::Tell() {
return nBufferOffset + nOffsetInBuffer;
} /************************************************************************/
/* Seek() */
/************************************************************************/ intCNSDTFDataset::Seek( GUIntBig nNewOffset ) {
nOffsetInBuffer = sizeof(achReadBuf);
return VSIFSeekL( fp, nNewOffset, SEEK_SET);
} /************************************************************************/
/* Getc() */
/* */
/* Read a single character from the inputfile (efficiently we */
/* hope). */
/************************************************************************/ charCNSDTFDataset::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**CNSDTFDataset::GetFileList() {
char **papszFileList =GDALPamDataset::GetFileList(); if( papszPrj != NULL )
papszFileList = CSLAddString(papszFileList, osPrjFilename ); return papszFileList;
} /************************************************************************/
/* Identify() */
/************************************************************************/ intCNSDTFDataset::Identify( GDALOpenInfo * poOpenInfo ) {
/*-------------------------------------------------------------------- */
/* Does this look like an CNSDTF grid file? */
/*-------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes < 40
|| !( EQUALN((const char *) poOpenInfo->pabyHeader,"DataMark",8)||
EQUALN((const char *)poOpenInfo->pabyHeader,"Version",7) ||
EQUALN((const char *)poOpenInfo->pabyHeader,"Alpha",5)||
EQUALN((const char *)poOpenInfo->pabyHeader,"Compress",7)||
EQUALN((const char *)poOpenInfo->pabyHeader,"X0",2)||
EQUALN((const char *)poOpenInfo->pabyHeader,"Y0",2)||
EQUALN((const char *)poOpenInfo->pabyHeader,"DX",2)||
EQUALN((const char *)poOpenInfo->pabyHeader,"DY",2)||
EQUALN((const char *) poOpenInfo->pabyHeader,"Row",3)||
EQUALN((const char *)poOpenInfo->pabyHeader,"Col",3)||
EQUALN((const char *)poOpenInfo->pabyHeader,"ValueType",9)||
EQUALN((const char *)poOpenInfo->pabyHeader,"HZoom",5)) )
return FALSE; char** papszTokens = CSLTokenizeString2((const char *) poOpenInfo->pabyHeader, " \n\r\t" , 0 );
int nTokens = CSLCount(papszTokens);
if (nTokens <11) //the header at (the) least 11 lines
{
CSLDestroy( papszTokens );
return FALSE;
} if( poOpenInfo->nHeaderBytes < 40 ||
!( EQUALN(papszTokens[0],"DataMark:CNSDTF-DEM", 19) ||
EQUALN(papszTokens[0],"DataMark:CNSDTF-RAS", 19) ||
EQUALN(papszTokens[0],"DataMark:CSDTF-DEM", 18) ||
EQUALN(papszTokens[0],"DataMark:CSDTF-RAS", 18))
)
{
CSLDestroy( papszTokens );
return FALSE;
} CSLDestroy( papszTokens );
return TRUE;
} /************************************************************************/
/* Open() */
/************************************************************************/ GDALDataset*CNSDTFDataset::Open( GDALOpenInfo * poOpenInfo )
{
if (!Identify(poOpenInfo))
return NULL; int i = 0; /*-------------------------------------------------------------------- */
/* Create a corresponding GDALDataset. */
/*-------------------------------------------------------------------- */
CNSDTFDataset *poDS;
poDS = new CNSDTFDataset(); /* --------------------------------------------------------------------*/
/* Parse the header. */
/*-------------------------------------------------------------------- */
if (!poDS->ParseHeader((const char *)poOpenInfo->pabyHeader))
{
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) failedunexpectedly.",
poOpenInfo->pszFilename );
delete poDS;
return NULL;
} /*-------------------------------------------------------------------- */
/* Find the start of real data. */
/*-------------------------------------------------------------------- */
int nStartOfData; for (i=0; TRUE; i++)
{
char cChar =poOpenInfo->pabyHeader[i];
if( cChar == '\0' )
{
CPLError( CE_Failure,CPLE_AppDefined,
"Couldn't find datavalues in CNSDTF Grid file.\n" );
delete poDS;
return NULL;
} if (cChar == '\r' || cChar == '\n')
{
char cNext =poOpenInfo->pabyHeader[i+1];
if(isalpha(cNext) ||isalpha(poOpenInfo->pabyHeader[i+2]))
continue; if ((cNext >= '0' &&cNext <='9')
|| cNext=='-'
|| cNext=='.' )
{
nStartOfData = i+1;
/* Beginning of real datafound. */
break;
}
}
} /*-------------------------------------------------------------------- */
/* Recognize the type of data. */
/* --------------------------------------------------------------------*/
CPLAssert( NULL != poDS->fp ); /*-------------------------------------------------------------------- */
/* Create band information objects. */
/* --------------------------------------------------------------------*/
CNSDTFRasterBand* band = newCNSDTFRasterBand( poDS, nStartOfData );
poDS->SetBand( 1, band );
if (band->panLineOffset == NULL)
{
delete poDS;
return NULL;
} /*-------------------------------------------------------------------- */
/* Parse the SRS. */
/*-------------------------------------------------------------------- */
CPLString strProjection = ParseSpatialReference((constchar *) poOpenInfo->pabyHeader);
if (strProjection != "")
{
CPLFree( poDS->pszProjection );
poDS->pszProjection =CPLStrdup(strProjection.c_str());
} if (EQUAL(poDS->pszProjection,""))
{
/* --------------------------------------------------------------------*/
/* 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( "CNSDTFGrid", "Loaded SRS from %s",
poDS->osPrjFilename.c_str()); if( oSRS.importFromESRI(poDS->papszPrj ) == OGRERR_NONE )
{
// If geographic valuesare in seconds, we must transform.
// Is there a code forminutes 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->papszSiblingFiles ); return( poDS );
} /************************************************************************/
/* ParseHeader() */
/************************************************************************/ intCNSDTFDataset::ParseHeader(const char* pszHeader)
{
int i, j;
char** papszTokens = CSLTokenizeString2(pszHeader, " \n\r\t::" , 0 );
int nTokens = CSLCount(papszTokens); if ( (i = CSLFindString( papszTokens,"DataMark" )) < 0 || i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
osDataMark = papszTokens[i + 1]; if ( (i = CSLFindString( papszTokens,"Version" )) < 0 || i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
osDataVersion = papszTokens[i + 1]; if ( (i = CSLFindString( papszTokens,"Alpha" )) < 0 || i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
adfAlphaValue = CPLAtofM(papszTokens[i +1]); if ( (i = CSLFindString( papszTokens,"Compress" )) < 0 || i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
bIsCompress = atoi(papszTokens[i + 1]); if ( (i = CSLFindString( papszTokens,"Hzoom" )) < 0 ||
i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
}
nHZoom = atoi(papszTokens[i + 1]); if ( (i = CSLFindString( papszTokens,"Col" )) < 0 || i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
} nRasterXSize = atoi(papszTokens[i + 1]); if ( (i = CSLFindString( papszTokens,"Row" )) < 0 || i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
} nRasterYSize = atoi(papszTokens[i + 1]); if(!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
{
CSLDestroy( papszTokens );
return FALSE;
} double dfCellDX = 0;
double dfCellDY = 0;
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,"X0" )) >= 0 &&
(j = CSLFindString( papszTokens,"Y0" )) >= 0 &&
i + 1 < nTokens && j + 1< nTokens)
{
adfGeoTransform[0] = CPLAtofM(papszTokens[i + 1] );
adfGeoTransform[1] = dfCellDX;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = CPLAtofM(papszTokens[j + 1] );
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,"ValueType" )) < 0 || i + 1 >= nTokens)
{
CSLDestroy( papszTokens );
return FALSE;
} osValueType = papszTokens[i + 1];
if (EQUAL(osValueType,"Integer"))
{
eDataType = GDT_Int32;
bNoDataSet = TRUE;
dfNoDataValue = -99999;
}
else if (EQUAL(osValueType,"Char"))
{
eDataType = GDT_Byte;
}
else
{
CSLDestroy( papszTokens );
returnFALSE;
} if( (i = CSLFindString( papszTokens,"NODATA_value" )) >= 0 && i + 1 < nTokens)
{
const char* pszNoData = papszTokens[i+ 1]; bNoDataSet = TRUE;
dfNoDataValue = CPLAtofM(pszNoData);
if((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,"MinV" )) >= 0 && i + 1 < nTokens)
dfMin = CPLAtofM(papszTokens[i + 1]); if ( (i = CSLFindString( papszTokens,"MaxV" )) >= 0 && i + 1 < nTokens)
dfMax = CPLAtofM(papszTokens[i + 1]); if ( (i = CSLFindString( papszTokens,"Unit" )) >= 0 && i + 1 < nTokens)
osUnitType = papszTokens[i + 1]; if ( (i = CSLFindString( papszTokens,"ZUnit" )) >= 0 && i + 1 < nTokens)
osUnitType = papszTokens[i + 1]; CSLDestroy( papszTokens );
return TRUE;
} /************************************************************************/
/* GetGeoTransform() */
/************************************************************************/ CPLErrCNSDTFDataset::GetGeoTransform( double * padfTransform ) {
memcpy( padfTransform, adfGeoTransform,sizeof(double) * 6 );
return( CE_None );
} /************************************************************************/
/* GetProjectionRef() */
/************************************************************************/ constchar *CNSDTFDataset::GetProjectionRef() {
return pszProjection;
} /************************************************************************/
/* CreateCopy() */
/************************************************************************/ GDALDataset* CNSDTFDataset::CreateCopy(
constchar * pszFilename, GDALDataset *poSrcDS,
intbStrict, char ** papszOptions,
GDALProgressFuncpfnProgress, 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,
"CNSDTF Grid driverdoesn't support %d bands. Must be 1band.\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 CNSDTF Grid file header */
/*-------------------------------------------------------------------- */
char szHeader[2000];
const char *pszForceRaster =CSLFetchNameValue( papszOptions, "FORCE_RASTER" );
char* pszHeaderMark ="DataMark:CNSDTF-DEM";
if(pszForceRaster != NULL &&CSLTestBoolean(pszForceRaster))
pszHeaderMark ="DataMark:CNSDTF-RAS"; double adfGeoTransform[6];
poSrcDS->GetGeoTransform(adfGeoTransform ); sprintf( szHeader,
"%s\n"
"Version:GB/T17798-2007\n"
"Alpha:0.0\n"
"Compress:0\n"
"X0:%.12f\n"
"Y0:%.12f\n"
"DX:%.12f\n"
"DY:%.12f\n"
"Row:%d\n"
"Col:%d\n"
"ValueType:Integer\n", //Integer
pszHeaderMark,
adfGeoTransform[0],
adfGeoTransform[3],
ABS(adfGeoTransform[1]),
ABS(adfGeoTransform[5]),
nYSize, nXSize); /*-------------------------------------------------------------------- */
/* Try to write projection file. */
/* --------------------------------------------------------------------*/
const char *pszOriginalProjection = 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 createfile %s.\n", pszPrjFilename );
}
CPLFree( pszDirname );
CPLFree( pszBasename );
CPLFree( pszPrjFilename );
} GDALRasterBand * poBand =poSrcDS->GetRasterBand( 1 );
double dfNoData, dfScale, dfMin, dfMax;
int bSuccess; const char* pszUnitType =poBand->GetUnitType();
if(pszUnitType != NULL)
sprintf( szHeader+strlen(szHeader),"ZUnit:%s\n", pszUnitType ); // Write `nodata' value to header if it isexists in source dataset
dfNoData = poBand->GetNoDataValue(&bSuccess );
if ( bSuccess )
sprintf( szHeader+strlen(szHeader),"NODATA_value:%6.20g\n", dfNoData ); // Write `HZoom' value to header if it isexists in source dataset
dfScale = poBand->GetScale(&bSuccess );
if ( !bSuccess )
dfScale = 1.0;
sprintf( szHeader+strlen(szHeader),"HZoom:%.20g\n", dfScale ); // Write `minmax' value to header if it isexists in source dataset
dfMin = poBand->GetMinimum(&bSuccess );
if ( bSuccess )
sprintf( szHeader+strlen(szHeader),"MinV:%.20g\n", dfMin ); dfMax = poBand->GetMaximum(&bSuccess );
if ( bSuccess )
sprintf( szHeader+strlen(szHeader),"MaxV:%.20g\n", dfMax ); VSIFWriteL( szHeader, 1, strlen(szHeader),fpImage ); /*-------------------------------------------------------------------- */
/* Builds the format string used for printing float values. */
/*-------------------------------------------------------------------- */
char szFormatFloat[32];
strcpy(szFormatFloat, " %.20g");
const char *pszDecimalPrecision =CSLFetchNameValue( papszOptions, "DECIMAL_PRECISION" );
if (pszDecimalPrecision)
{
int nDecimal =atoi(pszDecimalPrecision);
if (nDecimal >= 0)
sprintf(szFormatFloat, "%%.%dg", nDecimal);
} /*-------------------------------------------------------------------- */
/* Loop over image, copying image data. */
/*-------------------------------------------------------------------- */
int *panScanline = NULL;
double *padfScanline = NULL;
int bReadAsInt;
int iLine, iPixel;
CPLErr eErr = CE_None; bReadAsInt = (poBand->GetRasterDataType() == GDT_Byte
|| poBand->GetRasterDataType() ==GDT_Int16
|| poBand->GetRasterDataType() ==GDT_UInt16
|| poBand->GetRasterDataType() ==GDT_Int32 ); // 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 ); if( bReadAsInt )
{
for ( iPixel = 0; iPixel <nXSize; iPixel++ )
{
sprintf( szHeader,"%d ", panScanline[iPixel] );
if(iPixel % 10 == 9)
sprintf(szHeader+strlen(szHeader), "\n" ); 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++ )
{
sprintf( szHeader,szFormatFloat, padfScanline[iPixel] );
if(iPixel % 10 == 9)
sprintf(szHeader+strlen(szHeader), "\n" ); 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; /*-------------------------------------------------------------------- */
/* Re-open dataset, and copy any auxilary pam information. */
/*-------------------------------------------------------------------- */ /* If outputing to stdout, we can't reopenit, 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(); CNSDTFDataset* poCnsdtfDS = newCNSDTFDataset();
poCnsdtfDS->nRasterXSize = nXSize;
poCnsdtfDS->nRasterYSize = nYSize;
poCnsdtfDS->nBands = 1;
poCnsdtfDS->SetBand( 1, newCNSDTFRasterBand( poCnsdtfDS, 1 ) );
return poCnsdtfDS;
}

上面的代码中关于CNSDTF中的空间参考部分,由于定义的比较复杂,所以保留了直接读取同名的prj文件投影。在这里需要用到一个函数OSR_GDS,该函数的实现以及定义如下:

/************************************************************************/
/* OSR_GDS() */
/************************************************************************/ staticCPLString 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;
}
}

此外还写了从文件头中解析投影信息并组成WKT格式,目前还有国标中定义的两个投影(彭纳等积伪圆锥和等积斜轴切方位)没有找到与OSR中对应的,所以暂时没有处理。具体的代码如下:

staticchar *papszProjectionName[18] = {
"地理坐标系",
"高斯-克吕格",
"兰勃特正形割圆锥",
"兰勃特正形切圆锥",
"兰勃特等积方位",
"亚尔勃斯等积割圆锥",
"亚尔勃斯等积切圆锥",
"通用横轴墨卡托",
"墨卡托正轴等角切圆柱",
"墨卡托正轴等角割圆柱",
"波斯托等距切方位",
"彭纳等积伪圆锥",
"等积正轴切圆柱",
"等积正轴割圆柱",
"等距正轴切圆锥",
"等距正轴割圆锥",
"等积斜轴切方位",
NULL
}; staticconst int nProjectionParametersIndex[18] = {
0, //0000000000 "地理坐标系"
543, //1000011111 "高斯-克吕格"
972, //1111001100 "兰勃特正形割圆锥"
780, //1100001100 "兰勃特正形切圆锥"
796, //1100011100 "兰勃特等积方位"
543, //1111001100 "亚尔勃斯等积割圆锥"
972, //1111001100 "亚尔勃斯等积切圆锥"
525, //1000001101 "通用横轴墨卡托"
540, //1000011100 "墨卡托正轴等角切圆柱"
796, //1100011100 "墨卡托正轴等角割圆柱"
780, //1100001100 "波斯托等距切方位"
780, //1100001100 "彭纳等积伪圆锥"
524, //1000001100 "等积正轴切圆柱"
780, //1100001100 "等积正轴割圆柱"
780, //1100001100 "等距正轴切圆锥"
972, //1111001100 "等距正轴割圆锥"
796, //1100011100 "等积斜轴切方位"
0
}; /************************************************************************/
/* ParseSpatialReference() */
/************************************************************************/ CPLStringParseSpatialReference(const char* pszHeader)
{
char** papszTokens = CSLTokenizeString2(pszHeader, " \n\r\t::", 0 );
int nTokens = CSLCount(papszTokens); int iCST = CSLFindString( papszTokens,"CoordinateSystemType" );
if ( iCST<0 || iCST+1 >= nTokens)
{
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","Can't find SRS");
return "";
} char *pszCoordinateSystemType =papszTokens[iCST + 1];
if (EQUAL(pszCoordinateSystemType,"C"))
{
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","Can't find SRS");
return "";
} if ( (!EQUAL(pszCoordinateSystemType,"D")) && (!EQUAL(pszCoordinateSystemType, "P")))
{
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","Can't find SRS");
return "";
} int iSpheroid = CSLFindString(papszTokens, "Spheroid" );
if ( iSpheroid<0 || iSpheroid+1 >=nTokens)
{
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","Can't find Spheroid, but the CoordinateSystemType is %s, this file headermaybe is wrong.", pszCoordinateSystemType);
return "";
} char* pszSpheroid = papszTokens[iSpheroid+ 1];
char** papszSpheroidTokens =CSLTokenizeString2( pszSpheroid, ",,", 0 );
int nSpheroidTokens =CSLCount(papszSpheroidTokens);
if(nSpheroidTokens != 3)
{
CSLDestroy( papszSpheroidTokens );
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","The Spheroid value is %s, maybe is wrong.", pszSpheroid);
return "";
} char *pszPrimeMeridian ="Greenwich";
double dPrimeMeridian = 0.0;
int iPrimeMeridian = CSLFindString(papszTokens, "PrimeMeridian" );
if ( iPrimeMeridian>=0 &&iPrimeMeridian+1 < nTokens)
{
char* pszPrimeMeridianValue =papszTokens[iPrimeMeridian + 1];
if (!EQUAL(pszPrimeMeridian,pszPrimeMeridianValue))
{
char** papszPrimeMeridianTokens= CSLTokenizeString2( pszPrimeMeridianValue, ",,", 0 );
int nPrimeMeridianTokens =CSLCount(papszPrimeMeridianTokens);
if(nPrimeMeridianTokens == 2)
{
pszPrimeMeridian =papszPrimeMeridianTokens[0];
dPrimeMeridian =CPLAtofM(papszPrimeMeridianTokens[1]);
} CSLDestroy(papszPrimeMeridianTokens );
}
} double dSemiMajor =CPLAtofM(papszSpheroidTokens[1]);
double dInvFlattening = CPLAtofM(papszSpheroidTokens[2]);
if (dSemiMajor < 6400) //unit is km
dSemiMajor *= 1000; OGRSpatialReference oSRS;
oSRS.SetGeogCS(papszSpheroidTokens[0],
"unknown",
papszSpheroidTokens[0],
dSemiMajor, dInvFlattening,
pszPrimeMeridian, dPrimeMeridian,
"degree",0.0174532925199433 ); char* pszWkt = NULL; if (EQUAL(pszCoordinateSystemType,"D"))
{
oSRS.exportToWkt( &pszWkt );
CSLDestroy( papszTokens );
return CPLString(pszWkt);
} int iProjection = CSLFindString(papszTokens, "Projection" );
if ( iProjection<0 || iProjection+1>= nTokens)
{
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","Can't find Projection");
return "";
} char* pszProjection =papszTokens[iProjection+1];
if(EQUAL(pszProjection, "地理坐标系"))
{
oSRS.exportToWkt( &pszWkt );
CSLDestroy( papszTokens );
return CPLString(pszWkt);
} int iProjectionType = CSLFindString(papszProjectionName, pszProjection );
if (iProjectionType<=0)
{
oSRS.exportToWkt( &pszWkt );
CSLDestroy( papszTokens );
return CPLString(pszWkt);
} int iParametersIndex =nProjectionParametersIndex[iProjectionType]; int iParameters = CSLFindString(papszTokens, "Parameters" );
if ( iParameters<0 || iParameters+1>= nTokens)
{
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","Can't find Projection Parameters");
return "";
} char* pszParameters =papszTokens[iParameters+1];
char** papszParametersTokens =CSLTokenizeString2( pszParameters, ",,", CSLT_ALLOWEMPTYTOKENS);
int nParametersTokens =CSLCount(papszParametersTokens);
if(nParametersTokens != 10)
{
CSLDestroy( papszParametersTokens );
CSLDestroy( papszTokens );
CPLDebug( "CNSDTF Grid","Parse projection parameters error, the count should be 10, but now is%d", nParametersTokens);
return "";
} double dParmeters[10] = {0.0};
for (int i=0; i<10; i++)
dParmeters[i] =CPLAtofM(papszParametersTokens[i]); switch(iProjectionType)
{
case 1: //高斯-克吕格
oSRS.SetTM(0.0, dParmeters[0],dParmeters[5], dParmeters[6], dParmeters[7]);
break;
case 2: //兰勃特正形割圆锥
oSRS.SetLCC(dParmeters[2],dParmeters[3], dParmeters[1], dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 3: //兰勃特正形切圆锥
oSRS.SetLCC(dParmeters[1],dParmeters[1], dParmeters[1], dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 4: //兰勃特等积方位
oSRS.SetLAEA(dParmeters[1],dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 5: //亚尔勃斯等积割圆锥
oSRS.SetACEA(dParmeters[2], dParmeters[3],dParmeters[1], dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 6: //亚尔勃斯等积切圆锥
oSRS.SetACEA(dParmeters[1],dParmeters[1], dParmeters[1], dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 7: //通用横轴墨卡托
{
int dZone =((int)(dParmeters[0]) + 183) / 6;
int bIsNorth = TRUE;
if(dParmeters[7] >=10000000)
bIsNorth = FALSE; oSRS.SetUTM(dZone, bIsNorth);
}
break;
case 8: //墨卡托正轴等角切圆柱
oSRS.SetMercator(0.0, dParmeters[0],dParmeters[5], dParmeters[6], dParmeters[7]);
break;
case 9: //墨卡托正轴等角割圆柱
oSRS.SetMercator(dParmeters[1],dParmeters[0], dParmeters[5], dParmeters[6], dParmeters[7]);
break;
case 10: //波斯托等距切方位
oSRS.SetAE(dParmeters[1],dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 11: //彭纳等积伪圆锥
// dont know which one is match
break;
case 12: //等积正轴切圆柱
oSRS.SetCEA(0.0, dParmeters[0],dParmeters[6], dParmeters[7]);
break;
case 13: //等积正轴割圆柱
oSRS.SetCEA(dParmeters[1],dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 14: //等距正轴切圆锥
oSRS.SetMC(dParmeters[1],dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 15: //等距正轴割圆锥
oSRS.SetEC(dParmeters[2],dParmeters[3], dParmeters[1], dParmeters[0], dParmeters[6], dParmeters[7]);
break;
case 16: //等积斜轴切方位
// dont know which one is match
break;
default:
{
CSLDestroy(papszParametersTokens );
CSLDestroy( papszTokens );
CPLDebug( "CNSDTFGrid", "Can not support this projection %s",papszProjectionName);
return "";
}
} CSLDestroy( papszParametersTokens ); oSRS.exportToWkt( &pszWkt );
CSLDestroy( papszTokens );
return CPLString(pszWkt);
}

接下来关于Dataset相关的所有的都已经实现,剩下就是GDALRasterBand类了。同样,按照官方文档中的说明,首先从GDALRasterBand类中派生一个类CNSDTFRasterBand,下面是该类的定义。

/************************************************************************/
/*==================================================================== */
/* CNSDTFRasterBand */
/*==================================================================== */
/************************************************************************/ classCNSDTFRasterBand : public GDALPamRasterBand
{
friend class CNSDTFDataset; GUIntBig *panLineOffset;
char *pszUnitType;
double dfScale; public: CNSDTFRasterBand( CNSDTFDataset *poDS, intnDataStart);
virtual ~CNSDTFRasterBand(); virtual double GetMinimum( int *pbSuccess);
virtual double GetMaximum( int *pbSuccess);
virtual double GetNoDataValue( int *pbSuccess);
virtual CPLErr SetNoDataValue( doubledfNoData);
virtual const char *GetUnitType();
CPLErr SetUnitType( const char *pszNewValue);
virtual double GetScale( int *pbSuccess =NULL );
CPLErr SetScale( double dfNewScale); virtual CPLErr IReadBlock( int nBlockXOff,int nBlockYOff, void * pImage);
};

接下来就是CNSDTFRasterBand类的具体实现。

/************************************************************************/
/* CNSDTFRasterBand() */
/************************************************************************/ CNSDTFRasterBand::CNSDTFRasterBand(CNSDTFDataset *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,
"CNSDTFRasterBand::CNSDTFRasterBand: Out of memory (nRasterYSize = %d)",
poDS->nRasterYSize);
return;
} panLineOffset[0] = nDataStart;
dfScale = poDS->nHZoom;
pszUnitType =CPLStrdup(poDS->osUnitType.c_str());
} /************************************************************************/
/* ~CNSDTFRasterBand() */
/************************************************************************/ CNSDTFRasterBand::~CNSDTFRasterBand() {
CPLFree( panLineOffset );
CPLFree( pszUnitType );
} /************************************************************************/
/* IReadBlock() */
/************************************************************************/ CPLErrCNSDTFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
void* pImage ) {
CNSDTFDataset *poODS = (CNSDTFDataset *)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 %luin input file to read data.",
(long unsignedint)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( (unsignedchar)chNext ) ); while( chNext != '\0' &&!isspace((unsigned char)chNext) )
{
if( iTokenChar ==sizeof(szToken)-2 )
{
CPLError( CE_Failure,CPLE_FileIO,
"Token too longat 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'tread 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;
} /************************************************************************/
/* GetMinimum() */
/************************************************************************/ doubleCNSDTFRasterBand::GetMinimum( int *pbSuccess ) {
CNSDTFDataset *poODS = (CNSDTFDataset *) poDS; if( pbSuccess != NULL )
*pbSuccess = TRUE; return poODS->dfMin;
} /************************************************************************/
/* GetMaximum() */
/************************************************************************/ doubleCNSDTFRasterBand::GetMaximum( int *pbSuccess ) {
CNSDTFDataset *poODS = (CNSDTFDataset *) poDS; if( pbSuccess != NULL )
*pbSuccess = TRUE; return poODS->dfMax;
} /************************************************************************/
/* GetNoDataValue() */
/************************************************************************/ doubleCNSDTFRasterBand::GetNoDataValue( int * pbSuccess ) {
CNSDTFDataset *poODS = (CNSDTFDataset *) poDS; if( pbSuccess )
*pbSuccess = poODS->bNoDataSet; return poODS->dfNoDataValue;
} /************************************************************************/
/* SetNoDataValue() */
/************************************************************************/ CPLErrCNSDTFRasterBand::SetNoDataValue( double dfNoData ) {
CNSDTFDataset *poODS = (CNSDTFDataset *)poDS; poODS->bNoDataSet = TRUE;
poODS->dfNoDataValue = dfNoData; return CE_None;
} /************************************************************************/
/* GetUnitType() */
/************************************************************************/ constchar *CNSDTFRasterBand::GetUnitType() {
if( pszUnitType == NULL )
return "";
else
return pszUnitType;
} /************************************************************************/
/* SetUnitType() */
/************************************************************************/ CPLErrCNSDTFRasterBand::SetUnitType( const char *pszNewValue ) {
CPLFree( pszUnitType ); if( pszNewValue == NULL )
pszUnitType = NULL;
else
pszUnitType = CPLStrdup(pszNewValue); return CE_None;
} /************************************************************************/
/* GetScale() */
/************************************************************************/ doubleCNSDTFRasterBand::GetScale( int *pbSuccess ) {
if( pbSuccess != NULL )
*pbSuccess = TRUE; return dfScale;
} /************************************************************************/
/* SetScale() */
/************************************************************************/ CPLErrCNSDTFRasterBand::SetScale( double dfNewScale ) {
dfScale = dfNewScale;
return CE_None;
}

至此,基本上所有的源码都已经编写完毕,接下来就是将源码加入到GDAL源码中进行编译。首先将上面的代码写成cpp和h文件,也可以写一个cpp。并且在cpp的末尾加上一个函数,用来注册使用,具体代码如下,主要就是对该格式的一些描述,以及是否可以创建,删除等,可以参考其他的驱动来进行修改就可以了。

/************************************************************************/
/* GDALRegister_CNSDTFGrid() */
/************************************************************************/ voidGDALRegister_CNSDTFGrid() {
GDALDriver *poDriver; if( GDALGetDriverByName("CNSDTF-GRD" ) == NULL )
{
poDriver = new GDALDriver(); poDriver->SetDescription("CNSDTF-GRD" );
poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
"China Geospatial DataTransfer Grid Format (.grd)" );
poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
"frmt_cnsdtf.html" );
poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd" );
poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
"Byte UInt16 Int16Int32" ); poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES" );
poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
"<CreationOptionList>\n"
" <Option name='FORCE_RASTER'type='boolean' description='Force use of RASTER, default isFALSE(DEM).'/>\n"
" <Option name='DECIMAL_PRECISION'type='int' description='Number of decimal when writing floating-pointnumbers.'/>\n"
"</CreationOptionList>\n"); poDriver->pfnOpen =CNSDTFDataset::Open;
poDriver->pfnIdentify =CNSDTFDataset::Identify;
poDriver->pfnCreateCopy =CNSDTFDataset::CreateCopy; GetGDALDriverManager()->RegisterDriver(poDriver );
}
}

四、        修改GDAL编译文件

上面基本上编写完了所有的代码,下面就需要修改GDAL库中的部分文件,然后编译来使GDAL支持新的文件格式。

1、在frmts文件夹中新建一个cnsdtf的文件夹,将上面的代码放到该文件夹中,并新建三个文件,分别是frmt_cnsdtf.html、makefile.vc 和GNUmakefile。frmt_cnsdtf.html这个文件主要就是对新的文件格式的一个说明,书写方式可以参考其他文件夹中的文件。makefile.vc和GNUmakefile分别对应于windows系统和Linux系统下的编译文件,这里只编译Window版本,所以只编写makefile.vc文件,编辑完的内容如下:

OBJ =   cnsdtfdataset.objcnsdtfsrs.obj

GDAL_ROOT =   ..\..

!INCLUDE$(GDAL_ROOT)\nmake.opt

default: $(OBJ)
xcopy /D /Y *.obj ..\o clean:
-del *.obj

2、修改frmts文件夹中的gdalallregister.cpp文件,在代码中的最后将新的文件驱动进行注册进去,如下(红色的为新增的):

#ifdefFRMT_iris
GDALRegister_IRIS();
#endif #ifdefFRMT_cnsdtf
GDALRegister_CNSDTFGrid(); //红色
#endif

3、修改frmts文件夹中的makefile.vc,在EXTRAFLAGS的最后没添加-DFRMT_cnsdtf,需要与步骤2中的一致。

4、修改gcore文件夹中的gdal_frmts.h文件,在最后添加新驱动注册声明。如下(红色的为新增的):

voidCPL_DLL GDALRegister_IRIS(void);
voidCPL_DLL GDALRegister_CNSDTFGrid(void); //红色

5、修改完上面的文件,然后将GDAL工程清理掉之后重新编译,如果提示有语法错误,请先修改代码中的语法错误,如果没有,正常编译完成的话,就是用gdalinfo工具(或者其他的工具)中的命令行参数—formats(下图1)或者—format CNSDTF-GRD(下图2)来进行查看是否新的驱动已经包含在内。如果输出有CNSDTF的字样,说明新的驱动添加成功。下面我们使用gdalinfo来对真实的数据进行测试。

扩展GDAL,支持CNSDTF格式(一)

图1 –formats命令

扩展GDAL,支持CNSDTF格式(一)

图2 —format CNSDTF-GRD命令

五、        测试

为了测试我们新添加的驱动是否能够正确读取数据,我们使用gdalinfo工具来进行检验。使用的命令行如下所示:

gdalinfo –checksum --configGDAL_FILENAME_IS_UTF8 NO F:\我的资料\cnsdtf\sample.grd

使用-checksum的原因是为了检验GDAL读取像素值是否正确,--config是为了支持中文路径,如果程序不报错,并且能够输出下面的信息,说明基本上读取是没有问题了。

扩展GDAL,支持CNSDTF格式(一)

接下来可以建议创建图像的功能,可以使用gdal_translate工具,将一个其他格式的单波段数据进行转换成CNSDTF格式,然后使用gdalinfo的-checksum命令,如果两个图像转换前后的checksum值计算的相同,那么就说明创建应该是没有问题的。

之后我会将CNSDTF格式的代码申请提交到GDAL的库中,如果GDAL接收的话,那么以后就可以直接支持了,如果没有接收,使用上面的方式自己扩展一下也问题不大。接下来有时间的话会将Create方法进行实现,同时也将矢量格式也扩展到OGR库中。