LCOV - code coverage report
Current view: directory - frmts/gsg - gsagdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 771 305 39.6 %
Date: 2010-01-09 Functions: 21 15 71.4 %

       1                 : /******************************************************************************
       2                 :  * $Id: gsagdataset.cpp 16925 2009-05-03 14:16:49Z rouault $
       3                 :  *
       4                 :  * Project:  GDAL
       5                 :  * Purpose:  Implements the Golden Software ASCII Grid Format.
       6                 :  * Author:   Kevin Locke, kwl7@cornell.edu
       7                 :  *       (Based largely on aaigriddataset.cpp by Frank Warmerdam)
       8                 :  *
       9                 :  ******************************************************************************
      10                 :  * Copyright (c) 2006, Kevin Locke <kwl7@cornell.edu>
      11                 :  *
      12                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      13                 :  * copy of this software and associated documentation files (the "Software"),
      14                 :  * to deal in the Software without restriction, including without limitation
      15                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16                 :  * and/or sell copies of the Software, and to permit persons to whom the
      17                 :  * Software is furnished to do so, subject to the following conditions:
      18                 :  *
      19                 :  * The above copyright notice and this permission notice shall be included
      20                 :  * in all copies or substantial portions of the Software.
      21                 :  *
      22                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28                 :  * DEALINGS IN THE SOFTWARE.
      29                 :  ****************************************************************************/
      30                 : 
      31                 : #include "cpl_conv.h"
      32                 : 
      33                 : #include <sstream>
      34                 : #include <float.h>
      35                 : #include <limits.h>
      36                 : #include <assert.h>
      37                 : 
      38                 : #include "gdal_pam.h"
      39                 : 
      40                 : #ifndef DBL_MAX
      41                 : # ifdef __DBL_MAX__
      42                 : #  define DBL_MAX __DBL_MAX__
      43                 : # else
      44                 : #  define DBL_MAX 1.7976931348623157E+308
      45                 : # endif /* __DBL_MAX__ */
      46                 : #endif /* DBL_MAX */
      47                 : 
      48                 : #ifndef INT_MAX
      49                 : # define INT_MAX 2147483647
      50                 : #endif /* INT_MAX */
      51                 : 
      52                 : CPL_CVSID("$Id: gsagdataset.cpp 16925 2009-05-03 14:16:49Z rouault $");
      53                 : 
      54                 : CPL_C_START
      55                 : void  GDALRegister_GSAG(void);
      56                 : CPL_C_END
      57                 : 
      58                 : /************************************************************************/
      59                 : /* ==================================================================== */
      60                 : /*        GSAGDataset       */
      61                 : /* ==================================================================== */
      62                 : /************************************************************************/
      63                 : 
      64                 : class GSAGRasterBand;
      65                 : 
      66                 : class GSAGDataset : public GDALPamDataset
      67                 : {
      68                 :     friend class GSAGRasterBand;
      69                 : 
      70                 :     static const double dfNODATA_VALUE;
      71                 :     static const int nFIELD_PRECISION;
      72                 :     static const size_t nMAX_HEADER_SIZE;
      73                 : 
      74                 :     static CPLErr ShiftFileContents( FILE *, vsi_l_offset, int, const char * );
      75                 : 
      76                 :     FILE  *fp;
      77                 :     size_t   nMinMaxZOffset;
      78                 :     char   szEOL[3];
      79                 : 
      80                 :     CPLErr UpdateHeader();
      81                 : 
      82                 :   public:
      83                 :     GSAGDataset( const char *pszEOL = "\x0D\x0A" );
      84                 :     ~GSAGDataset();
      85                 : 
      86                 :     static GDALDataset *Open( GDALOpenInfo * );
      87                 :     static GDALDataset *CreateCopy( const char *pszFilename,
      88                 :             GDALDataset *poSrcDS,
      89                 :             int bStrict, char **papszOptions,
      90                 :             GDALProgressFunc pfnProgress,
      91                 :             void *pProgressData );
      92                 :     static CPLErr Delete( const char *pszFilename );
      93                 : 
      94                 :     CPLErr GetGeoTransform( double *padfGeoTransform );
      95                 :     CPLErr SetGeoTransform( double *padfGeoTransform );
      96                 : };
      97                 : 
      98                 : /* NOTE:  This is not mentioned in the spec, but Surfer 8 uses this value */
      99                 : const double GSAGDataset::dfNODATA_VALUE = 1.70141E+38;
     100                 : 
     101                 : const int GSAGDataset::nFIELD_PRECISION = 14;
     102                 : const size_t GSAGDataset::nMAX_HEADER_SIZE = 200;
     103                 : 
     104                 : /************************************************************************/
     105                 : /* ==================================================================== */
     106                 : /*                            GSAGRasterBand                            */
     107                 : /* ==================================================================== */
     108                 : /************************************************************************/
     109                 : 
     110                 : class GSAGRasterBand : public GDALPamRasterBand
     111                 : {
     112                 :     friend class GSAGDataset;
     113                 : 
     114                 :     double dfMinX;
     115                 :     double dfMaxX;
     116                 :     double dfMinY;
     117                 :     double dfMaxY;
     118                 :     double dfMinZ;
     119                 :     double dfMaxZ;
     120                 : 
     121                 :     vsi_l_offset *panLineOffset;
     122                 :   int nLastReadLine;
     123                 : 
     124                 :     double *padfRowMinZ;
     125                 :     double *padfRowMaxZ;
     126                 :     int nMinZRow;
     127                 :     int nMaxZRow;
     128                 : 
     129                 :     CPLErr ScanForMinMaxZ();
     130                 : 
     131                 :   public:
     132                 : 
     133                 :         GSAGRasterBand( GSAGDataset *, int, vsi_l_offset );
     134                 :         ~GSAGRasterBand();
     135                 :     
     136                 :     CPLErr IReadBlock( int, int, void * );
     137                 :     CPLErr IWriteBlock( int, int, void * );
     138                 : 
     139                 :     double GetNoDataValue( int *pbSuccess = NULL );
     140                 :     double GetMinimum( int *pbSuccess = NULL );
     141                 :     double GetMaximum( int *pbSuccess = NULL );
     142                 : };
     143                 : 
     144                 : /************************************************************************/
     145                 : /*                            AlmostEqual()                             */
     146                 : /* This function is needed because in release mode "1.70141E+38" is not */
     147                 : /* parsed as 1.70141E+38 in the last bit of the mantissa.   */
     148                 : /* See http://gcc.gnu.org/ml/gcc/2003-08/msg01195.html for some   */
     149                 : /* explanation.               */
     150                 : /************************************************************************/
     151                 : 
     152             400 : bool AlmostEqual( double dfVal1, double dfVal2 )
     153                 : 
     154                 : {
     155             400 :     const double dfTOLERANCE = 0.0000000001;
     156             400 :     if( dfVal1 == 0.0 || dfVal2 == 0.0 )
     157               0 :   return fabs(dfVal1 - dfVal2) < dfTOLERANCE;
     158             400 :     return fabs((dfVal1 - dfVal2)/dfVal1) < dfTOLERANCE;
     159                 : }
     160                 : 
     161                 : /************************************************************************/
     162                 : /*                           GSAGRasterBand()                           */
     163                 : /************************************************************************/
     164                 : 
     165              15 : GSAGRasterBand::GSAGRasterBand( GSAGDataset *poDS, int nBand,
     166                 :         vsi_l_offset nDataStart ) :
     167                 :     padfRowMinZ(NULL),
     168                 :     padfRowMaxZ(NULL),
     169                 :     nMinZRow(-1),
     170              15 :     nMaxZRow(-1)
     171                 : 
     172                 : {
     173              15 :     this->poDS = poDS;
     174              15 :     nBand = nBand;
     175                 :     
     176              15 :     eDataType = GDT_Float64;
     177                 : 
     178              15 :     nBlockXSize = poDS->GetRasterXSize();
     179              15 :     nBlockYSize = 1;
     180                 : 
     181                 :     panLineOffset =
     182              15 :   (vsi_l_offset *)VSICalloc( poDS->nRasterYSize+1, sizeof(vsi_l_offset) );
     183              15 :     if( panLineOffset == NULL )
     184                 :     {
     185                 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     186                 :                  "GSAGRasterBand::GSAGRasterBand : Out of memory allocating %d * %d bytes",
     187               0 :                  (int) poDS->nRasterYSize+1, (int) sizeof(vsi_l_offset) );
     188               0 :   return;
     189                 :     }
     190                 : 
     191              15 :   panLineOffset[poDS->nRasterYSize-1] = nDataStart;
     192              15 :   nLastReadLine = poDS->nRasterYSize;
     193               0 : }
     194                 : 
     195                 : /************************************************************************/
     196                 : /*                          ~GSAGRasterBand()                           */
     197                 : /************************************************************************/
     198                 : 
     199              30 : GSAGRasterBand::~GSAGRasterBand()
     200                 : {
     201              15 :     CPLFree( panLineOffset );
     202              15 :     if( padfRowMinZ != NULL )
     203               0 :   CPLFree( padfRowMinZ );
     204              15 :     if( padfRowMaxZ != NULL )
     205               0 :   CPLFree( padfRowMaxZ );
     206              30 : }
     207                 : 
     208                 : /************************************************************************/
     209                 : /*                           ScanForMinMaxZ()                           */
     210                 : /************************************************************************/
     211                 : 
     212               0 : CPLErr GSAGRasterBand::ScanForMinMaxZ()
     213                 : 
     214                 : {
     215               0 :     double *padfRowValues = (double *)VSIMalloc2( nBlockXSize, sizeof(double) );
     216               0 :     if( padfRowValues == NULL )
     217                 :     {
     218                 :   CPLError( CE_Failure, CPLE_OutOfMemory,
     219               0 :       "Unable to allocate memory for grid row values.\n" );
     220               0 :   return CE_Failure;
     221                 :     }
     222                 : 
     223               0 :     double dfNewMinZ = DBL_MAX;
     224               0 :     double dfNewMaxZ = -DBL_MAX;
     225               0 :     int nNewMinZRow = 0;
     226               0 :     int nNewMaxZRow = 0;
     227                 : 
     228                 :     /* Since we have to scan, lets calc. statistics too */
     229               0 :     double dfSum = 0.0;
     230               0 :     double dfSum2 = 0.0;
     231               0 :     unsigned long nValuesRead = 0;
     232               0 :     for( int iRow=0; iRow<nRasterYSize; iRow++ )
     233                 :     {
     234               0 :   CPLErr eErr = IReadBlock( 0, iRow, padfRowValues );
     235               0 :   if( eErr != CE_None )
     236                 :   {
     237               0 :       VSIFree( padfRowValues );
     238               0 :       return eErr;
     239                 :   }
     240                 : 
     241               0 :   padfRowMinZ[iRow] = DBL_MAX;
     242               0 :   padfRowMaxZ[iRow] = -DBL_MAX;
     243               0 :   for( int iCell=0; iCell<nRasterXSize; iCell++ )
     244                 :   {
     245               0 :       if( AlmostEqual(padfRowValues[iCell], GSAGDataset::dfNODATA_VALUE) )
     246               0 :     continue;
     247                 : 
     248               0 :       if( padfRowValues[iCell] < padfRowMinZ[iRow] )
     249               0 :     padfRowMinZ[iRow] = padfRowValues[iCell];
     250                 : 
     251               0 :       if( padfRowValues[iCell] > padfRowMaxZ[iRow] )
     252               0 :     padfRowMaxZ[iRow] = padfRowValues[iCell];
     253                 : 
     254               0 :       dfSum += padfRowValues[iCell];
     255               0 :       dfSum2 += padfRowValues[iCell] * padfRowValues[iCell];
     256               0 :       nValuesRead++;
     257                 :   }
     258                 : 
     259               0 :   if( padfRowMinZ[iRow] < dfNewMinZ )
     260                 :   {
     261               0 :       dfNewMinZ = padfRowMinZ[iRow];
     262               0 :       nNewMinZRow = iRow;
     263                 :   }
     264                 : 
     265               0 :   if( padfRowMaxZ[iRow] > dfNewMaxZ )
     266                 :   {
     267               0 :       dfNewMaxZ = padfRowMaxZ[iRow];
     268               0 :       nNewMaxZRow = iRow;
     269                 :   }
     270                 :     }
     271                 : 
     272               0 :     VSIFree( padfRowValues );
     273                 : 
     274               0 :     if( nValuesRead == 0 )
     275                 :     {
     276               0 :   dfMinZ = 0.0;
     277               0 :   dfMaxZ = 0.0;
     278               0 :   nMinZRow = 0;
     279               0 :   nMaxZRow = 0;
     280               0 :   return CE_None;
     281                 :     }
     282                 : 
     283               0 :     dfMinZ = dfNewMinZ;
     284               0 :     dfMaxZ = dfNewMaxZ;
     285               0 :     nMinZRow = nNewMinZRow;
     286               0 :     nMaxZRow = nNewMaxZRow;
     287                 : 
     288               0 :     double dfMean = dfSum / nValuesRead;
     289               0 :     double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean));
     290               0 :     SetStatistics( dfMinZ, dfMaxZ, dfMean, dfStdDev );
     291                 : 
     292               0 :     return CE_None;
     293                 : }
     294                 : 
     295                 : /************************************************************************/
     296                 : /*                             IReadBlock()                             */
     297                 : /************************************************************************/
     298                 : 
     299             156 : CPLErr GSAGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
     300                 :            void * pImage )
     301                 : {
     302                 :     static size_t nMaxLineSize = 128;
     303             156 :     double *pdfImage = (double *)pImage;
     304             156 :     GSAGDataset *poGDS = (GSAGDataset *)poDS;
     305                 : 
     306             156 :     assert( poGDS != NULL );
     307                 : 
     308             156 :     if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 )
     309               0 :         return CE_Failure;
     310                 : 
     311             156 :     if( panLineOffset[nBlockYOff] == 0 )
     312                 :     {
     313                 :         // Discover the last read block
     314              80 :         for ( int iFoundLine = nLastReadLine - 1; iFoundLine > nBlockYOff; iFoundLine--)
     315                 :         {
     316              76 :             IReadBlock( nBlockXOff, iFoundLine, NULL);
     317                 :         }
     318                 :     }
     319                 : 
     320             156 :     if( panLineOffset[nBlockYOff] == 0 )
     321               0 :         return CE_Failure;
     322             156 :     if( VSIFSeekL( poGDS->fp, panLineOffset[nBlockYOff], SEEK_SET ) != 0 )
     323                 :     {
     324                 :         CPLError( CE_Failure, CPLE_FileIO,
     325                 :                   "Can't seek to offset %ld to read grid row %d.",
     326               0 :                   (long) panLineOffset[nBlockYOff], nBlockYOff );
     327               0 :         return CE_Failure;
     328                 :     }
     329                 : 
     330                 :     size_t nLineBufSize;
     331             156 :     char *szLineBuf = NULL;
     332                 :     size_t nCharsRead;
     333             156 :     size_t nCharsExamined = 0;
     334                 :     /* If we know the offsets, we can just read line directly */
     335             232 :     if( (nBlockYOff > 0) && ( panLineOffset[nBlockYOff-1] != 0 ) )
     336                 :     {
     337              76 :   assert(panLineOffset[nBlockYOff-1] > panLineOffset[nBlockYOff]);
     338              76 :   nLineBufSize = panLineOffset[nBlockYOff-1]
     339              76 :             - panLineOffset[nBlockYOff] + 1;
     340                 :     }
     341                 :     else
     342                 :     {
     343              80 :   nLineBufSize = nMaxLineSize;
     344                 :     }
     345                 : 
     346             156 :     szLineBuf = (char *)VSIMalloc( nLineBufSize );
     347             156 :     if( szLineBuf == NULL )
     348                 :     {
     349                 :   CPLError( CE_Failure, CPLE_OutOfMemory,
     350               0 :       "Unable to read block, unable to allocate line buffer.\n" );
     351               0 :   return CE_Failure;
     352                 :     }
     353                 : 
     354             156 :     nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp );
     355             156 :     if( nCharsRead == 0 )
     356                 :     {
     357               0 :   VSIFree( szLineBuf );
     358                 :   CPLError( CE_Failure, CPLE_FileIO,
     359                 :       "Can't read grid row %d at offset %ld.\n",
     360               0 :       nBlockYOff, (long) panLineOffset[nBlockYOff] );
     361               0 :   return CE_Failure;
     362                 :     }
     363             156 :     szLineBuf[nCharsRead] = '\0';
     364                 : 
     365             156 :     char *szStart = szLineBuf;
     366             156 :     char *szEnd = szStart;
     367            3276 :     for( int iCell=0; iCell<nBlockXSize; szStart = szEnd )
     368                 :     {
     369            3120 :   double dfValue = CPLStrtod( szStart, &szEnd );
     370            3120 :   if( szStart == szEnd )
     371                 :   {
     372                 :       /* No number found */
     373                 : 
     374                 :       /* Check if this was an expected failure */
     375               0 :       while( isspace( (unsigned char)*szStart ) )
     376               0 :     szStart++;
     377                 : 
     378                 :       /* Found sign at end of input, seek back to re-read it */
     379               0 :       if ( (*szStart == '-' || *szStart == '+') && *(szStart+1) == '\0' )
     380                 :       {
     381               0 :         if( VSIFSeekL( poGDS->fp, 
     382                 :                                VSIFTellL( poGDS->fp)-1, 
     383                 :                                SEEK_SET ) != 0 )
     384                 :         {
     385               0 :         VSIFree( szLineBuf );
     386                 :         CPLError( CE_Failure, CPLE_FileIO,
     387                 :             "Unable to seek in grid row %d "
     388                 :             "(offset %ld, seek %d).\n",
     389                 :             nBlockYOff, 
     390                 :                               (long) VSIFTellL(poGDS->fp),
     391               0 :             -1 );
     392                 : 
     393               0 :         return CE_Failure;
     394                 :     }
     395                 :       }
     396               0 :       else if( *szStart != '\0' )
     397                 :       {
     398               0 :     szEnd = szStart;
     399               0 :     while( !isspace( (unsigned char)*szEnd ) && *szEnd != '\0' )
     400               0 :         szEnd++;
     401               0 :     char cOldEnd = *szEnd;
     402               0 :     *szEnd = '\0';
     403                 : 
     404                 :     CPLError( CE_Warning, CPLE_FileIO,
     405                 :         "Unexpected value in grid row %d (expected floating "
     406                 :         "point value, found \"%s\").\n",
     407               0 :         nBlockYOff, szStart );
     408                 : 
     409               0 :     *szEnd = cOldEnd;
     410                 : 
     411               0 :     szEnd = szStart;
     412               0 :     while( !isdigit( *szEnd ) && *szEnd != '.' && *szEnd != '\0' )
     413               0 :         szEnd++;
     414                 : 
     415               0 :     continue;
     416                 :       }
     417               0 :       else if( static_cast<size_t>(szStart - szLineBuf) != nCharsRead )
     418                 :       {
     419                 :     CPLError( CE_Warning, CPLE_FileIO,
     420                 :         "Unexpected ASCII null-character in grid row %d at "
     421                 :         "offset %ld.\n", 
     422                 :                           nBlockYOff, 
     423               0 :                           (long) (szStart - szLineBuf) );
     424                 : 
     425               0 :     while( *szStart == '\0' &&
     426                 :                        static_cast<size_t>(szStart - szLineBuf) < nCharsRead )
     427               0 :         szStart++;
     428                 : 
     429               0 :     szEnd = szStart;
     430               0 :     continue;
     431                 :       }
     432                 : 
     433               0 :       nCharsExamined += szStart - szLineBuf;
     434               0 :       nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp );
     435               0 :       if( nCharsRead == 0 )
     436                 :       {
     437               0 :     VSIFree( szLineBuf );
     438                 :     CPLError( CE_Failure, CPLE_FileIO,
     439                 :         "Can't read portion of grid row %d at offset %ld.",
     440               0 :         nBlockYOff, (long) panLineOffset[nBlockYOff] );
     441               0 :     return CE_Failure;
     442                 :       }
     443               0 :       szLineBuf[nCharsRead] = '\0';
     444               0 :       szStart = szEnd = szLineBuf;
     445               0 :       continue;
     446                 :   }
     447            3120 :   else if( *szEnd == '\0'
     448                 :                  || (*szEnd == '.' && *(szEnd+1) == '\0')
     449                 :                  || (*szEnd == '-' && *(szEnd+1) == '\0')
     450                 :                  || (*szEnd == '+' && *(szEnd+1) == '\0')
     451                 :                  || (*szEnd == 'E' && *(szEnd+1) == '\0')
     452                 :                  || (*szEnd == 'E' && *(szEnd+1) == '-' && *(szEnd+2) == '\0')
     453                 :                  || (*szEnd == 'E' && *(szEnd+1) == '+' && *(szEnd+2) == '\0')
     454                 :                  || (*szEnd == 'e' && *(szEnd+1) == '\0')
     455                 :                  || (*szEnd == 'e' && *(szEnd+1) == '-' && *(szEnd+2) == '\0')
     456                 :                  || (*szEnd == 'e' && *(szEnd+1) == '+' && *(szEnd+2) == '\0'))
     457                 :   {
     458                 :       /* Number was interrupted by a nul character */
     459               0 :       while( *szEnd != '\0' )
     460               0 :     szEnd++;
     461                 : 
     462               0 :       if( static_cast<size_t>(szEnd - szLineBuf) != nCharsRead )
     463                 :       {
     464                 :     CPLError( CE_Warning, CPLE_FileIO,
     465                 :         "Unexpected ASCII null-character in grid row %d at "
     466                 :         "offset %ld.\n", 
     467                 :                           nBlockYOff, 
     468               0 :                           (long) (szStart - szLineBuf) );
     469                 : 
     470               0 :     while( *szEnd == '\0' &&
     471                 :            static_cast<size_t>(szStart - szLineBuf) < nCharsRead )
     472               0 :         szEnd++;
     473                 : 
     474               0 :     continue;
     475                 :       }
     476                 : 
     477                 :       /* End of buffer, could be interrupting a number */
     478               0 :       if( VSIFSeekL( poGDS->fp, szStart - szEnd, SEEK_CUR ) != 0 )
     479                 :       {
     480               0 :     VSIFree( szLineBuf );
     481                 :     CPLError( CE_Failure, CPLE_FileIO,
     482                 :         "Unable to seek in grid row %d (offset %ld, seek %d)"
     483                 :         ".\n", nBlockYOff, 
     484                 :                           (long) VSIFTellL(poGDS->fp),
     485               0 :         (int) (szStart - szEnd) );
     486                 : 
     487               0 :     return CE_Failure;
     488                 :       }
     489               0 :       nCharsExamined += szStart - szLineBuf;
     490               0 :       nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp );
     491               0 :       szLineBuf[nCharsRead] = '\0';
     492                 : 
     493               0 :       if( nCharsRead == 0 )
     494                 :       {
     495               0 :     VSIFree( szLineBuf );
     496                 :     CPLError( CE_Failure, CPLE_FileIO,
     497                 :         "Can't read portion of grid row %d at offset %ld.",
     498               0 :         nBlockYOff, (long) panLineOffset[nBlockYOff] );
     499               0 :     return CE_Failure;
     500                 :       }
     501               0 :       else if( nCharsRead > static_cast<size_t>(szEnd - szStart) )
     502                 :       {
     503                 :     /* Read new data, this was not really the end */
     504               0 :     szEnd = szStart = szLineBuf;
     505               0 :     continue;
     506                 :       }
     507                 : 
     508                 :       /* This is really the last value and has no tailing newline */
     509               0 :       szStart = szLineBuf;
     510               0 :       szEnd = szLineBuf + nCharsRead;
     511                 :   }
     512                 : 
     513            3120 :   if( pdfImage != NULL )
     514                 :   {
     515            1600 :       *(pdfImage+iCell) = dfValue;
     516                 :   }
     517                 : 
     518            3120 :   iCell++;
     519                 :     }
     520                 : 
     521             468 :     while( *szEnd == ' ' )
     522             156 :   szEnd++;
     523                 : 
     524             156 :     if( *szEnd != '\0' && *szEnd != poGDS->szEOL[0] )
     525                 :   CPLDebug( "GSAG", "Grid row %d does not end with a newline.  "
     526               0 :                   "Possible skew.\n", nBlockYOff );
     527                 : 
     528             986 :     while( isspace( (unsigned char)*szEnd ) )
     529             674 :   szEnd++;
     530                 : 
     531             156 :     nCharsExamined += szEnd - szLineBuf;
     532                 : 
     533             156 :     if( nCharsExamined >= nMaxLineSize )
     534               0 :   nMaxLineSize = nCharsExamined + 1;
     535                 : 
     536             156 :     if( nBlockYOff > 0 )
     537             152 :         panLineOffset[nBlockYOff - 1] = 
     538             152 :             panLineOffset[nBlockYOff] + nCharsExamined;
     539                 : 
     540             156 :     nLastReadLine = nBlockYOff;
     541                 : 
     542             156 :     VSIFree( szLineBuf );
     543                 : 
     544             156 :     return CE_None;
     545                 : }
     546                 : 
     547                 : /************************************************************************/
     548                 : /*                            IWriteBlock()                             */
     549                 : /************************************************************************/
     550                 : 
     551               0 : CPLErr GSAGRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
     552                 :             void * pImage )
     553                 : 
     554                 : {
     555               0 :     if( eAccess == GA_ReadOnly )
     556                 :     {
     557                 :   CPLError( CE_Failure, CPLE_NoWriteAccess,
     558               0 :       "Unable to write block, dataset opened read only.\n" );
     559               0 :   return CE_Failure;
     560                 :     }
     561                 : 
     562               0 :     if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 )
     563               0 :   return CE_Failure;
     564                 : 
     565               0 :     GSAGDataset *poGDS = (GSAGDataset *)poDS;
     566               0 :     assert( poGDS != NULL );
     567                 : 
     568               0 :     if( padfRowMinZ == NULL || padfRowMaxZ == NULL
     569                 :   || nMinZRow < 0 || nMaxZRow < 0 )
     570                 :     {
     571               0 :   padfRowMinZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) );
     572               0 :   if( padfRowMinZ == NULL )
     573                 :   {
     574                 :       CPLError( CE_Failure, CPLE_OutOfMemory,
     575               0 :           "Unable to allocate space for row minimums array.\n" );
     576               0 :       return CE_Failure;
     577                 :   }
     578                 : 
     579               0 :   padfRowMaxZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) );
     580               0 :   if( padfRowMaxZ == NULL )
     581                 :   {
     582               0 :       VSIFree( padfRowMinZ );
     583               0 :       padfRowMinZ = NULL;
     584                 :       CPLError( CE_Failure, CPLE_OutOfMemory,
     585               0 :           "Unable to allocate space for row maximums array.\n" );
     586               0 :       return CE_Failure;
     587                 :   }
     588                 : 
     589               0 :   CPLErr eErr = ScanForMinMaxZ();
     590               0 :   if( eErr != CE_None )
     591               0 :       return eErr;
     592                 :     }
     593                 : 
     594               0 :     if( panLineOffset[nBlockYOff+1] == 0 )
     595               0 :   IReadBlock( nBlockXOff, nBlockYOff, NULL );
     596                 : 
     597               0 :     if( panLineOffset[nBlockYOff+1] == 0 || panLineOffset[nBlockYOff] == 0 )
     598               0 :   return CE_Failure;
     599                 : 
     600               0 :     std::ostringstream ssOutBuf;
     601               0 :     ssOutBuf.precision( GSAGDataset::nFIELD_PRECISION );
     602               0 :     ssOutBuf.setf( std::ios::uppercase );
     603                 : 
     604               0 :     double *pdfImage = (double *)pImage;
     605               0 :     padfRowMinZ[nBlockYOff] = DBL_MAX;
     606               0 :     padfRowMaxZ[nBlockYOff] = -DBL_MAX;
     607               0 :     for( int iCell=0; iCell<nBlockXSize; )
     608                 :     {
     609               0 :   for( int iCol=0; iCol<10 && iCell<nBlockXSize; iCol++, iCell++ )
     610                 :   {
     611               0 :       if( AlmostEqual( pdfImage[iCell], GSAGDataset::dfNODATA_VALUE ) )
     612                 :       {
     613               0 :     if( pdfImage[iCell] < padfRowMinZ[nBlockYOff] )
     614               0 :         padfRowMinZ[nBlockYOff] = pdfImage[iCell];
     615                 : 
     616               0 :     if( pdfImage[iCell] > padfRowMaxZ[nBlockYOff] )
     617               0 :         padfRowMaxZ[nBlockYOff] = pdfImage[iCell];
     618                 :       }
     619                 : 
     620               0 :       ssOutBuf << pdfImage[iCell] << " ";
     621                 :   }
     622               0 :   ssOutBuf << poGDS->szEOL;
     623                 :     }
     624               0 :     ssOutBuf << poGDS->szEOL;
     625                 : 
     626               0 :     CPLString sOut = ssOutBuf.str();
     627               0 :     if( sOut.length() != panLineOffset[nBlockYOff+1]-panLineOffset[nBlockYOff] )
     628                 :     {
     629               0 :   int nShiftSize = sOut.length() - (panLineOffset[nBlockYOff+1]
     630               0 :             - panLineOffset[nBlockYOff]);
     631               0 :   if( nBlockYOff != poGDS->nRasterYSize
     632                 :       && GSAGDataset::ShiftFileContents( poGDS->fp,
     633                 :                  panLineOffset[nBlockYOff+1],
     634                 :                  nShiftSize,
     635                 :                  poGDS->szEOL ) != CE_None )
     636                 :   {
     637                 :       CPLError( CE_Failure, CPLE_FileIO,
     638                 :           "Failure writing block, "
     639               0 :           "unable to shift file contents.\n" );
     640               0 :       return CE_Failure;
     641                 :   }
     642                 : 
     643               0 :   for( size_t iLine=nBlockYOff+1;
     644                 :        iLine < static_cast<unsigned>(poGDS->nRasterYSize+1)
     645               0 :     && panLineOffset[iLine] != 0; iLine++ )
     646               0 :       panLineOffset[iLine] += nShiftSize;
     647                 :     }
     648                 : 
     649               0 :     if( VSIFSeekL( poGDS->fp, panLineOffset[nBlockYOff], SEEK_SET ) != 0 )
     650                 :     {
     651               0 :   CPLError( CE_Failure, CPLE_FileIO, "Unable to seek to grid line.\n" );
     652               0 :   return CE_Failure;
     653                 :     }
     654                 : 
     655               0 :     if( VSIFWriteL( sOut.c_str(), 1, sOut.length(),
     656                 :         poGDS->fp ) != sOut.length() )
     657                 :     {
     658               0 :   CPLError( CE_Failure, CPLE_FileIO, "Unable to write grid block.\n" );
     659               0 :   return CE_Failure;
     660                 :     }
     661                 : 
     662                 :     /* Update header as needed */
     663               0 :     bool bHeaderNeedsUpdate = false;
     664               0 :     if( nMinZRow == nBlockYOff && padfRowMinZ[nBlockYOff] > dfMinZ )
     665                 :     {
     666               0 :   double dfNewMinZ = -DBL_MAX;
     667               0 :   for( int iRow=0; iRow<nRasterYSize; iRow++ )
     668                 :   {
     669               0 :       if( padfRowMinZ[iRow] < dfNewMinZ )
     670                 :       {
     671               0 :     dfNewMinZ = padfRowMinZ[iRow];
     672               0 :     nMinZRow = iRow;
     673                 :       }
     674                 :   }
     675                 : 
     676               0 :   if( dfNewMinZ != dfMinZ )
     677                 :   {
     678               0 :       dfMinZ = dfNewMinZ;
     679               0 :       bHeaderNeedsUpdate = true;
     680                 :   }
     681                 :     }
     682                 : 
     683               0 :     if( nMaxZRow == nBlockYOff && padfRowMaxZ[nBlockYOff] < dfMaxZ )
     684                 :     {
     685               0 :   double dfNewMaxZ = -DBL_MAX;
     686               0 :   for( int iRow=0; iRow<nRasterYSize; iRow++ )
     687                 :   {
     688               0 :       if( padfRowMaxZ[iRow] > dfNewMaxZ )
     689                 :       {
     690               0 :     dfNewMaxZ = padfRowMaxZ[iRow];
     691               0 :     nMaxZRow = iRow;
     692                 :       }
     693                 :   }
     694                 : 
     695               0 :   if( dfNewMaxZ != dfMaxZ )
     696                 :   {
     697               0 :       dfMaxZ = dfNewMaxZ;
     698               0 :       bHeaderNeedsUpdate = true;
     699                 :   }
     700                 :     }
     701                 : 
     702               0 :     if( padfRowMinZ[nBlockYOff] < dfMinZ || padfRowMaxZ[nBlockYOff] > dfMaxZ )
     703                 :     {
     704               0 :   if( padfRowMinZ[nBlockYOff] < dfMinZ )
     705                 :   {
     706               0 :       dfMinZ = padfRowMinZ[nBlockYOff];
     707               0 :       nMinZRow = nBlockYOff;
     708                 :   }
     709                 : 
     710               0 :   if( padfRowMaxZ[nBlockYOff] > dfMaxZ )
     711                 :   {
     712               0 :       dfMaxZ = padfRowMaxZ[nBlockYOff];
     713               0 :       nMaxZRow = nBlockYOff;
     714                 :   }
     715                 : 
     716               0 :   bHeaderNeedsUpdate = true;
     717                 :     }
     718                 : 
     719               0 :     if( bHeaderNeedsUpdate && dfMaxZ > dfMinZ )
     720                 :     {
     721               0 :   CPLErr eErr = poGDS->UpdateHeader();
     722               0 :   return eErr;
     723                 :     }
     724                 : 
     725               0 :     return CE_None;
     726                 : }
     727                 : 
     728                 : /************************************************************************/
     729                 : /*                           GetNoDataValue()                           */
     730                 : /************************************************************************/
     731                 : 
     732               4 : double GSAGRasterBand::GetNoDataValue( int * pbSuccess )
     733                 : {
     734               4 :     if( pbSuccess )
     735               4 :         *pbSuccess = TRUE;
     736                 : 
     737               4 :     return GSAGDataset::dfNODATA_VALUE;
     738                 : }
     739                 : 
     740                 : /************************************************************************/
     741                 : /*                             GetMinimum()                             */
     742                 : /************************************************************************/
     743                 : 
     744               0 : double GSAGRasterBand::GetMinimum( int *pbSuccess )
     745                 : {
     746               0 :     if( pbSuccess )
     747               0 :         *pbSuccess = TRUE;
     748                 : 
     749               0 :     return dfMinZ;
     750                 : }
     751                 : 
     752                 : /************************************************************************/
     753                 : /*                             GetMaximum()                             */
     754                 : /************************************************************************/
     755                 : 
     756               0 : double GSAGRasterBand::GetMaximum( int *pbSuccess )
     757                 : {
     758               0 :     if( pbSuccess )
     759               0 :         *pbSuccess = TRUE;
     760                 : 
     761               0 :     return dfMaxZ;
     762                 : }
     763                 : 
     764                 : /************************************************************************/
     765                 : /* ==================================================================== */
     766                 : /*        GSAGDataset       */
     767                 : /* ==================================================================== */
     768                 : /************************************************************************/
     769                 : 
     770                 : /************************************************************************/
     771                 : /*                             GSAGDataset()                            */
     772                 : /************************************************************************/
     773                 : 
     774              15 : GSAGDataset::GSAGDataset( const char *pszEOL )
     775                 : 
     776                 : {
     777              15 :     if( pszEOL == NULL || EQUAL(pszEOL, "") )
     778                 :     {
     779               0 :   CPLDebug( "GSAG", "GSAGDataset() created with invalid EOL string.\n" );
     780               0 :   this->szEOL[0] = '\x0D';
     781               0 :   this->szEOL[1] = '\x0A';
     782               0 :   this->szEOL[2] = '\0';
     783                 :     }
     784                 :     else
     785                 :     {
     786              15 :         strncpy(this->szEOL, pszEOL, sizeof(this->szEOL));
     787              15 :   this->szEOL[sizeof(this->szEOL) - 1] = '\0';
     788                 :     }
     789              15 : }
     790                 : 
     791                 : /************************************************************************/
     792                 : /*                            ~GSAGDataset()                            */
     793                 : /************************************************************************/
     794                 : 
     795              30 : GSAGDataset::~GSAGDataset()
     796                 : 
     797                 : {
     798              15 :     FlushCache();
     799              15 :     if( fp != NULL )
     800              15 :         VSIFCloseL( fp );
     801              30 : }
     802                 : 
     803                 : /************************************************************************/
     804                 : /*                                Open()                                */
     805                 : /************************************************************************/
     806                 : 
     807            8827 : GDALDataset *GSAGDataset::Open( GDALOpenInfo * poOpenInfo )
     808                 : 
     809                 : {
     810                 :     /* Check for signature */
     811            8842 :     if( poOpenInfo->nHeaderBytes < 5
     812                 :   || !EQUALN((const char *) poOpenInfo->pabyHeader,"DSAA",4)
     813              15 :   || ( poOpenInfo->pabyHeader[4] != '\x0D'
     814               0 :        && poOpenInfo->pabyHeader[4] != '\x0A' ))
     815                 :     {
     816            8812 :         return NULL;
     817                 :     }
     818                 : 
     819                 :     /* Identify the end of line marker (should be \x0D\x0A, but try some others)
     820                 :      * (note that '\x0D' == '\r' and '\x0A' == '\n' on most systems) */
     821                 :     char szEOL[3];
     822              15 :     szEOL[0] = poOpenInfo->pabyHeader[4];
     823              15 :     szEOL[1] = poOpenInfo->pabyHeader[5];
     824              15 :     szEOL[2] = '\0';
     825              15 :     if( szEOL[1] != '\x0D' && szEOL[1] != '\x0A' )
     826               0 :   szEOL[1] = '\0';
     827                 : 
     828                 : /* -------------------------------------------------------------------- */
     829                 : /*      Create a corresponding GDALDataset.                             */
     830                 : /* -------------------------------------------------------------------- */
     831              15 :     GSAGDataset *poDS = new GSAGDataset( szEOL );
     832                 : 
     833                 : /* -------------------------------------------------------------------- */
     834                 : /*      Open file with large file API.                                  */
     835                 : /* -------------------------------------------------------------------- */
     836                 : 
     837              15 :     poDS->eAccess = poOpenInfo->eAccess;
     838              15 :     if( poOpenInfo->eAccess == GA_ReadOnly )
     839               3 :   poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
     840                 :     else
     841              12 :   poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
     842                 : 
     843              15 :     if( poDS->fp == NULL )
     844                 :     {
     845                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     846                 :                   "VSIFOpenL(%s) failed unexpectedly.", 
     847               0 :                   poOpenInfo->pszFilename );
     848               0 :   delete poDS;
     849               0 :         return NULL;
     850                 :     }
     851                 :  
     852                 : /* -------------------------------------------------------------------- */
     853                 : /*      Read the header.                                                */
     854                 : /* -------------------------------------------------------------------- */
     855                 :     char *pabyHeader;
     856              15 :     bool bMustFreeHeader = false;
     857              15 :     if( poOpenInfo->nHeaderBytes >= static_cast<int>(nMAX_HEADER_SIZE) )
     858                 :     {
     859              15 :   pabyHeader = (char *)poOpenInfo->pabyHeader;
     860                 :     }
     861                 :     else
     862                 :     {
     863               0 :   bMustFreeHeader = true;
     864               0 :   pabyHeader = (char *)VSIMalloc( nMAX_HEADER_SIZE );
     865               0 :   if( pabyHeader == NULL )
     866                 :   {
     867                 :       CPLError( CE_Failure, CPLE_OutOfMemory,
     868               0 :           "Unable to open dataset, unable to header buffer.\n" );
     869               0 :       return NULL;
     870                 :   }
     871                 : 
     872               0 :   size_t nRead = VSIFReadL( pabyHeader, 1, nMAX_HEADER_SIZE-1, poDS->fp );
     873               0 :   pabyHeader[nRead] = '\0';
     874                 :     }
     875                 : 
     876              15 :     const char *szErrorMsg = NULL;
     877              15 :     const char *szStart = pabyHeader + 5;
     878                 :     char *szEnd;
     879                 :     double dfTemp;
     880                 :     double dfMinX;
     881                 :     double dfMaxX;
     882                 :     double dfMinY;
     883                 :     double dfMaxY;
     884                 :     double dfMinZ;
     885                 :     double dfMaxZ;
     886                 : 
     887                 :     /* Parse number of X axis grid rows */
     888              15 :     long nTemp = strtol( szStart, &szEnd, 10 );
     889              15 :     if( szStart == szEnd || nTemp < 0l )
     890                 :     {
     891               0 :   szErrorMsg = "Unable to parse the number of X axis grid columns.\n";
     892               0 :   goto error;
     893                 :     }
     894                 :     else if( nTemp > INT_MAX )
     895                 :     {
     896                 :   CPLError( CE_Warning, CPLE_AppDefined,
     897                 :       "Number of X axis grid columns not representable.\n" );
     898                 :   poDS->nRasterXSize = INT_MAX;
     899                 :     }
     900              15 :     else if ( nTemp == 0 )
     901                 :     {
     902               0 :         szErrorMsg = "Number of X axis grid columns is zero, which is invalid.\n";
     903               0 :         goto error;
     904                 :     }
     905                 :     else
     906                 :     {
     907              15 :   poDS->nRasterXSize = static_cast<int>(nTemp);
     908                 :     }
     909              15 :     szStart = szEnd;
     910                 : 
     911                 :     /* Parse number of Y axis grid rows */
     912              15 :     nTemp = strtol( szStart, &szEnd, 10 );
     913              15 :     if( szStart == szEnd || nTemp < 0l )
     914                 :     {
     915               0 :   szErrorMsg = "Unable to parse the number of Y axis grid rows.\n";
     916               0 :   goto error;
     917                 :     }
     918                 :     else if( nTemp > INT_MAX )
     919                 :     {
     920                 :   CPLError( CE_Warning, CPLE_AppDefined,
     921                 :       "Number of Y axis grid rows not representable.\n" );
     922                 :   poDS->nRasterYSize = INT_MAX;
     923                 :     }
     924              15 :     else if ( nTemp == 0)
     925                 :     {
     926               0 :         szErrorMsg = "Number of Y axis grid rows is zero, which is invalid.\n";
     927               0 :         goto error;
     928                 :     }
     929                 :     else
     930                 :     {
     931              15 :   poDS->nRasterYSize = static_cast<int>(nTemp);
     932                 :     }
     933              15 :     szStart = szEnd;
     934                 : 
     935                 :     /* Parse the minimum X value of the grid */
     936              15 :     dfTemp = CPLStrtod( szStart, &szEnd );
     937              15 :     if( szStart == szEnd )
     938                 :     {
     939               0 :   szErrorMsg = "Unable to parse the minimum X value.\n";
     940               0 :   goto error;
     941                 :     }
     942                 :     else
     943                 :     {
     944              15 :   dfMinX = dfTemp;
     945                 :     }
     946              15 :     szStart = szEnd;
     947                 : 
     948                 :     /* Parse the maximum X value of the grid */
     949              15 :     dfTemp = CPLStrtod( szStart, &szEnd );
     950              15 :     if( szStart == szEnd )
     951                 :     {
     952               0 :   szErrorMsg = "Unable to parse the maximum X value.\n";
     953               0 :   goto error;
     954                 :     }
     955                 :     else
     956                 :     {
     957              15 :   dfMaxX = dfTemp;
     958                 :     }
     959              15 :     szStart = szEnd;
     960                 : 
     961                 :     /* Parse the minimum Y value of the grid */
     962              15 :     dfTemp = CPLStrtod( szStart, &szEnd );
     963              15 :     if( szStart == szEnd )
     964                 :     {
     965               0 :   szErrorMsg = "Unable to parse the minimum Y value.\n";
     966               0 :   goto error;
     967                 :     }
     968                 :     else
     969                 :     {
     970              15 :   dfMinY = dfTemp;
     971                 :     }
     972              15 :     szStart = szEnd;
     973                 : 
     974                 :     /* Parse the maximum Y value of the grid */
     975              15 :     dfTemp = CPLStrtod( szStart, &szEnd );
     976              15 :     if( szStart == szEnd )
     977                 :     {
     978               0 :   szErrorMsg = "Unable to parse the maximum Y value.\n";
     979               0 :   goto error;
     980                 :     }
     981                 :     else
     982                 :     {
     983              15 :   dfMaxY = dfTemp;
     984                 :     }
     985              15 :     szStart = szEnd;
     986                 : 
     987                 :     /* Parse the minimum Z value of the grid */
     988              60 :     while( isspace( (unsigned char)*szStart ) )
     989              30 :   szStart++;
     990              15 :     poDS->nMinMaxZOffset = szStart - pabyHeader;
     991                 : 
     992              15 :     dfTemp = CPLStrtod( szStart, &szEnd );
     993              15 :     if( szStart == szEnd )
     994                 :     {
     995               0 :   szErrorMsg = "Unable to parse the minimum Z value.\n";
     996               0 :   goto error;
     997                 :     }
     998                 :     else
     999                 :     {
    1000              15 :   dfMinZ = dfTemp;
    1001                 :     }
    1002              15 :     szStart = szEnd;
    1003                 : 
    1004                 :     /* Parse the maximum Z value of the grid */
    1005              15 :     dfTemp = CPLStrtod( szStart, &szEnd );
    1006              15 :     if( szStart == szEnd )
    1007                 :     {
    1008               0 :   szErrorMsg = "Unable to parse the maximum Z value.\n";
    1009               0 :   goto error;
    1010                 :     }
    1011                 :     else
    1012                 :     {
    1013              15 :   dfMaxZ = dfTemp;
    1014                 :     }
    1015                 : 
    1016              60 :     while( isspace((unsigned char)*szEnd) )
    1017              30 :       szEnd++;
    1018                 : 
    1019                 : /* -------------------------------------------------------------------- */
    1020                 : /*      Create band information objects.                                */
    1021                 : /* -------------------------------------------------------------------- */
    1022                 :     {
    1023              15 :     GSAGRasterBand *poBand = new GSAGRasterBand( poDS, 1, szEnd-pabyHeader );
    1024              15 :     if( poBand->panLineOffset == NULL )
    1025                 :     {
    1026               0 :         delete poBand;
    1027               0 :         goto error;
    1028                 :     }
    1029                 : 
    1030              15 :     poBand->dfMinX = dfMinX;
    1031              15 :     poBand->dfMaxX = dfMaxX;
    1032              15 :     poBand->dfMinY = dfMinY;
    1033              15 :     poBand->dfMaxY = dfMaxY;
    1034              15 :     poBand->dfMinZ = dfMinZ;
    1035              15 :     poBand->dfMaxZ = dfMaxZ;
    1036                 : 
    1037              15 :     poDS->SetBand( 1, poBand );
    1038                 :     }
    1039                 : 
    1040              15 :     if( bMustFreeHeader )
    1041                 :     {
    1042               0 :   CPLFree( pabyHeader );
    1043                 :     }
    1044                 : 
    1045                 : /* -------------------------------------------------------------------- */
    1046                 : /*      Initialize any PAM information.                                 */
    1047                 : /* -------------------------------------------------------------------- */
    1048              15 :     poDS->SetDescription( poOpenInfo->pszFilename );
    1049              15 :     poDS->TryLoadXML();
    1050                 : 
    1051              15 :     return( poDS );
    1052                 : 
    1053                 : error:
    1054               0 :     if ( bMustFreeHeader )
    1055                 :     {
    1056               0 :   CPLFree( pabyHeader );
    1057                 :     }
    1058                 : 
    1059               0 :     delete poDS;
    1060                 : 
    1061               0 :     if (szErrorMsg)
    1062               0 :         CPLError( CE_Failure, CPLE_AppDefined, "%s", szErrorMsg );
    1063               0 :     return NULL;
    1064                 : }
    1065                 : 
    1066                 : /************************************************************************/
    1067                 : /*                          GetGeoTransform()                           */
    1068                 : /************************************************************************/
    1069                 : 
    1070               4 : CPLErr GSAGDataset::GetGeoTransform( double *padfGeoTransform )
    1071                 : {
    1072               4 :     if( padfGeoTransform == NULL )
    1073               0 :   return CE_Failure;
    1074                 : 
    1075               4 :     GSAGRasterBand *poGRB = (GSAGRasterBand *)GetRasterBand( 1 );
    1076                 : 
    1077               4 :     if( poGRB == NULL )
    1078                 :     {
    1079               0 :   padfGeoTransform[0] = 0;
    1080               0 :   padfGeoTransform[1] = 1;
    1081               0 :   padfGeoTransform[2] = 0;
    1082               0 :   padfGeoTransform[3] = 0;
    1083               0 :   padfGeoTransform[4] = 0;
    1084               0 :   padfGeoTransform[5] = 1;
    1085               0 :   return CE_Failure;
    1086                 :     }
    1087                 : 
    1088                 :     /* check if we have a PAM GeoTransform stored */
    1089               4 :     CPLPushErrorHandler( CPLQuietErrorHandler );
    1090               4 :     CPLErr eErr = GDALPamDataset::GetGeoTransform( padfGeoTransform );
    1091               4 :     CPLPopErrorHandler();
    1092                 : 
    1093               4 :     if( eErr == CE_None )
    1094               0 :   return CE_None;
    1095                 : 
    1096                 :     /* calculate pixel size first */
    1097               4 :     padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX)/(nRasterXSize - 1);
    1098               4 :     padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY)/(nRasterYSize - 1);
    1099                 : 
    1100                 :     /* then calculate image origin */
    1101               4 :     padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2;
    1102               4 :     padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2;
    1103                 : 
    1104                 :     /* tilt/rotation does not supported by the GS grids */
    1105               4 :     padfGeoTransform[4] = 0.0;
    1106               4 :     padfGeoTransform[2] = 0.0;
    1107                 : 
    1108               4 :     return CE_None;
    1109                 : }
    1110                 : 
    1111                 : /************************************************************************/
    1112                 : /*                          SetGeoTransform()                           */
    1113                 : /************************************************************************/
    1114                 : 
    1115               0 : CPLErr GSAGDataset::SetGeoTransform( double *padfGeoTransform )
    1116                 : {
    1117               0 :     if( eAccess == GA_ReadOnly )
    1118                 :     {
    1119                 :   CPLError( CE_Failure, CPLE_NoWriteAccess,
    1120               0 :       "Unable to set GeoTransform, dataset opened read only.\n" );
    1121               0 :   return CE_Failure;
    1122                 :     }
    1123                 : 
    1124               0 :     GSAGRasterBand *poGRB = (GSAGRasterBand *)GetRasterBand( 1 );
    1125                 : 
    1126               0 :     if( poGRB == NULL || padfGeoTransform == NULL)
    1127               0 :   return CE_Failure;
    1128                 : 
    1129                 :     /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
    1130               0 :     CPLErr eErr = CE_None;
    1131                 :     /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0
    1132                 :   || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 )
    1133                 :   eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform );*/
    1134                 : 
    1135               0 :     if( eErr != CE_None )
    1136               0 :   return eErr;
    1137                 : 
    1138               0 :     double dfOldMinX = poGRB->dfMinX;
    1139               0 :     double dfOldMaxX = poGRB->dfMaxX;
    1140               0 :     double dfOldMinY = poGRB->dfMinY;
    1141               0 :     double dfOldMaxY = poGRB->dfMaxY;
    1142                 : 
    1143               0 :     poGRB->dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
    1144                 :     poGRB->dfMaxX =
    1145               0 :         padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0];
    1146                 :     poGRB->dfMinY =
    1147               0 :         padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
    1148               0 :     poGRB->dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2;
    1149                 : 
    1150               0 :     eErr = UpdateHeader();
    1151                 : 
    1152               0 :     if( eErr != CE_None )
    1153                 :     {
    1154               0 :   poGRB->dfMinX = dfOldMinX;
    1155               0 :   poGRB->dfMaxX = dfOldMaxX;
    1156               0 :   poGRB->dfMinY = dfOldMinY;
    1157               0 :   poGRB->dfMaxY = dfOldMaxY;
    1158                 :     }
    1159                 : 
    1160               0 :     return eErr;
    1161                 : }
    1162                 : 
    1163                 : /************************************************************************/
    1164                 : /*                         ShiftFileContents()                          */
    1165                 : /************************************************************************/
    1166              12 : CPLErr GSAGDataset::ShiftFileContents( FILE *fp, vsi_l_offset nShiftStart,
    1167                 :                int nShiftSize, const char *pszEOL )
    1168                 : {
    1169                 :     /* nothing to do for zero-shift */
    1170              12 :     if( nShiftSize == 0 )
    1171               0 :   return CE_None;
    1172                 : 
    1173                 :     /* make sure start location is sane */
    1174              12 :     if( nShiftStart < 0
    1175                 :   || (nShiftSize < 0
    1176                 :       && nShiftStart < static_cast<vsi_l_offset>(-nShiftSize)) )
    1177               0 :   nShiftStart = (nShiftSize > 0) ? 0 : -nShiftSize;
    1178                 : 
    1179                 :     /* get offset at end of file */
    1180              12 :     if( VSIFSeekL( fp, 0, SEEK_END ) != 0 )
    1181                 :     {
    1182                 :   CPLError( CE_Failure, CPLE_FileIO,
    1183               0 :       "Unable to seek to end of grid file.\n" );
    1184               0 :   return CE_Failure;
    1185                 :     }
    1186                 : 
    1187              12 :     vsi_l_offset nOldEnd = VSIFTellL( fp );
    1188                 : 
    1189                 :     /* If shifting past end, just zero-pad as necessary */
    1190              12 :     if( nShiftStart >= nOldEnd )
    1191                 :     {
    1192               0 :   if( nShiftSize < 0 )
    1193                 :   {
    1194               0 :       if( nShiftStart + nShiftSize >= nOldEnd )
    1195               0 :     return CE_None;
    1196                 : 
    1197               0 :       if( VSIFSeekL( fp, nShiftStart + nShiftSize, SEEK_SET ) != 0 )
    1198                 :       {
    1199                 :     CPLError( CE_Failure, CPLE_FileIO,
    1200               0 :         "Unable to seek near end of file.\n" );
    1201               0 :     return CE_Failure;
    1202                 :       }
    1203                 : 
    1204                 :       /* ftruncate()? */
    1205               0 :       for( vsi_l_offset nPos = nShiftStart + nShiftSize;
    1206                 :      nPos > nOldEnd; nPos++ )
    1207                 :       {
    1208               0 :     if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1209                 :     {
    1210                 :         CPLError( CE_Failure, CPLE_FileIO,
    1211                 :             "Unable to write padding to grid file "
    1212               0 :             "(Out of space?).\n" );
    1213               0 :         return CE_Failure;
    1214                 :     }
    1215                 :       }
    1216                 : 
    1217               0 :       return CE_None;
    1218                 :   }
    1219                 :   else
    1220                 :   {
    1221               0 :       for( vsi_l_offset nPos = nOldEnd;
    1222                 :      nPos < nShiftStart + nShiftSize; nPos++ )
    1223                 :       {
    1224               0 :     if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1225                 :     {
    1226                 :         CPLError( CE_Failure, CPLE_FileIO,
    1227                 :             "Unable to write padding to grid file "
    1228               0 :             "(Out of space?).\n" );
    1229               0 :         return CE_Failure;
    1230                 :     }
    1231                 :       }
    1232               0 :       return CE_None;
    1233                 :   }
    1234                 :     }
    1235                 : 
    1236                 :     /* prepare buffer for real shifting */
    1237              12 :     size_t nBufferSize = (1024 >= abs(nShiftSize)*2) ? 1024 : abs(nShiftSize)*2;
    1238              12 :     char *pabyBuffer = (char *)VSIMalloc( nBufferSize );
    1239              12 :     if( pabyBuffer == NULL)
    1240                 :     {
    1241                 :   CPLError( CE_Failure, CPLE_OutOfMemory,
    1242               0 :       "Unable to allocate space for shift buffer.\n" );
    1243               0 :   return CE_Failure;
    1244                 :     }
    1245                 : 
    1246              12 :     if( VSIFSeekL( fp, nShiftStart, SEEK_SET ) != 0 )
    1247                 :     {
    1248               0 :   VSIFree( pabyBuffer );
    1249                 :   CPLError( CE_Failure, CPLE_FileIO,
    1250               0 :       "Unable to seek to start of shift in grid file.\n" );
    1251               0 :   return CE_Failure;
    1252                 :     }
    1253                 : 
    1254                 :     size_t nRead;
    1255              12 :     size_t nOverlap = (nShiftSize > 0) ? nShiftSize : 0;
    1256                 :     /* If there is overlap, fill buffer with the overlap to start */
    1257              12 :     if( nOverlap > 0)
    1258                 :     {
    1259               0 :   nRead = VSIFReadL( (void *)pabyBuffer, 1, nOverlap, fp );
    1260               0 :   if( nRead < nOverlap && !VSIFEofL( fp ) )
    1261                 :   {
    1262               0 :       VSIFree( pabyBuffer );
    1263                 :       CPLError( CE_Failure, CPLE_FileIO,
    1264               0 :           "Error reading grid file.\n" );
    1265               0 :       return CE_Failure;
    1266                 :   }
    1267                 : 
    1268                 :   /* overwrite the new space with ' ' */
    1269               0 :       if( VSIFSeekL( fp, nShiftStart, SEEK_SET ) != 0 )
    1270                 :   {
    1271               0 :       VSIFree( pabyBuffer );
    1272                 :       CPLError( CE_Failure, CPLE_FileIO,
    1273               0 :           "Unable to seek to start of shift in grid file.\n" );
    1274               0 :       return CE_Failure;
    1275                 :   }
    1276                 : 
    1277               0 :   for( int iFill=0; iFill<nShiftSize; iFill++ )
    1278                 :   {
    1279               0 :       if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1280                 :       {
    1281               0 :     VSIFree( pabyBuffer );
    1282                 :     CPLError( CE_Failure, CPLE_FileIO,
    1283                 :         "Unable to write padding to grid file "
    1284               0 :         "(Out of space?).\n" );
    1285               0 :     return CE_Failure;
    1286                 :       }
    1287                 :   }
    1288                 : 
    1289                 :   /* if we have already read the entire file, finish it off */
    1290               0 :   if( VSIFTellL( fp ) >= nOldEnd )
    1291                 :   {
    1292               0 :       if( VSIFWriteL( (void *)pabyBuffer, 1, nRead, fp ) != nRead )
    1293                 :       {
    1294               0 :     VSIFree( pabyBuffer );
    1295                 :     CPLError( CE_Failure, CPLE_FileIO,
    1296               0 :         "Unable to write to grid file (Out of space?).\n" );
    1297               0 :     return CE_Failure;
    1298                 :       }
    1299                 : 
    1300               0 :       VSIFree( pabyBuffer );
    1301               0 :       return CE_None;
    1302                 :   }
    1303                 :     }
    1304                 : 
    1305                 :     /* iterate over the remainder of the file and shift as requested */
    1306              12 :     bool bEOF = false;
    1307              37 :     while( !bEOF )
    1308                 :     {
    1309                 :   nRead = VSIFReadL( (void *)(pabyBuffer+nOverlap), 1,
    1310              13 :          nBufferSize - nOverlap, fp );
    1311                 : 
    1312              13 :   bEOF = VSIFEofL( fp );
    1313                 : 
    1314              13 :   if( nRead == 0 && !bEOF )
    1315                 :   {
    1316               0 :       VSIFree( pabyBuffer );
    1317                 :       CPLError( CE_Failure, CPLE_FileIO,
    1318               0 :           "Unable to read from grid file (possible corruption).\n");
    1319               0 :       return CE_Failure;
    1320                 :   }
    1321                 : 
    1322                 :   /* FIXME:  Should use SEEK_CUR, review integer promotions... */
    1323              13 :   if( VSIFSeekL( fp, VSIFTellL(fp)-nRead+nShiftSize-nOverlap,
    1324                 :            SEEK_SET ) != 0 )
    1325                 :   {
    1326               0 :       VSIFree( pabyBuffer );
    1327                 :       CPLError( CE_Failure, CPLE_FileIO,
    1328               0 :           "Unable to seek in grid file (possible corruption).\n" );
    1329               0 :       return CE_Failure;
    1330                 :   }
    1331                 : 
    1332              13 :   size_t nWritten = VSIFWriteL( (void *)pabyBuffer, 1, nRead, fp );
    1333              13 :   if( nWritten != nRead )
    1334                 :   {
    1335               0 :       VSIFree( pabyBuffer );
    1336                 :       CPLError( CE_Failure, CPLE_FileIO,
    1337               0 :           "Unable to write to grid file (out of space?).\n" );
    1338               0 :       return CE_Failure;
    1339                 :   }
    1340                 : 
    1341                 :   /* shift overlapped contents to the front of the buffer if necessary */
    1342              13 :   if( nOverlap > 0)
    1343               0 :       memmove(pabyBuffer, pabyBuffer+nRead, nOverlap);
    1344                 :     }
    1345                 : 
    1346                 :     /* write the remainder of the buffer or overwrite leftovers and finish */
    1347              12 :     if( nShiftSize > 0 )
    1348                 :     {
    1349               0 :   size_t nTailSize = nOverlap;
    1350               0 :   while( nTailSize > 0 && isspace( (unsigned char)pabyBuffer[nTailSize-1] ) )
    1351               0 :       nTailSize--;
    1352                 : 
    1353               0 :   if( VSIFWriteL( (void *)pabyBuffer, 1, nTailSize, fp ) != nTailSize )
    1354                 :   {
    1355               0 :       VSIFree( pabyBuffer );
    1356                 :       CPLError( CE_Failure, CPLE_FileIO,
    1357               0 :           "Unable to write to grid file (out of space?).\n" );
    1358               0 :       return CE_Failure;
    1359                 :   }
    1360                 : 
    1361               0 :   if( VSIFWriteL( (void *)pszEOL, 1, strlen(pszEOL), fp )
    1362                 :             != strlen(pszEOL) )
    1363                 :   {
    1364               0 :       VSIFree( pabyBuffer );
    1365                 :       CPLError( CE_Failure, CPLE_FileIO,
    1366               0 :           "Unable to write to grid file (out of space?).\n" );
    1367               0 :       return CE_Failure;
    1368                 :   }
    1369                 :     }
    1370                 :     else
    1371                 :     {
    1372                 :   /* FIXME: ftruncate()? */
    1373                 :   /* FIXME:  Should use SEEK_CUR, review integer promotions... */
    1374              12 :   if( VSIFSeekL( fp, VSIFTellL(fp)-strlen(pszEOL), SEEK_SET ) != 0 )
    1375                 :   {
    1376               0 :       VSIFree( pabyBuffer );
    1377                 :       CPLError( CE_Failure, CPLE_FileIO,
    1378               0 :           "Unable to seek in grid file.\n" );
    1379               0 :       return CE_Failure;
    1380                 :   }
    1381                 : 
    1382             301 :   for( int iPadding=0; iPadding<-nShiftSize; iPadding++ )
    1383                 :   {
    1384             289 :       if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1385                 :       {
    1386               0 :     VSIFree( pabyBuffer );
    1387                 :     CPLError( CE_Failure, CPLE_FileIO,
    1388               0 :         "Error writing to grid file.\n" );
    1389               0 :     return CE_Failure;
    1390                 :       }
    1391                 :   }
    1392                 : 
    1393              12 :   if( VSIFWriteL( (void *)pszEOL, 1, strlen(pszEOL), fp )
    1394                 :             != strlen(pszEOL) )
    1395                 :   {
    1396               0 :       VSIFree( pabyBuffer );
    1397                 :       CPLError( CE_Failure, CPLE_FileIO,
    1398               0 :           "Unable to write to grid file (out of space?).\n" );
    1399               0 :       return CE_Failure;
    1400                 :   }
    1401                 :     }
    1402                 : 
    1403              12 :     VSIFree( pabyBuffer );
    1404              12 :     return CE_None;
    1405                 : }
    1406                 : 
    1407                 : /************************************************************************/
    1408                 : /*                             UpdateHeader()                           */
    1409                 : /************************************************************************/
    1410                 : 
    1411               0 : CPLErr GSAGDataset::UpdateHeader()
    1412                 : 
    1413                 : {
    1414               0 :     GSAGRasterBand *poBand = (GSAGRasterBand *)GetRasterBand( 1 );
    1415               0 :     if( poBand == NULL )
    1416                 :     {
    1417               0 :   CPLError( CE_Failure, CPLE_FileIO, "Unable to open raster band.\n" );
    1418               0 :   return CE_Failure;
    1419                 :     }
    1420                 : 
    1421               0 :     std::ostringstream ssOutBuf;
    1422               0 :     ssOutBuf.precision( nFIELD_PRECISION );
    1423               0 :     ssOutBuf.setf( std::ios::uppercase );
    1424                 : 
    1425                 :     /* signature */
    1426               0 :     ssOutBuf << "DSAA" << szEOL;
    1427                 : 
    1428                 :     /* columns rows */
    1429               0 :     ssOutBuf << nRasterXSize << " " << nRasterYSize << szEOL;
    1430                 : 
    1431                 :     /* x range */
    1432               0 :     ssOutBuf << poBand->dfMinX << " " << poBand->dfMaxX << szEOL;
    1433                 : 
    1434                 :     /* y range */
    1435               0 :     ssOutBuf << poBand->dfMinY << " " << poBand->dfMaxY << szEOL;
    1436                 : 
    1437                 :     /* z range */
    1438               0 :     ssOutBuf << poBand->dfMinZ << " " << poBand->dfMaxZ << szEOL;
    1439                 : 
    1440               0 :     CPLString sOut = ssOutBuf.str();
    1441               0 :     if( sOut.length() != poBand->panLineOffset[0] )
    1442                 :     {
    1443               0 :   int nShiftSize = sOut.length() - poBand->panLineOffset[0];
    1444               0 :   if( ShiftFileContents( fp, poBand->panLineOffset[0], nShiftSize,
    1445                 :              szEOL ) != CE_None )
    1446                 :   {
    1447                 :       CPLError( CE_Failure, CPLE_FileIO,
    1448                 :           "Unable to update grid header, "
    1449               0 :           "failure shifting file contents.\n" );
    1450               0 :       return CE_Failure;
    1451                 :   }
    1452                 : 
    1453               0 :   for( size_t iLine=0;
    1454                 :        iLine < static_cast<unsigned>(nRasterYSize+1)
    1455               0 :     && poBand->panLineOffset[iLine] != 0;
    1456                 :        iLine++ )
    1457               0 :       poBand->panLineOffset[iLine] += nShiftSize;
    1458                 :     }
    1459                 : 
    1460               0 :     if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 )
    1461                 :     {
    1462                 :   CPLError( CE_Failure, CPLE_FileIO,
    1463               0 :       "Unable to seek to start of grid file.\n" );
    1464               0 :   return CE_Failure;
    1465                 :     }
    1466                 : 
    1467               0 :     if( VSIFWriteL( sOut.c_str(), 1, sOut.length(), fp ) != sOut.length() )
    1468                 :     {
    1469                 :   CPLError( CE_Failure, CPLE_FileIO,
    1470               0 :       "Unable to update file header.  Disk full?\n" );
    1471               0 :   return CE_Failure;
    1472                 :     }
    1473                 : 
    1474               0 :     return CE_None;
    1475                 : }
    1476                 : 
    1477                 : /************************************************************************/
    1478                 : /*                             CreateCopy()                             */
    1479                 : /************************************************************************/
    1480                 : 
    1481              17 : GDALDataset *GSAGDataset::CreateCopy( const char *pszFilename,
    1482                 :               GDALDataset *poSrcDS,
    1483                 :               int bStrict, char **papszOptions,
    1484                 :               GDALProgressFunc pfnProgress,
    1485                 :               void *pProgressData )
    1486                 : {
    1487              17 :     if( pfnProgress == NULL )
    1488               0 :   pfnProgress = GDALDummyProgress;
    1489                 : 
    1490              17 :     int nBands = poSrcDS->GetRasterCount();
    1491              17 :     if (nBands == 0)
    1492                 :     {
    1493                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    1494               1 :                   "GSAG driver does not support source dataset with zero band.\n");
    1495               1 :         return NULL;
    1496                 :     }
    1497              16 :     else if (nBands > 1)
    1498                 :     {
    1499               4 :   if( bStrict )
    1500                 :   {
    1501                 :       CPLError( CE_Failure, CPLE_NotSupported,
    1502                 :           "Unable to create copy, Golden Software ASCII Grid "
    1503               4 :           "format only supports one raster band.\n" );
    1504               4 :       return NULL;
    1505                 :   }
    1506                 :   else
    1507                 :       CPLError( CE_Warning, CPLE_NotSupported,
    1508                 :           "Golden Software ASCII Grid format only supports one "
    1509               0 :           "raster band, first band will be copied.\n" );
    1510                 :     }
    1511                 : 
    1512              12 :     if( !pfnProgress( 0.0, NULL, pProgressData ) )
    1513                 :     {
    1514               0 :         CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated\n" );
    1515               0 :         return NULL;
    1516                 :     }
    1517                 : 
    1518              12 :     FILE *fp = VSIFOpenL( pszFilename, "w+b" );
    1519                 : 
    1520              12 :     if( fp == NULL )
    1521                 :     {
    1522                 :         CPLError( CE_Failure, CPLE_OpenFailed,
    1523                 :                   "Attempt to create file '%s' failed.\n",
    1524               0 :                   pszFilename );
    1525               0 :         return NULL;
    1526                 :     }
    1527                 : 
    1528              12 :     int          nXSize = poSrcDS->GetRasterXSize();
    1529              12 :     int          nYSize = poSrcDS->GetRasterYSize();
    1530                 :     double   adfGeoTransform[6];
    1531                 : 
    1532              12 :     poSrcDS->GetGeoTransform( adfGeoTransform );
    1533                 : 
    1534              12 :     std::ostringstream ssHeader;
    1535              12 :     ssHeader.precision( nFIELD_PRECISION );
    1536              12 :     ssHeader.setf( std::ios::uppercase );
    1537                 : 
    1538              12 :     ssHeader << "DSAA\x0D\x0A";
    1539                 : 
    1540              12 :     ssHeader << nXSize << " " << nYSize << "\x0D\x0A";
    1541                 : 
    1542              12 :     ssHeader << adfGeoTransform[0] + adfGeoTransform[1] / 2 << " "
    1543              24 :        << adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0]
    1544              36 :        << "\x0D\x0A";
    1545                 : 
    1546              24 :     ssHeader << adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3] << " "
    1547              12 :        << adfGeoTransform[3] + adfGeoTransform[5] / 2
    1548              36 :        << "\x0D\x0A";
    1549                 : 
    1550              12 :     if( VSIFWriteL( (void *)ssHeader.str().c_str(), 1, ssHeader.str().length(),
    1551                 :         fp ) != ssHeader.str().length() )
    1552                 :     {
    1553               0 :   VSIFCloseL( fp );
    1554                 :         CPLError( CE_Failure, CPLE_FileIO,
    1555               0 :                   "Unable to create copy, writing header failed.\n" );
    1556               0 :         return NULL;
    1557                 :     }
    1558                 : 
    1559                 :     /* Save the location and write placeholders for the min/max Z value */
    1560              12 :     vsi_l_offset nRangeStart = VSIFTellL( fp );
    1561              12 :     const char *szDummyRange = "0.0000000000001 0.0000000000001\x0D\x0A";
    1562              12 :     size_t nDummyRangeLen = strlen( szDummyRange );
    1563              12 :     if( VSIFWriteL( (void *)szDummyRange, 1, nDummyRangeLen,
    1564                 :         fp ) != nDummyRangeLen )
    1565                 :     {
    1566               0 :   VSIFCloseL( fp );
    1567                 :         CPLError( CE_Failure, CPLE_FileIO,
    1568               0 :                   "Unable to create copy, writing header failed.\n" );
    1569               0 :         return NULL;
    1570                 :     }
    1571                 : 
    1572                 : /* -------------------------------------------------------------------- */
    1573                 : /*      Copy band data.             */
    1574                 : /* -------------------------------------------------------------------- */
    1575              12 :     double *pdfData = (double *)VSIMalloc2( nXSize, sizeof( double ) );
    1576              12 :     if( pdfData == NULL )
    1577                 :     {
    1578               0 :   VSIFCloseL( fp );
    1579                 :   CPLError( CE_Failure, CPLE_OutOfMemory,
    1580               0 :       "Unable to create copy, unable to allocate line buffer.\n" );
    1581               0 :   return NULL;
    1582                 :     }
    1583                 : 
    1584              12 :     GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
    1585                 :     int bSrcHasNDValue;
    1586              12 :     double dfSrcNoDataValue = poSrcBand->GetNoDataValue( &bSrcHasNDValue );
    1587              12 :     double dfMin = DBL_MAX;
    1588              12 :     double dfMax = -DBL_MAX;
    1589             142 :     for( int iRow=0; iRow<nYSize; iRow++ )
    1590                 :     {
    1591                 :   CPLErr eErr = poSrcBand->RasterIO( GF_Read, 0, nYSize-iRow-1,
    1592                 :              nXSize, 1, pdfData,
    1593             130 :              nXSize, 1, GDT_Float64, 0, 0 );
    1594                 : 
    1595             130 :   if( eErr != CE_None )
    1596                 :   {
    1597               0 :       VSIFCloseL( fp );
    1598               0 :       VSIFree( pdfData );
    1599               0 :       return NULL;
    1600                 :   }
    1601                 : 
    1602             410 :   for( int iCol=0; iCol<nXSize; )
    1603                 :   {
    1604             150 :       for( int iCount=0;
    1605                 :      iCount<10 && iCol<nXSize;
    1606                 :      iCount++, iCol++ )
    1607                 :       {
    1608            1500 :     double dfValue = pdfData[iCol];
    1609                 : 
    1610            1500 :     if( bSrcHasNDValue && AlmostEqual( dfValue, dfSrcNoDataValue ) )
    1611                 :     {
    1612               0 :         dfValue = dfNODATA_VALUE;
    1613                 :     }
    1614                 :     else
    1615                 :     {
    1616            1500 :         if( dfValue > dfMax )
    1617              14 :       dfMax = dfValue;
    1618                 : 
    1619            1500 :         if( dfValue < dfMin )
    1620              19 :       dfMin = dfValue;
    1621                 :     }
    1622                 : 
    1623            1500 :     std::ostringstream ssOut;
    1624            1500 :     ssOut.precision(nFIELD_PRECISION);
    1625            1500 :     ssOut.setf( std::ios::uppercase );
    1626            1500 :     ssOut << dfValue << " ";
    1627            1500 :     CPLString sOut = ssOut.str();
    1628                 : 
    1629            1500 :     if( VSIFWriteL( sOut.c_str(), 1, sOut.length(), fp )
    1630                 :         != sOut.length() )
    1631                 :     {
    1632               0 :         VSIFCloseL( fp );
    1633               0 :         VSIFree( pdfData );
    1634                 :         CPLError( CE_Failure, CPLE_FileIO,
    1635               0 :             "Unable to write grid cell.  Disk full?\n" );
    1636               0 :         return NULL;
    1637                 :     }
    1638                 :       }
    1639                 : 
    1640             150 :       if( VSIFWriteL( (void *)"\x0D\x0A", 1, 2, fp ) != 2 )
    1641                 :       {
    1642               0 :     VSIFCloseL( fp );
    1643               0 :     VSIFree( pdfData );
    1644                 :     CPLError( CE_Failure, CPLE_FileIO,
    1645               0 :         "Unable to finish write of grid line. Disk full?\n" );
    1646               0 :     return NULL;
    1647                 :       }
    1648                 :   }
    1649                 : 
    1650             130 :   if( VSIFWriteL( (void *)"\x0D\x0A", 1, 2, fp ) != 2 )
    1651                 :   {
    1652               0 :       VSIFCloseL( fp );
    1653               0 :       VSIFree( pdfData );
    1654                 :       CPLError( CE_Failure, CPLE_FileIO,
    1655               0 :           "Unable to finish write of grid row. Disk full?\n" );
    1656               0 :       return NULL;
    1657                 :   }
    1658                 : 
    1659             130 :   if( !pfnProgress( static_cast<double>(iRow)/nYSize,
    1660                 :         NULL, pProgressData ) )
    1661                 :   {
    1662               0 :       VSIFCloseL( fp );
    1663               0 :       VSIFree( pdfData );
    1664               0 :       CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
    1665               0 :       return NULL;
    1666                 :   }
    1667                 :     }
    1668                 : 
    1669              12 :     VSIFree( pdfData );
    1670                 : 
    1671                 :     /* write out the min and max values */
    1672              12 :     std::ostringstream ssRange;
    1673              12 :     ssRange.precision( nFIELD_PRECISION );
    1674              12 :     ssRange.setf( std::ios::uppercase );
    1675              12 :     ssRange << dfMin << " " << dfMax << "\x0D\x0A";
    1676              12 :     if( ssRange.str().length() != nDummyRangeLen )
    1677                 :     {
    1678              12 :   int nShiftSize = ssRange.str().length() - nDummyRangeLen;
    1679              12 :   if( ShiftFileContents( fp, nRangeStart + nDummyRangeLen,
    1680                 :              nShiftSize, "\x0D\x0A" ) != CE_None )
    1681                 :   {
    1682               0 :       VSIFCloseL( fp );
    1683                 :       CPLError( CE_Failure, CPLE_FileIO,
    1684               0 :           "Unable to shift file contents.\n" );
    1685               0 :       return NULL;
    1686                 :   }
    1687                 :     }
    1688                 : 
    1689              12 :     if( VSIFSeekL( fp, nRangeStart, SEEK_SET ) != 0 )
    1690                 :     {
    1691               0 :   VSIFCloseL( fp );
    1692                 :   CPLError( CE_Failure, CPLE_FileIO,
    1693               0 :       "Unable to seek to start of grid file copy.\n" );
    1694               0 :   return NULL;
    1695                 :     }
    1696                 : 
    1697              12 :     if( VSIFWriteL( (void *)ssRange.str().c_str(), 1, ssRange.str().length(),
    1698                 :         fp ) != ssRange.str().length() )
    1699                 :     {
    1700               0 :   VSIFCloseL( fp );
    1701                 :         CPLError( CE_Failure, CPLE_FileIO,
    1702               0 :                   "Unable to write range information.\n" );
    1703               0 :         return NULL;
    1704                 :     }
    1705                 : 
    1706              12 :     VSIFCloseL( fp );
    1707                 : 
    1708                 :     GDALPamDataset *poDstDS = (GDALPamDataset *)GDALOpen( pszFilename,
    1709              12 :                 GA_Update );
    1710              12 :     if( poDstDS == NULL )
    1711                 :     {
    1712               0 :   VSIUnlink( pszFilename );
    1713                 :   CPLError( CE_Failure, CPLE_FileIO,
    1714               0 :       "Unable to open copy of dataset.\n" );
    1715               0 :   return NULL;
    1716                 :     }
    1717                 : 
    1718              12 :     GDALRasterBand *poDstBand = poDstDS->GetRasterBand(1);
    1719              12 :     if( poDstBand == NULL )
    1720                 :     {
    1721               0 :   VSIUnlink( pszFilename );
    1722               0 :   delete poDstDS;
    1723                 :   CPLError( CE_Failure, CPLE_FileIO,
    1724               0 :       "Unable to open copy of raster band?\n" );
    1725               0 :   return NULL;
    1726                 :     }
    1727                 : 
    1728                 : /* -------------------------------------------------------------------- */
    1729                 : /*      Attempt to copy metadata.         */
    1730                 : /* -------------------------------------------------------------------- */
    1731              12 :     if( !bStrict )
    1732               1 :   CPLPushErrorHandler( CPLQuietErrorHandler );
    1733                 : 
    1734                 :     /* non-zero transform 2 or 4 or negative 1 or 5  not supported natively */
    1735                 :     /*if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0
    1736                 :   || adfGeoTransform[1] < 0.0 || adfGeoTransform[5] < 0.0 )
    1737                 :   poDstDS->GDALPamDataset::SetGeoTransform( adfGeoTransform );*/
    1738                 : 
    1739              12 :     const char *szProjectionRef = poSrcDS->GetProjectionRef();
    1740              12 :     if( *szProjectionRef != '\0' )
    1741              11 :   poDstDS->SetProjection( szProjectionRef );
    1742                 : 
    1743              12 :     char **pszMetadata = poSrcDS->GetMetadata();
    1744              12 :     if( pszMetadata != NULL )
    1745              11 :   poDstDS->SetMetadata( pszMetadata );
    1746                 : 
    1747                 :     /* FIXME:  Should the dataset description be copied as well, or is it
    1748                 :      *         always the file name? */
    1749              12 :     poDstBand->SetDescription( poSrcBand->GetDescription() );
    1750                 : 
    1751                 :     int bSuccess;
    1752              12 :     double dfOffset = poSrcBand->GetOffset( &bSuccess );
    1753              12 :     if( bSuccess && dfOffset != 0.0 )
    1754               0 :   poDstBand->SetOffset( dfOffset );
    1755                 : 
    1756              12 :     double dfScale = poSrcBand->GetScale( &bSuccess );
    1757              12 :     if( bSuccess && dfScale != 1.0 )
    1758               0 :   poDstBand->SetScale( dfScale );
    1759                 : 
    1760              12 :     GDALColorInterp oColorInterp = poSrcBand->GetColorInterpretation();
    1761              12 :     if( oColorInterp != GCI_Undefined )
    1762              11 :         poDstBand->SetColorInterpretation( oColorInterp );
    1763                 : 
    1764              12 :     char **pszCatNames = poSrcBand->GetCategoryNames();
    1765              12 :     if( pszCatNames != NULL)
    1766               0 :   poDstBand->SetCategoryNames( pszCatNames );
    1767                 : 
    1768              12 :     GDALColorTable *poColorTable = poSrcBand->GetColorTable();
    1769              12 :     if( poColorTable != NULL )
    1770               0 :   poDstBand->SetColorTable( poColorTable );
    1771                 : 
    1772              12 :     if( !bStrict )
    1773               1 :   CPLPopErrorHandler();
    1774                 : 
    1775              12 :     return poDstDS;
    1776                 : }
    1777                 : 
    1778                 : /************************************************************************/
    1779                 : /*                               Delete()                               */
    1780                 : /************************************************************************/
    1781                 : 
    1782               1 : CPLErr GSAGDataset::Delete( const char *pszFilename )
    1783                 : 
    1784                 : {
    1785                 :     VSIStatBufL sStat;
    1786                 :     
    1787               1 :     if( VSIStatL( pszFilename, &sStat ) != 0 )
    1788                 :     {
    1789                 :   CPLError( CE_Failure, CPLE_FileIO,
    1790               0 :       "Unable to stat() %s.\n", pszFilename );
    1791               0 :   return CE_Failure;
    1792                 :     }
    1793                 :     
    1794               1 :     if( !VSI_ISREG( sStat.st_mode ) )
    1795                 :     {
    1796                 :   CPLError( CE_Failure, CPLE_FileIO,
    1797               0 :       "%s is not a regular file, not removed.\n", pszFilename );
    1798               0 :   return CE_Failure;
    1799                 :     }
    1800                 : 
    1801               1 :     if( VSIUnlink( pszFilename ) != 0 )
    1802                 :     {
    1803                 :   CPLError( CE_Failure, CPLE_FileIO,
    1804               0 :       "Error unlinking %s.\n", pszFilename );
    1805               0 :   return CE_Failure;
    1806                 :     }
    1807                 : 
    1808               1 :     return CE_None;
    1809                 : }
    1810                 : 
    1811                 : /************************************************************************/
    1812                 : /*                          GDALRegister_GSAG()                          */
    1813                 : /************************************************************************/
    1814                 : 
    1815             338 : void GDALRegister_GSAG()
    1816                 : 
    1817                 : {
    1818                 :     GDALDriver  *poDriver;
    1819                 : 
    1820             338 :     if( GDALGetDriverByName( "GSAG" ) == NULL )
    1821                 :     {
    1822             336 :         poDriver = new GDALDriver();
    1823                 :         
    1824             336 :         poDriver->SetDescription( "GSAG" );
    1825                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
    1826             336 :                                    "Golden Software ASCII Grid (.grd)" );
    1827                 :         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
    1828             336 :                                    "frmt_various.html#GSAG" );
    1829             336 :         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grd" );
    1830                 :   poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
    1831                 :            "Byte Int16 UInt16 Int32 UInt32 "
    1832             336 :            "Float32 Float64" );
    1833                 : 
    1834             336 :     poDriver->pfnOpen = GSAGDataset::Open;
    1835             336 :   poDriver->pfnCreateCopy = GSAGDataset::CreateCopy;
    1836             336 :   poDriver->pfnDelete = GSAGDataset::Delete;
    1837                 : 
    1838             336 :         GetGDALDriverManager()->RegisterDriver( poDriver );
    1839                 :     }
    1840             338 : }

Generated by: LCOV version 1.7