LCOV - code coverage report
Current view: top level - frmts/gsg - gsagdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 329 784 42.0 %
Date: 2024-05-06 13:02:59 Functions: 15 21 71.4 %

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

Generated by: LCOV version 1.14