LCOV - code coverage report
Current view: directory - frmts/gsg - gsagdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 741 289 39.0 %
Date: 2012-12-26 Functions: 25 13 52.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: gsagdataset.cpp 25215 2012-11-08 08:25:05Z 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 25215 2012-11-08 08:25:05Z 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( VSILFILE *, vsi_l_offset, int, const char * );
      75                 : 
      76                 :     VSILFILE  *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 int          Identify( GDALOpenInfo * );
      87                 :     static GDALDataset *Open( GDALOpenInfo * );
      88                 :     static GDALDataset *CreateCopy( const char *pszFilename,
      89                 :             GDALDataset *poSrcDS,
      90                 :             int bStrict, char **papszOptions,
      91                 :             GDALProgressFunc pfnProgress,
      92                 :             void *pProgressData );
      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              16 : GSAGRasterBand::GSAGRasterBand( GSAGDataset *poDS, int nBand,
     166                 :         vsi_l_offset nDataStart ) :
     167                 :     padfRowMinZ(NULL),
     168                 :     padfRowMaxZ(NULL),
     169                 :     nMinZRow(-1),
     170              16 :     nMaxZRow(-1)
     171                 : 
     172                 : {
     173              16 :     this->poDS = poDS;
     174              16 :     this->nBand = nBand;
     175                 :     
     176              16 :     eDataType = GDT_Float64;
     177                 : 
     178              16 :     nBlockXSize = poDS->GetRasterXSize();
     179              16 :     nBlockYSize = 1;
     180                 : 
     181                 :     panLineOffset =
     182              16 :   (vsi_l_offset *)VSICalloc( poDS->nRasterYSize+1, sizeof(vsi_l_offset) );
     183              16 :     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              16 :   panLineOffset[poDS->nRasterYSize-1] = nDataStart;
     192              16 :   nLastReadLine = poDS->nRasterYSize;
     193               0 : }
     194                 : 
     195                 : /************************************************************************/
     196                 : /*                          ~GSAGRasterBand()                           */
     197                 : /************************************************************************/
     198                 : 
     199              16 : GSAGRasterBand::~GSAGRasterBand()
     200                 : {
     201              16 :     CPLFree( panLineOffset );
     202              16 :     if( padfRowMinZ != NULL )
     203               0 :   CPLFree( padfRowMinZ );
     204              16 :     if( padfRowMaxZ != NULL )
     205               0 :   CPLFree( padfRowMaxZ );
     206              16 : }
     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 :             if( IReadBlock( nBlockXOff, iFoundLine, NULL) != CE_None )
     317               0 :                 return CE_Failure;
     318                 :         }
     319                 :     }
     320                 : 
     321             156 :     if( panLineOffset[nBlockYOff] == 0 )
     322               0 :         return CE_Failure;
     323             156 :     if( VSIFSeekL( poGDS->fp, panLineOffset[nBlockYOff], SEEK_SET ) != 0 )
     324                 :     {
     325                 :         CPLError( CE_Failure, CPLE_FileIO,
     326                 :                   "Can't seek to offset %ld to read grid row %d.",
     327               0 :                   (long) panLineOffset[nBlockYOff], nBlockYOff );
     328               0 :         return CE_Failure;
     329                 :     }
     330                 : 
     331                 :     size_t nLineBufSize;
     332             156 :     char *szLineBuf = NULL;
     333                 :     size_t nCharsRead;
     334             156 :     size_t nCharsExamined = 0;
     335                 :     /* If we know the offsets, we can just read line directly */
     336             232 :     if( (nBlockYOff > 0) && ( panLineOffset[nBlockYOff-1] != 0 ) )
     337                 :     {
     338              76 :   assert(panLineOffset[nBlockYOff-1] > panLineOffset[nBlockYOff]);
     339              76 :   nLineBufSize = (size_t) (panLineOffset[nBlockYOff-1]
     340              76 :                                  - panLineOffset[nBlockYOff] + 1);
     341                 :     }
     342                 :     else
     343                 :     {
     344              80 :   nLineBufSize = nMaxLineSize;
     345                 :     }
     346                 : 
     347             156 :     szLineBuf = (char *)VSIMalloc( nLineBufSize );
     348             156 :     if( szLineBuf == NULL )
     349                 :     {
     350                 :   CPLError( CE_Failure, CPLE_OutOfMemory,
     351               0 :       "Unable to read block, unable to allocate line buffer.\n" );
     352               0 :   return CE_Failure;
     353                 :     }
     354                 : 
     355             156 :     nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp );
     356             156 :     if( nCharsRead == 0 )
     357                 :     {
     358               0 :   VSIFree( szLineBuf );
     359                 :   CPLError( CE_Failure, CPLE_FileIO,
     360                 :       "Can't read grid row %d at offset %ld.\n",
     361               0 :       nBlockYOff, (long) panLineOffset[nBlockYOff] );
     362               0 :   return CE_Failure;
     363                 :     }
     364             156 :     szLineBuf[nCharsRead] = '\0';
     365                 : 
     366             156 :     char *szStart = szLineBuf;
     367             156 :     char *szEnd = szStart;
     368            3276 :     for( int iCell=0; iCell<nBlockXSize; szStart = szEnd )
     369                 :     {
     370            3120 :   double dfValue = CPLStrtod( szStart, &szEnd );
     371            3120 :   if( szStart == szEnd )
     372                 :   {
     373                 :       /* No number found */
     374                 : 
     375                 :       /* Check if this was an expected failure */
     376               0 :       while( isspace( (unsigned char)*szStart ) )
     377               0 :     szStart++;
     378                 : 
     379                 :       /* Found sign at end of input, seek back to re-read it */
     380               0 :       if ( (*szStart == '-' || *szStart == '+') && *(szStart+1) == '\0' )
     381                 :       {
     382               0 :         if( VSIFSeekL( poGDS->fp, 
     383                 :                                VSIFTellL( poGDS->fp)-1, 
     384                 :                                SEEK_SET ) != 0 )
     385                 :         {
     386               0 :         VSIFree( szLineBuf );
     387                 :         CPLError( CE_Failure, CPLE_FileIO,
     388                 :             "Unable to seek in grid row %d "
     389                 :             "(offset %ld, seek %d).\n",
     390                 :             nBlockYOff, 
     391                 :                               (long) VSIFTellL(poGDS->fp),
     392               0 :             -1 );
     393                 : 
     394               0 :         return CE_Failure;
     395                 :     }
     396                 :       }
     397               0 :       else if( *szStart != '\0' )
     398                 :       {
     399               0 :     szEnd = szStart;
     400               0 :     while( !isspace( (unsigned char)*szEnd ) && *szEnd != '\0' )
     401               0 :         szEnd++;
     402               0 :     char cOldEnd = *szEnd;
     403               0 :     *szEnd = '\0';
     404                 : 
     405                 :     CPLError( CE_Warning, CPLE_FileIO,
     406                 :         "Unexpected value in grid row %d (expected floating "
     407                 :         "point value, found \"%s\").\n",
     408               0 :         nBlockYOff, szStart );
     409                 : 
     410               0 :     *szEnd = cOldEnd;
     411                 : 
     412               0 :     szEnd = szStart;
     413               0 :     while( !isdigit( *szEnd ) && *szEnd != '.' && *szEnd != '\0' )
     414               0 :         szEnd++;
     415                 : 
     416               0 :     continue;
     417                 :       }
     418               0 :       else if( static_cast<size_t>(szStart - szLineBuf) != nCharsRead )
     419                 :       {
     420                 :     CPLError( CE_Warning, CPLE_FileIO,
     421                 :         "Unexpected ASCII null-character in grid row %d at "
     422                 :         "offset %ld.\n", 
     423                 :                           nBlockYOff, 
     424               0 :                           (long) (szStart - szLineBuf) );
     425                 : 
     426               0 :     while( *szStart == '\0' &&
     427                 :                        static_cast<size_t>(szStart - szLineBuf) < nCharsRead )
     428               0 :         szStart++;
     429                 : 
     430               0 :     szEnd = szStart;
     431               0 :     continue;
     432                 :       }
     433                 : 
     434               0 :       nCharsExamined += szStart - szLineBuf;
     435               0 :       nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp );
     436               0 :       if( nCharsRead == 0 )
     437                 :       {
     438               0 :     VSIFree( szLineBuf );
     439                 :     CPLError( CE_Failure, CPLE_FileIO,
     440                 :         "Can't read portion of grid row %d at offset %ld.",
     441               0 :         nBlockYOff, (long) panLineOffset[nBlockYOff] );
     442               0 :     return CE_Failure;
     443                 :       }
     444               0 :       szLineBuf[nCharsRead] = '\0';
     445               0 :       szStart = szEnd = szLineBuf;
     446               0 :       continue;
     447                 :   }
     448            3120 :   else if( *szEnd == '\0'
     449                 :                  || (*szEnd == '.' && *(szEnd+1) == '\0')
     450                 :                  || (*szEnd == '-' && *(szEnd+1) == '\0')
     451                 :                  || (*szEnd == '+' && *(szEnd+1) == '\0')
     452                 :                  || (*szEnd == 'E' && *(szEnd+1) == '\0')
     453                 :                  || (*szEnd == 'E' && *(szEnd+1) == '-' && *(szEnd+2) == '\0')
     454                 :                  || (*szEnd == 'E' && *(szEnd+1) == '+' && *(szEnd+2) == '\0')
     455                 :                  || (*szEnd == 'e' && *(szEnd+1) == '\0')
     456                 :                  || (*szEnd == 'e' && *(szEnd+1) == '-' && *(szEnd+2) == '\0')
     457                 :                  || (*szEnd == 'e' && *(szEnd+1) == '+' && *(szEnd+2) == '\0'))
     458                 :   {
     459                 :       /* Number was interrupted by a nul character */
     460               0 :       while( *szEnd != '\0' )
     461               0 :     szEnd++;
     462                 : 
     463               0 :       if( static_cast<size_t>(szEnd - szLineBuf) != nCharsRead )
     464                 :       {
     465                 :     CPLError( CE_Warning, CPLE_FileIO,
     466                 :         "Unexpected ASCII null-character in grid row %d at "
     467                 :         "offset %ld.\n", 
     468                 :                           nBlockYOff, 
     469               0 :                           (long) (szStart - szLineBuf) );
     470                 : 
     471               0 :     while( *szEnd == '\0' &&
     472                 :            static_cast<size_t>(szStart - szLineBuf) < nCharsRead )
     473               0 :         szEnd++;
     474                 : 
     475               0 :     continue;
     476                 :       }
     477                 : 
     478                 :       /* End of buffer, could be interrupting a number */
     479               0 :       if( VSIFSeekL( poGDS->fp, szStart - szEnd, SEEK_CUR ) != 0 )
     480                 :       {
     481               0 :     VSIFree( szLineBuf );
     482                 :     CPLError( CE_Failure, CPLE_FileIO,
     483                 :         "Unable to seek in grid row %d (offset %ld, seek %d)"
     484                 :         ".\n", nBlockYOff, 
     485                 :                           (long) VSIFTellL(poGDS->fp),
     486               0 :         (int) (szStart - szEnd) );
     487                 : 
     488               0 :     return CE_Failure;
     489                 :       }
     490               0 :       nCharsExamined += szStart - szLineBuf;
     491               0 :       nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp );
     492               0 :       szLineBuf[nCharsRead] = '\0';
     493                 : 
     494               0 :       if( nCharsRead == 0 )
     495                 :       {
     496               0 :     VSIFree( szLineBuf );
     497                 :     CPLError( CE_Failure, CPLE_FileIO,
     498                 :         "Can't read portion of grid row %d at offset %ld.",
     499               0 :         nBlockYOff, (long) panLineOffset[nBlockYOff] );
     500               0 :     return CE_Failure;
     501                 :       }
     502               0 :       else if( nCharsRead > static_cast<size_t>(szEnd - szStart) )
     503                 :       {
     504                 :     /* Read new data, this was not really the end */
     505               0 :     szEnd = szStart = szLineBuf;
     506               0 :     continue;
     507                 :       }
     508                 : 
     509                 :       /* This is really the last value and has no tailing newline */
     510               0 :       szStart = szLineBuf;
     511               0 :       szEnd = szLineBuf + nCharsRead;
     512                 :   }
     513                 : 
     514            3120 :   if( pdfImage != NULL )
     515                 :   {
     516            1600 :       *(pdfImage+iCell) = dfValue;
     517                 :   }
     518                 : 
     519            3120 :   iCell++;
     520                 :     }
     521                 : 
     522             468 :     while( *szEnd == ' ' )
     523             156 :   szEnd++;
     524                 : 
     525             156 :     if( *szEnd != '\0' && *szEnd != poGDS->szEOL[0] )
     526                 :   CPLDebug( "GSAG", "Grid row %d does not end with a newline.  "
     527               0 :                   "Possible skew.\n", nBlockYOff );
     528                 : 
     529             986 :     while( isspace( (unsigned char)*szEnd ) )
     530             674 :   szEnd++;
     531                 : 
     532             156 :     nCharsExamined += szEnd - szLineBuf;
     533                 : 
     534             156 :     if( nCharsExamined >= nMaxLineSize )
     535               0 :   nMaxLineSize = nCharsExamined + 1;
     536                 : 
     537             156 :     if( nBlockYOff > 0 )
     538             152 :         panLineOffset[nBlockYOff - 1] = 
     539             152 :             panLineOffset[nBlockYOff] + nCharsExamined;
     540                 : 
     541             156 :     nLastReadLine = nBlockYOff;
     542                 : 
     543             156 :     VSIFree( szLineBuf );
     544                 : 
     545             156 :     return CE_None;
     546                 : }
     547                 : 
     548                 : /************************************************************************/
     549                 : /*                            IWriteBlock()                             */
     550                 : /************************************************************************/
     551                 : 
     552               0 : CPLErr GSAGRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
     553                 :             void * pImage )
     554                 : 
     555                 : {
     556               0 :     if( eAccess == GA_ReadOnly )
     557                 :     {
     558                 :   CPLError( CE_Failure, CPLE_NoWriteAccess,
     559               0 :       "Unable to write block, dataset opened read only.\n" );
     560               0 :   return CE_Failure;
     561                 :     }
     562                 : 
     563               0 :     if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 )
     564               0 :   return CE_Failure;
     565                 : 
     566               0 :     GSAGDataset *poGDS = (GSAGDataset *)poDS;
     567               0 :     assert( poGDS != NULL );
     568                 : 
     569               0 :     if( padfRowMinZ == NULL || padfRowMaxZ == NULL
     570                 :   || nMinZRow < 0 || nMaxZRow < 0 )
     571                 :     {
     572               0 :   padfRowMinZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) );
     573               0 :   if( padfRowMinZ == NULL )
     574                 :   {
     575                 :       CPLError( CE_Failure, CPLE_OutOfMemory,
     576               0 :           "Unable to allocate space for row minimums array.\n" );
     577               0 :       return CE_Failure;
     578                 :   }
     579                 : 
     580               0 :   padfRowMaxZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) );
     581               0 :   if( padfRowMaxZ == NULL )
     582                 :   {
     583               0 :       VSIFree( padfRowMinZ );
     584               0 :       padfRowMinZ = NULL;
     585                 :       CPLError( CE_Failure, CPLE_OutOfMemory,
     586               0 :           "Unable to allocate space for row maximums array.\n" );
     587               0 :       return CE_Failure;
     588                 :   }
     589                 : 
     590               0 :   CPLErr eErr = ScanForMinMaxZ();
     591               0 :   if( eErr != CE_None )
     592               0 :       return eErr;
     593                 :     }
     594                 : 
     595               0 :     if( panLineOffset[nBlockYOff+1] == 0 )
     596               0 :   IReadBlock( nBlockXOff, nBlockYOff, NULL );
     597                 : 
     598               0 :     if( panLineOffset[nBlockYOff+1] == 0 || panLineOffset[nBlockYOff] == 0 )
     599               0 :   return CE_Failure;
     600                 : 
     601               0 :     std::ostringstream ssOutBuf;
     602               0 :     ssOutBuf.precision( GSAGDataset::nFIELD_PRECISION );
     603               0 :     ssOutBuf.setf( std::ios::uppercase );
     604                 : 
     605               0 :     double *pdfImage = (double *)pImage;
     606               0 :     padfRowMinZ[nBlockYOff] = DBL_MAX;
     607               0 :     padfRowMaxZ[nBlockYOff] = -DBL_MAX;
     608               0 :     for( int iCell=0; iCell<nBlockXSize; )
     609                 :     {
     610               0 :   for( int iCol=0; iCol<10 && iCell<nBlockXSize; iCol++, iCell++ )
     611                 :   {
     612               0 :       if( AlmostEqual( pdfImage[iCell], GSAGDataset::dfNODATA_VALUE ) )
     613                 :       {
     614               0 :     if( pdfImage[iCell] < padfRowMinZ[nBlockYOff] )
     615               0 :         padfRowMinZ[nBlockYOff] = pdfImage[iCell];
     616                 : 
     617               0 :     if( pdfImage[iCell] > padfRowMaxZ[nBlockYOff] )
     618               0 :         padfRowMaxZ[nBlockYOff] = pdfImage[iCell];
     619                 :       }
     620                 : 
     621               0 :       ssOutBuf << pdfImage[iCell] << " ";
     622                 :   }
     623               0 :   ssOutBuf << poGDS->szEOL;
     624                 :     }
     625               0 :     ssOutBuf << poGDS->szEOL;
     626                 : 
     627               0 :     CPLString sOut = ssOutBuf.str();
     628               0 :     if( sOut.length() != panLineOffset[nBlockYOff+1]-panLineOffset[nBlockYOff] )
     629                 :     {
     630               0 :   int nShiftSize = (int) (sOut.length() - (panLineOffset[nBlockYOff+1]
     631               0 :                                                  - panLineOffset[nBlockYOff]));
     632               0 :   if( nBlockYOff != poGDS->nRasterYSize
     633                 :       && GSAGDataset::ShiftFileContents( poGDS->fp,
     634                 :                  panLineOffset[nBlockYOff+1],
     635                 :                  nShiftSize,
     636                 :                  poGDS->szEOL ) != CE_None )
     637                 :   {
     638                 :       CPLError( CE_Failure, CPLE_FileIO,
     639                 :           "Failure writing block, "
     640               0 :           "unable to shift file contents.\n" );
     641               0 :       return CE_Failure;
     642                 :   }
     643                 : 
     644               0 :   for( size_t iLine=nBlockYOff+1;
     645                 :        iLine < static_cast<unsigned>(poGDS->nRasterYSize+1)
     646               0 :     && panLineOffset[iLine] != 0; iLine++ )
     647               0 :       panLineOffset[iLine] += nShiftSize;
     648                 :     }
     649                 : 
     650               0 :     if( VSIFSeekL( poGDS->fp, panLineOffset[nBlockYOff], SEEK_SET ) != 0 )
     651                 :     {
     652               0 :   CPLError( CE_Failure, CPLE_FileIO, "Unable to seek to grid line.\n" );
     653               0 :   return CE_Failure;
     654                 :     }
     655                 : 
     656               0 :     if( VSIFWriteL( sOut.c_str(), 1, sOut.length(),
     657                 :         poGDS->fp ) != sOut.length() )
     658                 :     {
     659               0 :   CPLError( CE_Failure, CPLE_FileIO, "Unable to write grid block.\n" );
     660               0 :   return CE_Failure;
     661                 :     }
     662                 : 
     663                 :     /* Update header as needed */
     664               0 :     bool bHeaderNeedsUpdate = false;
     665               0 :     if( nMinZRow == nBlockYOff && padfRowMinZ[nBlockYOff] > dfMinZ )
     666                 :     {
     667               0 :   double dfNewMinZ = -DBL_MAX;
     668               0 :   for( int iRow=0; iRow<nRasterYSize; iRow++ )
     669                 :   {
     670               0 :       if( padfRowMinZ[iRow] < dfNewMinZ )
     671                 :       {
     672               0 :     dfNewMinZ = padfRowMinZ[iRow];
     673               0 :     nMinZRow = iRow;
     674                 :       }
     675                 :   }
     676                 : 
     677               0 :   if( dfNewMinZ != dfMinZ )
     678                 :   {
     679               0 :       dfMinZ = dfNewMinZ;
     680               0 :       bHeaderNeedsUpdate = true;
     681                 :   }
     682                 :     }
     683                 : 
     684               0 :     if( nMaxZRow == nBlockYOff && padfRowMaxZ[nBlockYOff] < dfMaxZ )
     685                 :     {
     686               0 :   double dfNewMaxZ = -DBL_MAX;
     687               0 :   for( int iRow=0; iRow<nRasterYSize; iRow++ )
     688                 :   {
     689               0 :       if( padfRowMaxZ[iRow] > dfNewMaxZ )
     690                 :       {
     691               0 :     dfNewMaxZ = padfRowMaxZ[iRow];
     692               0 :     nMaxZRow = iRow;
     693                 :       }
     694                 :   }
     695                 : 
     696               0 :   if( dfNewMaxZ != dfMaxZ )
     697                 :   {
     698               0 :       dfMaxZ = dfNewMaxZ;
     699               0 :       bHeaderNeedsUpdate = true;
     700                 :   }
     701                 :     }
     702                 : 
     703               0 :     if( padfRowMinZ[nBlockYOff] < dfMinZ || padfRowMaxZ[nBlockYOff] > dfMaxZ )
     704                 :     {
     705               0 :   if( padfRowMinZ[nBlockYOff] < dfMinZ )
     706                 :   {
     707               0 :       dfMinZ = padfRowMinZ[nBlockYOff];
     708               0 :       nMinZRow = nBlockYOff;
     709                 :   }
     710                 : 
     711               0 :   if( padfRowMaxZ[nBlockYOff] > dfMaxZ )
     712                 :   {
     713               0 :       dfMaxZ = padfRowMaxZ[nBlockYOff];
     714               0 :       nMaxZRow = nBlockYOff;
     715                 :   }
     716                 : 
     717               0 :   bHeaderNeedsUpdate = true;
     718                 :     }
     719                 : 
     720               0 :     if( bHeaderNeedsUpdate && dfMaxZ > dfMinZ )
     721                 :     {
     722               0 :   CPLErr eErr = poGDS->UpdateHeader();
     723               0 :   return eErr;
     724                 :     }
     725                 : 
     726               0 :     return CE_None;
     727                 : }
     728                 : 
     729                 : /************************************************************************/
     730                 : /*                           GetNoDataValue()                           */
     731                 : /************************************************************************/
     732                 : 
     733               8 : double GSAGRasterBand::GetNoDataValue( int * pbSuccess )
     734                 : {
     735               8 :     if( pbSuccess )
     736               7 :         *pbSuccess = TRUE;
     737                 : 
     738               8 :     return GSAGDataset::dfNODATA_VALUE;
     739                 : }
     740                 : 
     741                 : /************************************************************************/
     742                 : /*                             GetMinimum()                             */
     743                 : /************************************************************************/
     744                 : 
     745               0 : double GSAGRasterBand::GetMinimum( int *pbSuccess )
     746                 : {
     747               0 :     if( pbSuccess )
     748               0 :         *pbSuccess = TRUE;
     749                 : 
     750               0 :     return dfMinZ;
     751                 : }
     752                 : 
     753                 : /************************************************************************/
     754                 : /*                             GetMaximum()                             */
     755                 : /************************************************************************/
     756                 : 
     757               0 : double GSAGRasterBand::GetMaximum( int *pbSuccess )
     758                 : {
     759               0 :     if( pbSuccess )
     760               0 :         *pbSuccess = TRUE;
     761                 : 
     762               0 :     return dfMaxZ;
     763                 : }
     764                 : 
     765                 : /************************************************************************/
     766                 : /* ==================================================================== */
     767                 : /*        GSAGDataset       */
     768                 : /* ==================================================================== */
     769                 : /************************************************************************/
     770                 : 
     771                 : /************************************************************************/
     772                 : /*                             GSAGDataset()                            */
     773                 : /************************************************************************/
     774                 : 
     775              16 : GSAGDataset::GSAGDataset( const char *pszEOL )
     776                 : 
     777                 : {
     778              16 :     if( pszEOL == NULL || EQUAL(pszEOL, "") )
     779                 :     {
     780               0 :   CPLDebug( "GSAG", "GSAGDataset() created with invalid EOL string.\n" );
     781               0 :   this->szEOL[0] = '\x0D';
     782               0 :   this->szEOL[1] = '\x0A';
     783               0 :   this->szEOL[2] = '\0';
     784                 :     }
     785                 :     else
     786                 :     {
     787              16 :         strncpy(this->szEOL, pszEOL, sizeof(this->szEOL));
     788              16 :   this->szEOL[sizeof(this->szEOL) - 1] = '\0';
     789                 :     }
     790              16 : }
     791                 : 
     792                 : /************************************************************************/
     793                 : /*                            ~GSAGDataset()                            */
     794                 : /************************************************************************/
     795                 : 
     796              16 : GSAGDataset::~GSAGDataset()
     797                 : 
     798                 : {
     799              16 :     FlushCache();
     800              16 :     if( fp != NULL )
     801              16 :         VSIFCloseL( fp );
     802              16 : }
     803                 : 
     804                 : /************************************************************************/
     805                 : /*                              Identify()                              */
     806                 : /************************************************************************/
     807                 : 
     808           12490 : int GSAGDataset::Identify( GDALOpenInfo * poOpenInfo )
     809                 : 
     810                 : {
     811                 :     /* Check for signature */
     812           12506 :     if( poOpenInfo->nHeaderBytes < 5
     813                 :         || !EQUALN((const char *) poOpenInfo->pabyHeader,"DSAA",4)
     814              16 :         || ( poOpenInfo->pabyHeader[4] != '\x0D'
     815               0 :             && poOpenInfo->pabyHeader[4] != '\x0A' ))
     816                 :     {
     817           12474 :         return FALSE;
     818                 :     }
     819              16 :     return TRUE;
     820                 : }
     821                 : 
     822                 : /************************************************************************/
     823                 : /*                                Open()                                */
     824                 : /************************************************************************/
     825                 : 
     826            2397 : GDALDataset *GSAGDataset::Open( GDALOpenInfo * poOpenInfo )
     827                 : 
     828                 : {
     829            2397 :     if( !Identify(poOpenInfo) )
     830                 :     {
     831            2381 :         return NULL;
     832                 :     }
     833                 : 
     834                 :     /* Identify the end of line marker (should be \x0D\x0A, but try some others)
     835                 :      * (note that '\x0D' == '\r' and '\x0A' == '\n' on most systems) */
     836                 :     char szEOL[3];
     837              16 :     szEOL[0] = poOpenInfo->pabyHeader[4];
     838              16 :     szEOL[1] = poOpenInfo->pabyHeader[5];
     839              16 :     szEOL[2] = '\0';
     840              16 :     if( szEOL[1] != '\x0D' && szEOL[1] != '\x0A' )
     841               0 :   szEOL[1] = '\0';
     842                 : 
     843                 : /* -------------------------------------------------------------------- */
     844                 : /*      Create a corresponding GDALDataset.                             */
     845                 : /* -------------------------------------------------------------------- */
     846              16 :     GSAGDataset *poDS = new GSAGDataset( szEOL );
     847                 : 
     848                 : /* -------------------------------------------------------------------- */
     849                 : /*      Open file with large file API.                                  */
     850                 : /* -------------------------------------------------------------------- */
     851                 : 
     852              16 :     poDS->eAccess = poOpenInfo->eAccess;
     853              16 :     if( poOpenInfo->eAccess == GA_ReadOnly )
     854               4 :   poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
     855                 :     else
     856              12 :   poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
     857                 : 
     858              16 :     if( poDS->fp == NULL )
     859                 :     {
     860                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     861                 :                   "VSIFOpenL(%s) failed unexpectedly.", 
     862               0 :                   poOpenInfo->pszFilename );
     863               0 :   delete poDS;
     864               0 :         return NULL;
     865                 :     }
     866                 :  
     867                 : /* -------------------------------------------------------------------- */
     868                 : /*      Read the header.                                                */
     869                 : /* -------------------------------------------------------------------- */
     870                 :     char *pabyHeader;
     871              16 :     bool bMustFreeHeader = false;
     872              16 :     if( poOpenInfo->nHeaderBytes >= static_cast<int>(nMAX_HEADER_SIZE) )
     873                 :     {
     874              16 :   pabyHeader = (char *)poOpenInfo->pabyHeader;
     875                 :     }
     876                 :     else
     877                 :     {
     878               0 :   bMustFreeHeader = true;
     879               0 :   pabyHeader = (char *)VSIMalloc( nMAX_HEADER_SIZE );
     880               0 :   if( pabyHeader == NULL )
     881                 :   {
     882                 :       CPLError( CE_Failure, CPLE_OutOfMemory,
     883               0 :           "Unable to open dataset, unable to header buffer.\n" );
     884               0 :       return NULL;
     885                 :   }
     886                 : 
     887               0 :   size_t nRead = VSIFReadL( pabyHeader, 1, nMAX_HEADER_SIZE-1, poDS->fp );
     888               0 :   pabyHeader[nRead] = '\0';
     889                 :     }
     890                 : 
     891              16 :     const char *szErrorMsg = NULL;
     892              16 :     const char *szStart = pabyHeader + 5;
     893                 :     char *szEnd;
     894                 :     double dfTemp;
     895                 :     double dfMinX;
     896                 :     double dfMaxX;
     897                 :     double dfMinY;
     898                 :     double dfMaxY;
     899                 :     double dfMinZ;
     900                 :     double dfMaxZ;
     901                 : 
     902                 :     /* Parse number of X axis grid rows */
     903              16 :     long nTemp = strtol( szStart, &szEnd, 10 );
     904              16 :     if( szStart == szEnd || nTemp < 0l )
     905                 :     {
     906               0 :   szErrorMsg = "Unable to parse the number of X axis grid columns.\n";
     907               0 :   goto error;
     908                 :     }
     909              16 :     else if( nTemp > INT_MAX )
     910                 :     {
     911                 :   CPLError( CE_Warning, CPLE_AppDefined,
     912               0 :       "Number of X axis grid columns not representable.\n" );
     913               0 :   poDS->nRasterXSize = INT_MAX;
     914                 :     }
     915              16 :     else if ( nTemp == 0 )
     916                 :     {
     917               0 :         szErrorMsg = "Number of X axis grid columns is zero, which is invalid.\n";
     918               0 :         goto error;
     919                 :     }
     920                 :     else
     921                 :     {
     922              16 :   poDS->nRasterXSize = static_cast<int>(nTemp);
     923                 :     }
     924              16 :     szStart = szEnd;
     925                 : 
     926                 :     /* Parse number of Y axis grid rows */
     927              16 :     nTemp = strtol( szStart, &szEnd, 10 );
     928              16 :     if( szStart == szEnd || nTemp < 0l )
     929                 :     {
     930               0 :   szErrorMsg = "Unable to parse the number of Y axis grid rows.\n";
     931               0 :   goto error;
     932                 :     }
     933              16 :     else if( nTemp > INT_MAX )
     934                 :     {
     935                 :   CPLError( CE_Warning, CPLE_AppDefined,
     936               0 :       "Number of Y axis grid rows not representable.\n" );
     937               0 :   poDS->nRasterYSize = INT_MAX;
     938                 :     }
     939              16 :     else if ( nTemp == 0)
     940                 :     {
     941               0 :         szErrorMsg = "Number of Y axis grid rows is zero, which is invalid.\n";
     942               0 :         goto error;
     943                 :     }
     944                 :     else
     945                 :     {
     946              16 :   poDS->nRasterYSize = static_cast<int>(nTemp);
     947                 :     }
     948              16 :     szStart = szEnd;
     949                 : 
     950                 :     /* Parse the minimum X value of the grid */
     951              16 :     dfTemp = CPLStrtod( szStart, &szEnd );
     952              16 :     if( szStart == szEnd )
     953                 :     {
     954               0 :   szErrorMsg = "Unable to parse the minimum X value.\n";
     955               0 :   goto error;
     956                 :     }
     957                 :     else
     958                 :     {
     959              16 :   dfMinX = dfTemp;
     960                 :     }
     961              16 :     szStart = szEnd;
     962                 : 
     963                 :     /* Parse the maximum X value of the grid */
     964              16 :     dfTemp = CPLStrtod( szStart, &szEnd );
     965              16 :     if( szStart == szEnd )
     966                 :     {
     967               0 :   szErrorMsg = "Unable to parse the maximum X value.\n";
     968               0 :   goto error;
     969                 :     }
     970                 :     else
     971                 :     {
     972              16 :   dfMaxX = dfTemp;
     973                 :     }
     974              16 :     szStart = szEnd;
     975                 : 
     976                 :     /* Parse the minimum Y value of the grid */
     977              16 :     dfTemp = CPLStrtod( szStart, &szEnd );
     978              16 :     if( szStart == szEnd )
     979                 :     {
     980               0 :   szErrorMsg = "Unable to parse the minimum Y value.\n";
     981               0 :   goto error;
     982                 :     }
     983                 :     else
     984                 :     {
     985              16 :   dfMinY = dfTemp;
     986                 :     }
     987              16 :     szStart = szEnd;
     988                 : 
     989                 :     /* Parse the maximum Y value of the grid */
     990              16 :     dfTemp = CPLStrtod( szStart, &szEnd );
     991              16 :     if( szStart == szEnd )
     992                 :     {
     993               0 :   szErrorMsg = "Unable to parse the maximum Y value.\n";
     994               0 :   goto error;
     995                 :     }
     996                 :     else
     997                 :     {
     998              16 :   dfMaxY = dfTemp;
     999                 :     }
    1000              16 :     szStart = szEnd;
    1001                 : 
    1002                 :     /* Parse the minimum Z value of the grid */
    1003              64 :     while( isspace( (unsigned char)*szStart ) )
    1004              32 :   szStart++;
    1005              16 :     poDS->nMinMaxZOffset = szStart - pabyHeader;
    1006                 : 
    1007              16 :     dfTemp = CPLStrtod( szStart, &szEnd );
    1008              16 :     if( szStart == szEnd )
    1009                 :     {
    1010               0 :   szErrorMsg = "Unable to parse the minimum Z value.\n";
    1011               0 :   goto error;
    1012                 :     }
    1013                 :     else
    1014                 :     {
    1015              16 :   dfMinZ = dfTemp;
    1016                 :     }
    1017              16 :     szStart = szEnd;
    1018                 : 
    1019                 :     /* Parse the maximum Z value of the grid */
    1020              16 :     dfTemp = CPLStrtod( szStart, &szEnd );
    1021              16 :     if( szStart == szEnd )
    1022                 :     {
    1023               0 :   szErrorMsg = "Unable to parse the maximum Z value.\n";
    1024               0 :   goto error;
    1025                 :     }
    1026                 :     else
    1027                 :     {
    1028              16 :   dfMaxZ = dfTemp;
    1029                 :     }
    1030                 : 
    1031              64 :     while( isspace((unsigned char)*szEnd) )
    1032              32 :       szEnd++;
    1033                 : 
    1034                 : /* -------------------------------------------------------------------- */
    1035                 : /*      Create band information objects.                                */
    1036                 : /* -------------------------------------------------------------------- */
    1037                 :     {
    1038              16 :     GSAGRasterBand *poBand = new GSAGRasterBand( poDS, 1, szEnd-pabyHeader );
    1039              16 :     if( poBand->panLineOffset == NULL )
    1040                 :     {
    1041               0 :         delete poBand;
    1042               0 :         goto error;
    1043                 :     }
    1044                 : 
    1045              16 :     poBand->dfMinX = dfMinX;
    1046              16 :     poBand->dfMaxX = dfMaxX;
    1047              16 :     poBand->dfMinY = dfMinY;
    1048              16 :     poBand->dfMaxY = dfMaxY;
    1049              16 :     poBand->dfMinZ = dfMinZ;
    1050              16 :     poBand->dfMaxZ = dfMaxZ;
    1051                 : 
    1052              16 :     poDS->SetBand( 1, poBand );
    1053                 :     }
    1054                 : 
    1055              16 :     if( bMustFreeHeader )
    1056                 :     {
    1057               0 :   CPLFree( pabyHeader );
    1058                 :     }
    1059                 : 
    1060                 : /* -------------------------------------------------------------------- */
    1061                 : /*      Initialize any PAM information.                                 */
    1062                 : /* -------------------------------------------------------------------- */
    1063              16 :     poDS->SetDescription( poOpenInfo->pszFilename );
    1064              16 :     poDS->TryLoadXML();
    1065                 : 
    1066                 : /* -------------------------------------------------------------------- */
    1067                 : /*      Check for external overviews.                                   */
    1068                 : /* -------------------------------------------------------------------- */
    1069              16 :     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->papszSiblingFiles );
    1070                 : 
    1071              16 :     return( poDS );
    1072                 : 
    1073                 : error:
    1074               0 :     if ( bMustFreeHeader )
    1075                 :     {
    1076               0 :   CPLFree( pabyHeader );
    1077                 :     }
    1078                 : 
    1079               0 :     delete poDS;
    1080                 : 
    1081               0 :     if (szErrorMsg)
    1082               0 :         CPLError( CE_Failure, CPLE_AppDefined, "%s", szErrorMsg );
    1083               0 :     return NULL;
    1084                 : }
    1085                 : 
    1086                 : /************************************************************************/
    1087                 : /*                          GetGeoTransform()                           */
    1088                 : /************************************************************************/
    1089                 : 
    1090              17 : CPLErr GSAGDataset::GetGeoTransform( double *padfGeoTransform )
    1091                 : {
    1092              17 :     if( padfGeoTransform == NULL )
    1093               0 :   return CE_Failure;
    1094                 : 
    1095              17 :     GSAGRasterBand *poGRB = (GSAGRasterBand *)GetRasterBand( 1 );
    1096                 : 
    1097              17 :     if( poGRB == NULL )
    1098                 :     {
    1099               0 :   padfGeoTransform[0] = 0;
    1100               0 :   padfGeoTransform[1] = 1;
    1101               0 :   padfGeoTransform[2] = 0;
    1102               0 :   padfGeoTransform[3] = 0;
    1103               0 :   padfGeoTransform[4] = 0;
    1104               0 :   padfGeoTransform[5] = 1;
    1105               0 :   return CE_Failure;
    1106                 :     }
    1107                 : 
    1108                 :     /* check if we have a PAM GeoTransform stored */
    1109              17 :     CPLPushErrorHandler( CPLQuietErrorHandler );
    1110              17 :     CPLErr eErr = GDALPamDataset::GetGeoTransform( padfGeoTransform );
    1111              17 :     CPLPopErrorHandler();
    1112                 : 
    1113              17 :     if( eErr == CE_None )
    1114               0 :   return CE_None;
    1115                 : 
    1116                 :     /* calculate pixel size first */
    1117              17 :     padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX)/(nRasterXSize - 1);
    1118              17 :     padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY)/(nRasterYSize - 1);
    1119                 : 
    1120                 :     /* then calculate image origin */
    1121              17 :     padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2;
    1122              17 :     padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2;
    1123                 : 
    1124                 :     /* tilt/rotation does not supported by the GS grids */
    1125              17 :     padfGeoTransform[4] = 0.0;
    1126              17 :     padfGeoTransform[2] = 0.0;
    1127                 : 
    1128              17 :     return CE_None;
    1129                 : }
    1130                 : 
    1131                 : /************************************************************************/
    1132                 : /*                          SetGeoTransform()                           */
    1133                 : /************************************************************************/
    1134                 : 
    1135               0 : CPLErr GSAGDataset::SetGeoTransform( double *padfGeoTransform )
    1136                 : {
    1137               0 :     if( eAccess == GA_ReadOnly )
    1138                 :     {
    1139                 :   CPLError( CE_Failure, CPLE_NoWriteAccess,
    1140               0 :       "Unable to set GeoTransform, dataset opened read only.\n" );
    1141               0 :   return CE_Failure;
    1142                 :     }
    1143                 : 
    1144               0 :     GSAGRasterBand *poGRB = (GSAGRasterBand *)GetRasterBand( 1 );
    1145                 : 
    1146               0 :     if( poGRB == NULL || padfGeoTransform == NULL)
    1147               0 :   return CE_Failure;
    1148                 : 
    1149                 :     /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
    1150               0 :     CPLErr eErr = CE_None;
    1151                 :     /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0
    1152                 :   || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 )
    1153                 :   eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform );*/
    1154                 : 
    1155               0 :     if( eErr != CE_None )
    1156               0 :   return eErr;
    1157                 : 
    1158               0 :     double dfOldMinX = poGRB->dfMinX;
    1159               0 :     double dfOldMaxX = poGRB->dfMaxX;
    1160               0 :     double dfOldMinY = poGRB->dfMinY;
    1161               0 :     double dfOldMaxY = poGRB->dfMaxY;
    1162                 : 
    1163               0 :     poGRB->dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
    1164                 :     poGRB->dfMaxX =
    1165               0 :         padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0];
    1166                 :     poGRB->dfMinY =
    1167               0 :         padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
    1168               0 :     poGRB->dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2;
    1169                 : 
    1170               0 :     eErr = UpdateHeader();
    1171                 : 
    1172               0 :     if( eErr != CE_None )
    1173                 :     {
    1174               0 :   poGRB->dfMinX = dfOldMinX;
    1175               0 :   poGRB->dfMaxX = dfOldMaxX;
    1176               0 :   poGRB->dfMinY = dfOldMinY;
    1177               0 :   poGRB->dfMaxY = dfOldMaxY;
    1178                 :     }
    1179                 : 
    1180               0 :     return eErr;
    1181                 : }
    1182                 : 
    1183                 : /************************************************************************/
    1184                 : /*                         ShiftFileContents()                          */
    1185                 : /************************************************************************/
    1186              12 : CPLErr GSAGDataset::ShiftFileContents( VSILFILE *fp, vsi_l_offset nShiftStart,
    1187                 :                int nShiftSize, const char *pszEOL )
    1188                 : {
    1189                 :     /* nothing to do for zero-shift */
    1190              12 :     if( nShiftSize == 0 )
    1191               0 :   return CE_None;
    1192                 : 
    1193                 :     /* make sure start location is sane */
    1194              12 :     if( nShiftStart < 0
    1195                 :   || (nShiftSize < 0
    1196                 :       && nShiftStart < static_cast<vsi_l_offset>(-nShiftSize)) )
    1197               0 :   nShiftStart = (nShiftSize > 0) ? 0 : -nShiftSize;
    1198                 : 
    1199                 :     /* get offset at end of file */
    1200              12 :     if( VSIFSeekL( fp, 0, SEEK_END ) != 0 )
    1201                 :     {
    1202                 :   CPLError( CE_Failure, CPLE_FileIO,
    1203               0 :       "Unable to seek to end of grid file.\n" );
    1204               0 :   return CE_Failure;
    1205                 :     }
    1206                 : 
    1207              12 :     vsi_l_offset nOldEnd = VSIFTellL( fp );
    1208                 : 
    1209                 :     /* If shifting past end, just zero-pad as necessary */
    1210              12 :     if( nShiftStart >= nOldEnd )
    1211                 :     {
    1212               0 :   if( nShiftSize < 0 )
    1213                 :   {
    1214               0 :       if( nShiftStart + nShiftSize >= nOldEnd )
    1215               0 :     return CE_None;
    1216                 : 
    1217               0 :       if( VSIFSeekL( fp, nShiftStart + nShiftSize, SEEK_SET ) != 0 )
    1218                 :       {
    1219                 :     CPLError( CE_Failure, CPLE_FileIO,
    1220               0 :         "Unable to seek near end of file.\n" );
    1221               0 :     return CE_Failure;
    1222                 :       }
    1223                 : 
    1224                 :       /* ftruncate()? */
    1225               0 :       for( vsi_l_offset nPos = nShiftStart + nShiftSize;
    1226                 :      nPos > nOldEnd; nPos++ )
    1227                 :       {
    1228               0 :     if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1229                 :     {
    1230                 :         CPLError( CE_Failure, CPLE_FileIO,
    1231                 :             "Unable to write padding to grid file "
    1232               0 :             "(Out of space?).\n" );
    1233               0 :         return CE_Failure;
    1234                 :     }
    1235                 :       }
    1236                 : 
    1237               0 :       return CE_None;
    1238                 :   }
    1239                 :   else
    1240                 :   {
    1241               0 :       for( vsi_l_offset nPos = nOldEnd;
    1242                 :      nPos < nShiftStart + nShiftSize; nPos++ )
    1243                 :       {
    1244               0 :     if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1245                 :     {
    1246                 :         CPLError( CE_Failure, CPLE_FileIO,
    1247                 :             "Unable to write padding to grid file "
    1248               0 :             "(Out of space?).\n" );
    1249               0 :         return CE_Failure;
    1250                 :     }
    1251                 :       }
    1252               0 :       return CE_None;
    1253                 :   }
    1254                 :     }
    1255                 : 
    1256                 :     /* prepare buffer for real shifting */
    1257              12 :     size_t nBufferSize = (1024 >= abs(nShiftSize)*2) ? 1024 : abs(nShiftSize)*2;
    1258              12 :     char *pabyBuffer = (char *)VSIMalloc( nBufferSize );
    1259              12 :     if( pabyBuffer == NULL)
    1260                 :     {
    1261                 :   CPLError( CE_Failure, CPLE_OutOfMemory,
    1262               0 :       "Unable to allocate space for shift buffer.\n" );
    1263               0 :   return CE_Failure;
    1264                 :     }
    1265                 : 
    1266              12 :     if( VSIFSeekL( fp, nShiftStart, SEEK_SET ) != 0 )
    1267                 :     {
    1268               0 :   VSIFree( pabyBuffer );
    1269                 :   CPLError( CE_Failure, CPLE_FileIO,
    1270               0 :       "Unable to seek to start of shift in grid file.\n" );
    1271               0 :   return CE_Failure;
    1272                 :     }
    1273                 : 
    1274                 :     size_t nRead;
    1275              12 :     size_t nOverlap = (nShiftSize > 0) ? nShiftSize : 0;
    1276                 :     /* If there is overlap, fill buffer with the overlap to start */
    1277              12 :     if( nOverlap > 0)
    1278                 :     {
    1279               0 :   nRead = VSIFReadL( (void *)pabyBuffer, 1, nOverlap, fp );
    1280               0 :   if( nRead < nOverlap && !VSIFEofL( fp ) )
    1281                 :   {
    1282               0 :       VSIFree( pabyBuffer );
    1283                 :       CPLError( CE_Failure, CPLE_FileIO,
    1284               0 :           "Error reading grid file.\n" );
    1285               0 :       return CE_Failure;
    1286                 :   }
    1287                 : 
    1288                 :   /* overwrite the new space with ' ' */
    1289               0 :       if( VSIFSeekL( fp, nShiftStart, SEEK_SET ) != 0 )
    1290                 :   {
    1291               0 :       VSIFree( pabyBuffer );
    1292                 :       CPLError( CE_Failure, CPLE_FileIO,
    1293               0 :           "Unable to seek to start of shift in grid file.\n" );
    1294               0 :       return CE_Failure;
    1295                 :   }
    1296                 : 
    1297               0 :   for( int iFill=0; iFill<nShiftSize; iFill++ )
    1298                 :   {
    1299               0 :       if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1300                 :       {
    1301               0 :     VSIFree( pabyBuffer );
    1302                 :     CPLError( CE_Failure, CPLE_FileIO,
    1303                 :         "Unable to write padding to grid file "
    1304               0 :         "(Out of space?).\n" );
    1305               0 :     return CE_Failure;
    1306                 :       }
    1307                 :   }
    1308                 : 
    1309                 :   /* if we have already read the entire file, finish it off */
    1310               0 :   if( VSIFTellL( fp ) >= nOldEnd )
    1311                 :   {
    1312               0 :       if( VSIFWriteL( (void *)pabyBuffer, 1, nRead, fp ) != nRead )
    1313                 :       {
    1314               0 :     VSIFree( pabyBuffer );
    1315                 :     CPLError( CE_Failure, CPLE_FileIO,
    1316               0 :         "Unable to write to grid file (Out of space?).\n" );
    1317               0 :     return CE_Failure;
    1318                 :       }
    1319                 : 
    1320               0 :       VSIFree( pabyBuffer );
    1321               0 :       return CE_None;
    1322                 :   }
    1323                 :     }
    1324                 : 
    1325                 :     /* iterate over the remainder of the file and shift as requested */
    1326              12 :     bool bEOF = false;
    1327              37 :     while( !bEOF )
    1328                 :     {
    1329                 :   nRead = VSIFReadL( (void *)(pabyBuffer+nOverlap), 1,
    1330              13 :          nBufferSize - nOverlap, fp );
    1331                 : 
    1332              13 :         if( VSIFEofL( fp ) )
    1333              12 :             bEOF = true;
    1334                 :         else 
    1335               1 :             bEOF = false;
    1336                 : 
    1337              13 :   if( nRead == 0 && !bEOF )
    1338                 :   {
    1339               0 :       VSIFree( pabyBuffer );
    1340                 :       CPLError( CE_Failure, CPLE_FileIO,
    1341               0 :           "Unable to read from grid file (possible corruption).\n");
    1342               0 :       return CE_Failure;
    1343                 :   }
    1344                 : 
    1345                 :   /* FIXME:  Should use SEEK_CUR, review integer promotions... */
    1346              13 :   if( VSIFSeekL( fp, VSIFTellL(fp)-nRead+nShiftSize-nOverlap,
    1347                 :            SEEK_SET ) != 0 )
    1348                 :   {
    1349               0 :       VSIFree( pabyBuffer );
    1350                 :       CPLError( CE_Failure, CPLE_FileIO,
    1351               0 :           "Unable to seek in grid file (possible corruption).\n" );
    1352               0 :       return CE_Failure;
    1353                 :   }
    1354                 : 
    1355              13 :   size_t nWritten = VSIFWriteL( (void *)pabyBuffer, 1, nRead, fp );
    1356              13 :   if( nWritten != nRead )
    1357                 :   {
    1358               0 :       VSIFree( pabyBuffer );
    1359                 :       CPLError( CE_Failure, CPLE_FileIO,
    1360               0 :           "Unable to write to grid file (out of space?).\n" );
    1361               0 :       return CE_Failure;
    1362                 :   }
    1363                 : 
    1364                 :   /* shift overlapped contents to the front of the buffer if necessary */
    1365              13 :   if( nOverlap > 0)
    1366               0 :       memmove(pabyBuffer, pabyBuffer+nRead, nOverlap);
    1367                 :     }
    1368                 : 
    1369                 :     /* write the remainder of the buffer or overwrite leftovers and finish */
    1370              12 :     if( nShiftSize > 0 )
    1371                 :     {
    1372               0 :   size_t nTailSize = nOverlap;
    1373               0 :   while( nTailSize > 0 && isspace( (unsigned char)pabyBuffer[nTailSize-1] ) )
    1374               0 :       nTailSize--;
    1375                 : 
    1376               0 :   if( VSIFWriteL( (void *)pabyBuffer, 1, nTailSize, fp ) != nTailSize )
    1377                 :   {
    1378               0 :       VSIFree( pabyBuffer );
    1379                 :       CPLError( CE_Failure, CPLE_FileIO,
    1380               0 :           "Unable to write to grid file (out of space?).\n" );
    1381               0 :       return CE_Failure;
    1382                 :   }
    1383                 : 
    1384               0 :   if( VSIFWriteL( (void *)pszEOL, 1, strlen(pszEOL), fp )
    1385                 :             != strlen(pszEOL) )
    1386                 :   {
    1387               0 :       VSIFree( pabyBuffer );
    1388                 :       CPLError( CE_Failure, CPLE_FileIO,
    1389               0 :           "Unable to write to grid file (out of space?).\n" );
    1390               0 :       return CE_Failure;
    1391                 :   }
    1392                 :     }
    1393                 :     else
    1394                 :     {
    1395                 :   /* FIXME: ftruncate()? */
    1396                 :   /* FIXME:  Should use SEEK_CUR, review integer promotions... */
    1397              12 :   if( VSIFSeekL( fp, VSIFTellL(fp)-strlen(pszEOL), SEEK_SET ) != 0 )
    1398                 :   {
    1399               0 :       VSIFree( pabyBuffer );
    1400                 :       CPLError( CE_Failure, CPLE_FileIO,
    1401               0 :           "Unable to seek in grid file.\n" );
    1402               0 :       return CE_Failure;
    1403                 :   }
    1404                 : 
    1405             301 :   for( int iPadding=0; iPadding<-nShiftSize; iPadding++ )
    1406                 :   {
    1407             289 :       if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 )
    1408                 :       {
    1409               0 :     VSIFree( pabyBuffer );
    1410                 :     CPLError( CE_Failure, CPLE_FileIO,
    1411               0 :         "Error writing to grid file.\n" );
    1412               0 :     return CE_Failure;
    1413                 :       }
    1414                 :   }
    1415                 : 
    1416              12 :   if( VSIFWriteL( (void *)pszEOL, 1, strlen(pszEOL), fp )
    1417                 :             != strlen(pszEOL) )
    1418                 :   {
    1419               0 :       VSIFree( pabyBuffer );
    1420                 :       CPLError( CE_Failure, CPLE_FileIO,
    1421               0 :           "Unable to write to grid file (out of space?).\n" );
    1422               0 :       return CE_Failure;
    1423                 :   }
    1424                 :     }
    1425                 : 
    1426              12 :     VSIFree( pabyBuffer );
    1427              12 :     return CE_None;
    1428                 : }
    1429                 : 
    1430                 : /************************************************************************/
    1431                 : /*                             UpdateHeader()                           */
    1432                 : /************************************************************************/
    1433                 : 
    1434               0 : CPLErr GSAGDataset::UpdateHeader()
    1435                 : 
    1436                 : {
    1437               0 :     GSAGRasterBand *poBand = (GSAGRasterBand *)GetRasterBand( 1 );
    1438               0 :     if( poBand == NULL )
    1439                 :     {
    1440               0 :   CPLError( CE_Failure, CPLE_FileIO, "Unable to open raster band.\n" );
    1441               0 :   return CE_Failure;
    1442                 :     }
    1443                 : 
    1444               0 :     std::ostringstream ssOutBuf;
    1445               0 :     ssOutBuf.precision( nFIELD_PRECISION );
    1446               0 :     ssOutBuf.setf( std::ios::uppercase );
    1447                 : 
    1448                 :     /* signature */
    1449               0 :     ssOutBuf << "DSAA" << szEOL;
    1450                 : 
    1451                 :     /* columns rows */
    1452               0 :     ssOutBuf << nRasterXSize << " " << nRasterYSize << szEOL;
    1453                 : 
    1454                 :     /* x range */
    1455               0 :     ssOutBuf << poBand->dfMinX << " " << poBand->dfMaxX << szEOL;
    1456                 : 
    1457                 :     /* y range */
    1458               0 :     ssOutBuf << poBand->dfMinY << " " << poBand->dfMaxY << szEOL;
    1459                 : 
    1460                 :     /* z range */
    1461               0 :     ssOutBuf << poBand->dfMinZ << " " << poBand->dfMaxZ << szEOL;
    1462                 : 
    1463               0 :     CPLString sOut = ssOutBuf.str();
    1464               0 :     if( sOut.length() != poBand->panLineOffset[0] )
    1465                 :     {
    1466               0 :   int nShiftSize = (int) (sOut.length() - poBand->panLineOffset[0]);
    1467               0 :   if( ShiftFileContents( fp, poBand->panLineOffset[0], nShiftSize,
    1468                 :              szEOL ) != CE_None )
    1469                 :   {
    1470                 :       CPLError( CE_Failure, CPLE_FileIO,
    1471                 :           "Unable to update grid header, "
    1472               0 :           "failure shifting file contents.\n" );
    1473               0 :       return CE_Failure;
    1474                 :   }
    1475                 : 
    1476               0 :   for( size_t iLine=0;
    1477                 :        iLine < static_cast<unsigned>(nRasterYSize+1)
    1478               0 :     && poBand->panLineOffset[iLine] != 0;
    1479                 :        iLine++ )
    1480               0 :       poBand->panLineOffset[iLine] += nShiftSize;
    1481                 :     }
    1482                 : 
    1483               0 :     if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 )
    1484                 :     {
    1485                 :   CPLError( CE_Failure, CPLE_FileIO,
    1486               0 :       "Unable to seek to start of grid file.\n" );
    1487               0 :   return CE_Failure;
    1488                 :     }
    1489                 : 
    1490               0 :     if( VSIFWriteL( sOut.c_str(), 1, sOut.length(), fp ) != sOut.length() )
    1491                 :     {
    1492                 :   CPLError( CE_Failure, CPLE_FileIO,
    1493               0 :       "Unable to update file header.  Disk full?\n" );
    1494               0 :   return CE_Failure;
    1495                 :     }
    1496                 : 
    1497               0 :     return CE_None;
    1498                 : }
    1499                 : 
    1500                 : /************************************************************************/
    1501                 : /*                             CreateCopy()                             */
    1502                 : /************************************************************************/
    1503                 : 
    1504              30 : GDALDataset *GSAGDataset::CreateCopy( const char *pszFilename,
    1505                 :               GDALDataset *poSrcDS,
    1506                 :               int bStrict, char **papszOptions,
    1507                 :               GDALProgressFunc pfnProgress,
    1508                 :               void *pProgressData )
    1509                 : {
    1510              30 :     if( pfnProgress == NULL )
    1511               0 :   pfnProgress = GDALDummyProgress;
    1512                 : 
    1513              30 :     int nBands = poSrcDS->GetRasterCount();
    1514              30 :     if (nBands == 0)
    1515                 :     {
    1516                 :         CPLError( CE_Failure, CPLE_NotSupported, 
    1517               1 :                   "GSAG driver does not support source dataset with zero band.\n");
    1518               1 :         return NULL;
    1519                 :     }
    1520              29 :     else if (nBands > 1)
    1521                 :     {
    1522               4 :   if( bStrict )
    1523                 :   {
    1524                 :       CPLError( CE_Failure, CPLE_NotSupported,
    1525                 :           "Unable to create copy, Golden Software ASCII Grid "
    1526               4 :           "format only supports one raster band.\n" );
    1527               4 :       return NULL;
    1528                 :   }
    1529                 :   else
    1530                 :       CPLError( CE_Warning, CPLE_NotSupported,
    1531                 :           "Golden Software ASCII Grid format only supports one "
    1532               0 :           "raster band, first band will be copied.\n" );
    1533                 :     }
    1534                 : 
    1535              25 :     if( !pfnProgress( 0.0, NULL, pProgressData ) )
    1536                 :     {
    1537               0 :         CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated\n" );
    1538               0 :         return NULL;
    1539                 :     }
    1540                 : 
    1541              25 :     VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" );
    1542                 : 
    1543              25 :     if( fp == NULL )
    1544                 :     {
    1545                 :         CPLError( CE_Failure, CPLE_OpenFailed,
    1546                 :                   "Attempt to create file '%s' failed.\n",
    1547              13 :                   pszFilename );
    1548              13 :         return NULL;
    1549                 :     }
    1550                 : 
    1551              12 :     int          nXSize = poSrcDS->GetRasterXSize();
    1552              12 :     int          nYSize = poSrcDS->GetRasterYSize();
    1553                 :     double   adfGeoTransform[6];
    1554                 : 
    1555              12 :     poSrcDS->GetGeoTransform( adfGeoTransform );
    1556                 : 
    1557              12 :     std::ostringstream ssHeader;
    1558              12 :     ssHeader.precision( nFIELD_PRECISION );
    1559              12 :     ssHeader.setf( std::ios::uppercase );
    1560                 : 
    1561              12 :     ssHeader << "DSAA\x0D\x0A";
    1562                 : 
    1563              12 :     ssHeader << nXSize << " " << nYSize << "\x0D\x0A";
    1564                 : 
    1565              12 :     ssHeader << adfGeoTransform[0] + adfGeoTransform[1] / 2 << " "
    1566              24 :        << adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0]
    1567              36 :        << "\x0D\x0A";
    1568                 : 
    1569              24 :     ssHeader << adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3] << " "
    1570              12 :        << adfGeoTransform[3] + adfGeoTransform[5] / 2
    1571              36 :        << "\x0D\x0A";
    1572                 : 
    1573              12 :     if( VSIFWriteL( (void *)ssHeader.str().c_str(), 1, ssHeader.str().length(),
    1574                 :         fp ) != ssHeader.str().length() )
    1575                 :     {
    1576               0 :   VSIFCloseL( fp );
    1577                 :         CPLError( CE_Failure, CPLE_FileIO,
    1578               0 :                   "Unable to create copy, writing header failed.\n" );
    1579               0 :         return NULL;
    1580                 :     }
    1581                 : 
    1582                 :     /* Save the location and write placeholders for the min/max Z value */
    1583              12 :     vsi_l_offset nRangeStart = VSIFTellL( fp );
    1584              12 :     const char *szDummyRange = "0.0000000000001 0.0000000000001\x0D\x0A";
    1585              12 :     size_t nDummyRangeLen = strlen( szDummyRange );
    1586              12 :     if( VSIFWriteL( (void *)szDummyRange, 1, nDummyRangeLen,
    1587                 :         fp ) != nDummyRangeLen )
    1588                 :     {
    1589               0 :   VSIFCloseL( fp );
    1590                 :         CPLError( CE_Failure, CPLE_FileIO,
    1591               0 :                   "Unable to create copy, writing header failed.\n" );
    1592               0 :         return NULL;
    1593                 :     }
    1594                 : 
    1595                 : /* -------------------------------------------------------------------- */
    1596                 : /*      Copy band data.             */
    1597                 : /* -------------------------------------------------------------------- */
    1598              12 :     double *pdfData = (double *)VSIMalloc2( nXSize, sizeof( double ) );
    1599              12 :     if( pdfData == NULL )
    1600                 :     {
    1601               0 :   VSIFCloseL( fp );
    1602                 :   CPLError( CE_Failure, CPLE_OutOfMemory,
    1603               0 :       "Unable to create copy, unable to allocate line buffer.\n" );
    1604               0 :   return NULL;
    1605                 :     }
    1606                 : 
    1607              12 :     GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
    1608                 :     int bSrcHasNDValue;
    1609              12 :     double dfSrcNoDataValue = poSrcBand->GetNoDataValue( &bSrcHasNDValue );
    1610              12 :     double dfMin = DBL_MAX;
    1611              12 :     double dfMax = -DBL_MAX;
    1612             142 :     for( int iRow=0; iRow<nYSize; iRow++ )
    1613                 :     {
    1614                 :   CPLErr eErr = poSrcBand->RasterIO( GF_Read, 0, nYSize-iRow-1,
    1615                 :              nXSize, 1, pdfData,
    1616             130 :              nXSize, 1, GDT_Float64, 0, 0 );
    1617                 : 
    1618             130 :   if( eErr != CE_None )
    1619                 :   {
    1620               0 :       VSIFCloseL( fp );
    1621               0 :       VSIFree( pdfData );
    1622               0 :       return NULL;
    1623                 :   }
    1624                 : 
    1625             410 :   for( int iCol=0; iCol<nXSize; )
    1626                 :   {
    1627             150 :       for( int iCount=0;
    1628                 :      iCount<10 && iCol<nXSize;
    1629                 :      iCount++, iCol++ )
    1630                 :       {
    1631            1500 :     double dfValue = pdfData[iCol];
    1632                 : 
    1633            1500 :     if( bSrcHasNDValue && AlmostEqual( dfValue, dfSrcNoDataValue ) )
    1634                 :     {
    1635               0 :         dfValue = dfNODATA_VALUE;
    1636                 :     }
    1637                 :     else
    1638                 :     {
    1639            1500 :         if( dfValue > dfMax )
    1640              14 :       dfMax = dfValue;
    1641                 : 
    1642            1500 :         if( dfValue < dfMin )
    1643              19 :       dfMin = dfValue;
    1644                 :     }
    1645                 : 
    1646            1500 :     std::ostringstream ssOut;
    1647            1500 :     ssOut.precision(nFIELD_PRECISION);
    1648            1500 :     ssOut.setf( std::ios::uppercase );
    1649            1500 :     ssOut << dfValue << " ";
    1650            1500 :     CPLString sOut = ssOut.str();
    1651                 : 
    1652            1500 :     if( VSIFWriteL( sOut.c_str(), 1, sOut.length(), fp )
    1653                 :         != sOut.length() )
    1654                 :     {
    1655               0 :         VSIFCloseL( fp );
    1656               0 :         VSIFree( pdfData );
    1657                 :         CPLError( CE_Failure, CPLE_FileIO,
    1658               0 :             "Unable to write grid cell.  Disk full?\n" );
    1659               0 :         return NULL;
    1660                 :     }
    1661                 :       }
    1662                 : 
    1663             150 :       if( VSIFWriteL( (void *)"\x0D\x0A", 1, 2, fp ) != 2 )
    1664                 :       {
    1665               0 :     VSIFCloseL( fp );
    1666               0 :     VSIFree( pdfData );
    1667                 :     CPLError( CE_Failure, CPLE_FileIO,
    1668               0 :         "Unable to finish write of grid line. Disk full?\n" );
    1669               0 :     return NULL;
    1670                 :       }
    1671                 :   }
    1672                 : 
    1673             130 :   if( VSIFWriteL( (void *)"\x0D\x0A", 1, 2, fp ) != 2 )
    1674                 :   {
    1675               0 :       VSIFCloseL( fp );
    1676               0 :       VSIFree( pdfData );
    1677                 :       CPLError( CE_Failure, CPLE_FileIO,
    1678               0 :           "Unable to finish write of grid row. Disk full?\n" );
    1679               0 :       return NULL;
    1680                 :   }
    1681                 : 
    1682             130 :   if( !pfnProgress( static_cast<double>(iRow + 1)/nYSize,
    1683                 :         NULL, pProgressData ) )
    1684                 :   {
    1685               0 :       VSIFCloseL( fp );
    1686               0 :       VSIFree( pdfData );
    1687               0 :       CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
    1688               0 :       return NULL;
    1689                 :   }
    1690                 :     }
    1691                 : 
    1692              12 :     VSIFree( pdfData );
    1693                 : 
    1694                 :     /* write out the min and max values */
    1695              12 :     std::ostringstream ssRange;
    1696              12 :     ssRange.precision( nFIELD_PRECISION );
    1697              12 :     ssRange.setf( std::ios::uppercase );
    1698              12 :     ssRange << dfMin << " " << dfMax << "\x0D\x0A";
    1699              12 :     if( ssRange.str().length() != nDummyRangeLen )
    1700                 :     {
    1701              12 :   int nShiftSize = ssRange.str().length() - nDummyRangeLen;
    1702              12 :   if( ShiftFileContents( fp, nRangeStart + nDummyRangeLen,
    1703                 :              nShiftSize, "\x0D\x0A" ) != CE_None )
    1704                 :   {
    1705               0 :       VSIFCloseL( fp );
    1706                 :       CPLError( CE_Failure, CPLE_FileIO,
    1707               0 :           "Unable to shift file contents.\n" );
    1708               0 :       return NULL;
    1709                 :   }
    1710                 :     }
    1711                 : 
    1712              12 :     if( VSIFSeekL( fp, nRangeStart, SEEK_SET ) != 0 )
    1713                 :     {
    1714               0 :   VSIFCloseL( fp );
    1715                 :   CPLError( CE_Failure, CPLE_FileIO,
    1716               0 :       "Unable to seek to start of grid file copy.\n" );
    1717               0 :   return NULL;
    1718                 :     }
    1719                 : 
    1720              12 :     if( VSIFWriteL( (void *)ssRange.str().c_str(), 1, ssRange.str().length(),
    1721                 :         fp ) != ssRange.str().length() )
    1722                 :     {
    1723               0 :   VSIFCloseL( fp );
    1724                 :         CPLError( CE_Failure, CPLE_FileIO,
    1725               0 :                   "Unable to write range information.\n" );
    1726               0 :         return NULL;
    1727                 :     }
    1728                 : 
    1729              12 :     VSIFCloseL( fp );
    1730                 : 
    1731                 :     GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen( pszFilename,
    1732              12 :                                                 GA_Update );
    1733              12 :     if (poDS)
    1734                 :     {
    1735              12 :         poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
    1736                 :     }
    1737              12 :     return poDS;
    1738                 : }
    1739                 : 
    1740                 : /************************************************************************/
    1741                 : /*                          GDALRegister_GSAG()                          */
    1742                 : /************************************************************************/
    1743                 : 
    1744             582 : void GDALRegister_GSAG()
    1745                 : 
    1746                 : {
    1747                 :     GDALDriver  *poDriver;
    1748                 : 
    1749             582 :     if( GDALGetDriverByName( "GSAG" ) == NULL )
    1750                 :     {
    1751             561 :         poDriver = new GDALDriver();
    1752                 :         
    1753             561 :         poDriver->SetDescription( "GSAG" );
    1754                 :         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, 
    1755             561 :                                    "Golden Software ASCII Grid (.grd)" );
    1756                 :         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, 
    1757             561 :                                    "frmt_various.html#GSAG" );
    1758             561 :         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grd" );
    1759                 :   poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
    1760                 :            "Byte Int16 UInt16 Int32 UInt32 "
    1761             561 :            "Float32 Float64" );
    1762             561 :         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
    1763                 : 
    1764             561 :         poDriver->pfnIdentify = GSAGDataset::Identify;
    1765             561 :         poDriver->pfnOpen = GSAGDataset::Open;
    1766             561 :         poDriver->pfnCreateCopy = GSAGDataset::CreateCopy;
    1767                 : 
    1768             561 :         GetGDALDriverManager()->RegisterDriver( poDriver );
    1769                 :     }
    1770             582 : }

Generated by: LCOV version 1.7