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


    扩展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官网中的说明,我们要实现一个从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] = '';
     
          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, " 
    
    	" , 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 == '' )
               {
                     CPLError( CE_Failure,CPLE_AppDefined,
                          "Couldn't find datavalues in CNSDTF Grid file.
    " );
                     delete poDS;
                     return NULL;
               }
     
               if (cChar == '
    ' || cChar == '
    ')
               {
                     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,  " 
    
    	::" , 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.
    ",
                     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.
    ", 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
    "
                "Version:GB/T17798-2007
    "
               "Alpha:0.0
    "
               "Compress:0
    "
               "X0:%.12f
    "
               "Y0:%.12f
    "
               "DX:%.12f
    "
               "DY:%.12f
    "
               "Row:%d
    "
               "Col:%d
    "
               "ValueType:Integer
    ",    //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.
    ", 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
    ", 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
    ", 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
    ", dfScale );
     
          // Write `minmax' value to header if it isexists in source dataset
          dfMin = poBand->GetMinimum(&bSuccess );
          if ( bSuccess )
               sprintf( szHeader+strlen(szHeader),"MinV:%.20g
    ", dfMin );
     
          dfMax = poBand->GetMaximum(&bSuccess );
          if ( bSuccess )
               sprintf( szHeader+strlen(szHeader),"MaxV:%.20g
    ", 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), "
    " );
     
                          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?
    " );
                                      break;
                                }
                                osBuf ="";
                          }
                     }
               }
               else
               {
                     for ( iPixel = 0; iPixel <nXSize; iPixel++ )
                     {
                          sprintf( szHeader,szFormatFloat, padfScanline[iPixel] );
                          if(iPixel % 10 == 9)
                                sprintf(szHeader+strlen(szHeader), "
    " );
     
                          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?
    " );
                                      break;
                                }
                                osBuf ="";
                          }
                     }
               }
               VSIFWriteL( (void *) "
    ",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,  " 
    
    	::", 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 != '' &&!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 == '' &&
                     (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] = '';
     
               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>
    "
                     "   <Option name='FORCE_RASTER'type='boolean' description='Force use of RASTER, default isFALSE(DEM).'/>
    "
                     "   <Option name='DECIMAL_PRECISION'type='int' description='Number of decimal when writing floating-pointnumbers.'/>
    "
                     "</CreationOptionList>
    ");
     
               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)
    make.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来对真实的数据进行测试。

    图1 –formats命令

    图2 —format CNSDTF-GRD命令

    五、        测试

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

    gdalinfo –checksum --configGDAL_FILENAME_IS_UTF8 NO F:我的资料cnsdtfsample.grd

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

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

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

  • 相关阅读:
    40-cut 简明笔记
    50-ln 简明笔记
    35-less 简明笔记
    37-more 简明笔记
    9-cat 简明笔记
    64-who 简明笔记
    60-chmod 修改文件的权限
    useradd 添加用户
    14-find 查找文件
    层次越低的人,越容易放弃自己
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6313930.html
Copyright © 2020-2023  润新知