LCOV - code coverage report
Current view: top level - frmts/gsg - gs7bgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 368 559 65.8 %
Date: 2024-11-21 22:18:42 Functions: 18 20 90.0 %

          Line data    Source code
       1             : /****************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Implements the Golden Software Surfer 7 Binary Grid Format.
       5             :  * Author:   Adam Guernsey, adam@ctech.com
       6             :  *           (Based almost entirely on gsbgdataset.cpp by Kevin Locke)
       7             :  *           Create functions added by Russell Jurgensen.
       8             :  *
       9             :  ****************************************************************************
      10             :  * Copyright (c) 2007, Adam Guernsey <adam@ctech.com>
      11             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include <cassert>
      17             : #include <cfloat>
      18             : #include <climits>
      19             : #include <cmath>
      20             : #include <limits>
      21             : 
      22             : #include "gdal_frmts.h"
      23             : #include "gdal_pam.h"
      24             : 
      25             : /************************************************************************/
      26             : /* ==================================================================== */
      27             : /*                GS7BGDataset                */
      28             : /* ==================================================================== */
      29             : /************************************************************************/
      30             : 
      31             : class GS7BGRasterBand;
      32             : 
      33             : constexpr double dfDefaultNoDataValue = 1.701410009187828e+38f;
      34             : 
      35             : class GS7BGDataset final : public GDALPamDataset
      36             : {
      37             :     friend class GS7BGRasterBand;
      38             : 
      39             :     double dfNoData_Value;
      40             :     static const size_t nHEADER_SIZE;
      41             :     size_t nData_Position;
      42             : 
      43             :     static CPLErr WriteHeader(VSILFILE *fp, GInt32 nXSize, GInt32 nYSize,
      44             :                               double dfMinX, double dfMaxX, double dfMinY,
      45             :                               double dfMaxY, double dfMinZ, double dfMaxZ);
      46             : 
      47             :     VSILFILE *fp;
      48             : 
      49             :   public:
      50          33 :     GS7BGDataset()
      51          33 :         : /* NOTE:  This is not mentioned in the spec, but Surfer 8 uses this
      52             :              value */
      53             :           /* 0x7effffee (Little Endian: eeffff7e) */
      54          33 :           dfNoData_Value(dfDefaultNoDataValue), nData_Position(0), fp(nullptr)
      55             :     {
      56          33 :     }
      57             : 
      58             :     ~GS7BGDataset();
      59             : 
      60             :     static int Identify(GDALOpenInfo *);
      61             :     static GDALDataset *Open(GDALOpenInfo *);
      62             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      63             :                                int nBandsIn, GDALDataType eType,
      64             :                                char **papszParamList);
      65             :     static GDALDataset *CreateCopy(const char *pszFilename,
      66             :                                    GDALDataset *poSrcDS, int bStrict,
      67             :                                    char **papszOptions,
      68             :                                    GDALProgressFunc pfnProgress,
      69             :                                    void *pProgressData);
      70             : 
      71             :     CPLErr GetGeoTransform(double *padfGeoTransform) override;
      72             :     CPLErr SetGeoTransform(double *padfGeoTransform) override;
      73             : };
      74             : 
      75             : const size_t GS7BGDataset::nHEADER_SIZE = 100;
      76             : 
      77             : constexpr long nHEADER_TAG = 0x42525344;
      78             : constexpr long nGRID_TAG = 0x44495247;
      79             : constexpr long nDATA_TAG = 0x41544144;
      80             : #if 0 /* Unused */
      81             : const long  nFAULT_TAG = 0x49544c46;
      82             : #endif
      83             : 
      84             : /************************************************************************/
      85             : /* ==================================================================== */
      86             : /*                            GS7BGRasterBand                           */
      87             : /* ==================================================================== */
      88             : /************************************************************************/
      89             : 
      90             : class GS7BGRasterBand final : public GDALPamRasterBand
      91             : {
      92             :     friend class GS7BGDataset;
      93             : 
      94             :     double dfMinX;
      95             :     double dfMaxX;
      96             :     double dfMinY;
      97             :     double dfMaxY;
      98             :     double dfMinZ;
      99             :     double dfMaxZ;
     100             : 
     101             :     double *pafRowMinZ;
     102             :     double *pafRowMaxZ;
     103             :     int nMinZRow;
     104             :     int nMaxZRow;
     105             : 
     106             :     CPLErr ScanForMinMaxZ();
     107             : 
     108             :   public:
     109             :     GS7BGRasterBand(GS7BGDataset *, int);
     110             :     ~GS7BGRasterBand();
     111             : 
     112             :     CPLErr IReadBlock(int, int, void *) override;
     113             :     CPLErr IWriteBlock(int, int, void *) override;
     114             :     double GetMinimum(int *pbSuccess = nullptr) override;
     115             :     double GetMaximum(int *pbSuccess = nullptr) override;
     116             : 
     117             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     118             : };
     119             : 
     120             : /************************************************************************/
     121             : /*                           GS7BGRasterBand()                          */
     122             : /************************************************************************/
     123             : 
     124          33 : GS7BGRasterBand::GS7BGRasterBand(GS7BGDataset *poDSIn, int nBandIn)
     125             :     : dfMinX(0.0), dfMaxX(0.0), dfMinY(0.0), dfMaxY(0.0), dfMinZ(0.0),
     126             :       dfMaxZ(0.0), pafRowMinZ(nullptr), pafRowMaxZ(nullptr), nMinZRow(-1),
     127          33 :       nMaxZRow(-1)
     128             : 
     129             : {
     130          33 :     poDS = poDSIn;
     131          33 :     nBand = nBandIn;
     132             : 
     133          33 :     eDataType = GDT_Float64;
     134             : 
     135          33 :     nBlockXSize = poDS->GetRasterXSize();
     136          33 :     nBlockYSize = 1;
     137          33 : }
     138             : 
     139             : /************************************************************************/
     140             : /*                           ~GSBGRasterBand()                          */
     141             : /************************************************************************/
     142             : 
     143          66 : GS7BGRasterBand::~GS7BGRasterBand()
     144             : 
     145             : {
     146          33 :     CPLFree(pafRowMinZ);
     147          33 :     CPLFree(pafRowMaxZ);
     148          66 : }
     149             : 
     150             : /************************************************************************/
     151             : /*                          ScanForMinMaxZ()                            */
     152             : /************************************************************************/
     153             : 
     154           1 : CPLErr GS7BGRasterBand::ScanForMinMaxZ()
     155             : 
     156             : {
     157           1 :     GS7BGDataset *poGDS = reinterpret_cast<GS7BGDataset *>(poDS);
     158             :     double *pafRowVals =
     159           1 :         (double *)VSI_MALLOC2_VERBOSE(nRasterXSize, sizeof(double));
     160             : 
     161           1 :     if (pafRowVals == nullptr)
     162             :     {
     163           0 :         return CE_Failure;
     164             :     }
     165             : 
     166           1 :     double dfNewMinZ = std::numeric_limits<double>::max();
     167           1 :     double dfNewMaxZ = std::numeric_limits<double>::lowest();
     168           1 :     int nNewMinZRow = 0;
     169           1 :     int nNewMaxZRow = 0;
     170             : 
     171             :     /* Since we have to scan, lets calc. statistics too */
     172           1 :     double dfSum = 0.0;
     173           1 :     double dfSum2 = 0.0;
     174           1 :     unsigned long nValuesRead = 0;
     175          21 :     for (int iRow = 0; iRow < nRasterYSize; iRow++)
     176             :     {
     177          20 :         CPLErr eErr = IReadBlock(0, iRow, pafRowVals);
     178          20 :         if (eErr != CE_None)
     179             :         {
     180           0 :             VSIFree(pafRowVals);
     181           0 :             return CE_Failure;
     182             :         }
     183             : 
     184          20 :         pafRowMinZ[iRow] = std::numeric_limits<float>::max();
     185          20 :         pafRowMaxZ[iRow] = std::numeric_limits<float>::lowest();
     186         420 :         for (int iCol = 0; iCol < nRasterXSize; iCol++)
     187             :         {
     188         400 :             if (pafRowVals[iCol] == poGDS->dfNoData_Value)
     189         400 :                 continue;
     190             : 
     191           0 :             if (pafRowVals[iCol] < pafRowMinZ[iRow])
     192           0 :                 pafRowMinZ[iRow] = pafRowVals[iCol];
     193             : 
     194           0 :             if (pafRowVals[iCol] > pafRowMinZ[iRow])
     195           0 :                 pafRowMaxZ[iRow] = pafRowVals[iCol];
     196             : 
     197           0 :             dfSum += pafRowVals[iCol];
     198           0 :             dfSum2 += pafRowVals[iCol] * pafRowVals[iCol];
     199           0 :             nValuesRead++;
     200             :         }
     201             : 
     202          20 :         if (pafRowMinZ[iRow] < dfNewMinZ)
     203             :         {
     204           1 :             dfNewMinZ = pafRowMinZ[iRow];
     205           1 :             nNewMinZRow = iRow;
     206             :         }
     207             : 
     208          20 :         if (pafRowMaxZ[iRow] > dfNewMaxZ)
     209             :         {
     210           1 :             dfNewMaxZ = pafRowMaxZ[iRow];
     211           1 :             nNewMaxZRow = iRow;
     212             :         }
     213             :     }
     214             : 
     215           1 :     VSIFree(pafRowVals);
     216             : 
     217           1 :     if (nValuesRead == 0)
     218             :     {
     219           1 :         dfMinZ = 0.0;
     220           1 :         dfMaxZ = 0.0;
     221           1 :         nMinZRow = 0;
     222           1 :         nMaxZRow = 0;
     223           1 :         return CE_None;
     224             :     }
     225             : 
     226           0 :     dfMinZ = dfNewMinZ;
     227           0 :     dfMaxZ = dfNewMaxZ;
     228           0 :     nMinZRow = nNewMinZRow;
     229           0 :     nMaxZRow = nNewMaxZRow;
     230             : 
     231           0 :     double dfMean = dfSum / nValuesRead;
     232           0 :     double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean));
     233           0 :     SetStatistics(dfMinZ, dfMaxZ, dfMean, dfStdDev);
     234             : 
     235           0 :     return CE_None;
     236             : }
     237             : 
     238             : /************************************************************************/
     239             : /*                             IReadBlock()                             */
     240             : /************************************************************************/
     241             : 
     242         140 : CPLErr GS7BGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     243             : 
     244             : {
     245         140 :     if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
     246           0 :         return CE_Failure;
     247             : 
     248         140 :     GS7BGDataset *poGDS = (GS7BGDataset *)(poDS);
     249             : 
     250         280 :     if (VSIFSeekL(poGDS->fp,
     251         140 :                   (poGDS->nData_Position +
     252         140 :                    sizeof(double) * static_cast<vsi_l_offset>(nRasterXSize) *
     253         140 :                        (nRasterYSize - nBlockYOff - 1)),
     254         140 :                   SEEK_SET) != 0)
     255             :     {
     256           0 :         CPLError(CE_Failure, CPLE_FileIO,
     257             :                  "Unable to seek to beginning of grid row.\n");
     258           0 :         return CE_Failure;
     259             :     }
     260             : 
     261         140 :     if (VSIFReadL(pImage, sizeof(double), nBlockXSize, poGDS->fp) !=
     262         140 :         static_cast<unsigned>(nBlockXSize))
     263             :     {
     264           0 :         CPLError(CE_Failure, CPLE_FileIO,
     265             :                  "Unable to read block from grid file.\n");
     266           0 :         return CE_Failure;
     267             :     }
     268             : 
     269             : #ifdef CPL_MSB
     270             :     double *pfImage = (double *)pImage;
     271             :     for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
     272             :         CPL_LSBPTR64(pfImage + iPixel);
     273             : #endif
     274             : 
     275         140 :     return CE_None;
     276             : }
     277             : 
     278             : /************************************************************************/
     279             : /*                            IWriteBlock()                             */
     280             : /************************************************************************/
     281             : 
     282          20 : CPLErr GS7BGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
     283             :                                     void *pImage)
     284             : 
     285             : {
     286          20 :     if (eAccess == GA_ReadOnly)
     287             :     {
     288           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     289             :                  "Unable to write block, dataset opened read only.\n");
     290           0 :         return CE_Failure;
     291             :     }
     292             : 
     293          20 :     if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
     294           0 :         return CE_Failure;
     295             : 
     296          20 :     GS7BGDataset *poGDS = (GS7BGDataset *)(poDS);
     297             : 
     298          20 :     if (pafRowMinZ == nullptr || pafRowMaxZ == nullptr || nMinZRow < 0 ||
     299          19 :         nMaxZRow < 0)
     300             :     {
     301           1 :         pafRowMinZ =
     302           1 :             (double *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(double));
     303           1 :         if (pafRowMinZ == nullptr)
     304             :         {
     305           0 :             return CE_Failure;
     306             :         }
     307             : 
     308           1 :         pafRowMaxZ =
     309           1 :             (double *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(double));
     310           1 :         if (pafRowMaxZ == nullptr)
     311             :         {
     312           0 :             VSIFree(pafRowMinZ);
     313           0 :             pafRowMinZ = nullptr;
     314           0 :             return CE_Failure;
     315             :         }
     316             : 
     317           1 :         CPLErr eErr = ScanForMinMaxZ();
     318           1 :         if (eErr != CE_None)
     319           0 :             return eErr;
     320             :     }
     321             : 
     322          40 :     if (VSIFSeekL(poGDS->fp,
     323          20 :                   GS7BGDataset::nHEADER_SIZE +
     324          20 :                       sizeof(double) * nRasterXSize *
     325          20 :                           (nRasterYSize - nBlockYOff - 1),
     326          20 :                   SEEK_SET) != 0)
     327             :     {
     328           0 :         CPLError(CE_Failure, CPLE_FileIO,
     329             :                  "Unable to seek to beginning of grid row.\n");
     330           0 :         return CE_Failure;
     331             :     }
     332             : 
     333          20 :     double *pdfImage = (double *)pImage;
     334          20 :     pafRowMinZ[nBlockYOff] = std::numeric_limits<double>::max();
     335          20 :     pafRowMaxZ[nBlockYOff] = std::numeric_limits<double>::lowest();
     336         420 :     for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
     337             :     {
     338         400 :         if (pdfImage[iPixel] != poGDS->dfNoData_Value)
     339             :         {
     340         400 :             if (pdfImage[iPixel] < pafRowMinZ[nBlockYOff])
     341          78 :                 pafRowMinZ[nBlockYOff] = pdfImage[iPixel];
     342             : 
     343         400 :             if (pdfImage[iPixel] > pafRowMaxZ[nBlockYOff])
     344          43 :                 pafRowMaxZ[nBlockYOff] = pdfImage[iPixel];
     345             :         }
     346             : 
     347         400 :         CPL_LSBPTR64(pdfImage + iPixel);
     348             :     }
     349             : 
     350          20 :     if (VSIFWriteL(pImage, sizeof(double), nBlockXSize, poGDS->fp) !=
     351          20 :         static_cast<unsigned>(nBlockXSize))
     352             :     {
     353           0 :         CPLError(CE_Failure, CPLE_FileIO,
     354             :                  "Unable to write block to grid file.\n");
     355           0 :         return CE_Failure;
     356             :     }
     357             : 
     358             :     /* Update min/max Z values as appropriate */
     359          20 :     bool bHeaderNeedsUpdate = false;
     360          20 :     if (nMinZRow == nBlockYOff && pafRowMinZ[nBlockYOff] > dfMinZ)
     361             :     {
     362           1 :         double dfNewMinZ = std::numeric_limits<double>::max();
     363          21 :         for (int iRow = 0; iRow < nRasterYSize; iRow++)
     364             :         {
     365          20 :             if (pafRowMinZ[iRow] < dfNewMinZ)
     366             :             {
     367           1 :                 dfNewMinZ = pafRowMinZ[iRow];
     368           1 :                 nMinZRow = iRow;
     369             :             }
     370             :         }
     371             : 
     372           1 :         if (dfNewMinZ != dfMinZ)
     373             :         {
     374           1 :             dfMinZ = dfNewMinZ;
     375           1 :             bHeaderNeedsUpdate = true;
     376             :         }
     377             :     }
     378             : 
     379          20 :     if (nMaxZRow == nBlockYOff && pafRowMaxZ[nBlockYOff] < dfMaxZ)
     380             :     {
     381           0 :         double dfNewMaxZ = std::numeric_limits<double>::lowest();
     382           0 :         for (int iRow = 0; iRow < nRasterYSize; iRow++)
     383             :         {
     384           0 :             if (pafRowMaxZ[iRow] > dfNewMaxZ)
     385             :             {
     386           0 :                 dfNewMaxZ = pafRowMaxZ[iRow];
     387           0 :                 nMaxZRow = iRow;
     388             :             }
     389             :         }
     390             : 
     391           0 :         if (dfNewMaxZ != dfMaxZ)
     392             :         {
     393           0 :             dfMaxZ = dfNewMaxZ;
     394           0 :             bHeaderNeedsUpdate = true;
     395             :         }
     396             :     }
     397             : 
     398          20 :     if (pafRowMinZ[nBlockYOff] < dfMinZ || pafRowMaxZ[nBlockYOff] > dfMaxZ)
     399             :     {
     400           6 :         if (pafRowMinZ[nBlockYOff] < dfMinZ)
     401             :         {
     402           3 :             dfMinZ = pafRowMinZ[nBlockYOff];
     403           3 :             nMinZRow = nBlockYOff;
     404             :         }
     405             : 
     406           6 :         if (pafRowMaxZ[nBlockYOff] > dfMaxZ)
     407             :         {
     408           4 :             dfMaxZ = pafRowMaxZ[nBlockYOff];
     409           4 :             nMaxZRow = nBlockYOff;
     410             :         }
     411             : 
     412           6 :         bHeaderNeedsUpdate = true;
     413             :     }
     414             : 
     415          20 :     if (bHeaderNeedsUpdate && dfMaxZ > dfMinZ)
     416             :     {
     417             :         CPLErr eErr =
     418           6 :             poGDS->WriteHeader(poGDS->fp, nRasterXSize, nRasterYSize, dfMinX,
     419             :                                dfMaxX, dfMinY, dfMaxY, dfMinZ, dfMaxZ);
     420           6 :         return eErr;
     421             :     }
     422             : 
     423          14 :     return CE_None;
     424             : }
     425             : 
     426             : /************************************************************************/
     427             : /*                           GetNoDataValue()                           */
     428             : /************************************************************************/
     429             : 
     430          11 : double GS7BGRasterBand::GetNoDataValue(int *pbSuccess)
     431             : {
     432          11 :     GS7BGDataset *poGDS = reinterpret_cast<GS7BGDataset *>(poDS);
     433          11 :     if (pbSuccess)
     434          10 :         *pbSuccess = TRUE;
     435             : 
     436          11 :     return poGDS->dfNoData_Value;
     437             : }
     438             : 
     439             : /************************************************************************/
     440             : /*                             GetMinimum()                             */
     441             : /************************************************************************/
     442             : 
     443           0 : double GS7BGRasterBand::GetMinimum(int *pbSuccess)
     444             : {
     445           0 :     if (pbSuccess)
     446           0 :         *pbSuccess = TRUE;
     447             : 
     448           0 :     return dfMinZ;
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                             GetMaximum()                             */
     453             : /************************************************************************/
     454             : 
     455           0 : double GS7BGRasterBand::GetMaximum(int *pbSuccess)
     456             : {
     457           0 :     if (pbSuccess)
     458           0 :         *pbSuccess = TRUE;
     459             : 
     460           0 :     return dfMaxZ;
     461             : }
     462             : 
     463             : /************************************************************************/
     464             : /* ==================================================================== */
     465             : /*                            GS7BGDataset                              */
     466             : /* ==================================================================== */
     467             : /************************************************************************/
     468             : 
     469          66 : GS7BGDataset::~GS7BGDataset()
     470             : 
     471             : {
     472          33 :     FlushCache(true);
     473          33 :     if (fp != nullptr)
     474          33 :         VSIFCloseL(fp);
     475          66 : }
     476             : 
     477             : /************************************************************************/
     478             : /*                              Identify()                              */
     479             : /************************************************************************/
     480             : 
     481       52914 : int GS7BGDataset::Identify(GDALOpenInfo *poOpenInfo)
     482             : 
     483             : {
     484             :     /* Check for signature - for GS7BG the signature is the */
     485             :     /* nHEADER_TAG with reverse byte order.                 */
     486       52914 :     if (poOpenInfo->nHeaderBytes < 4 ||
     487        4516 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "DSRB"))
     488             :     {
     489       52848 :         return FALSE;
     490             :     }
     491             : 
     492          66 :     return TRUE;
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                                Open()                                */
     497             : /************************************************************************/
     498             : 
     499          33 : GDALDataset *GS7BGDataset::Open(GDALOpenInfo *poOpenInfo)
     500             : 
     501             : {
     502          33 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     503             :     {
     504           0 :         return nullptr;
     505             :     }
     506             : 
     507             :     /* ------------------------------------------------------------------- */
     508             :     /*      Create a corresponding GDALDataset.                            */
     509             :     /* ------------------------------------------------------------------- */
     510          33 :     GS7BGDataset *poDS = new GS7BGDataset();
     511          33 :     poDS->eAccess = poOpenInfo->eAccess;
     512          33 :     poDS->fp = poOpenInfo->fpL;
     513          33 :     poOpenInfo->fpL = nullptr;
     514             : 
     515             :     /* ------------------------------------------------------------------- */
     516             :     /*      Read the header. The Header section must be the first section  */
     517             :     /*      in the file.                                                   */
     518             :     /* ------------------------------------------------------------------- */
     519          33 :     if (VSIFSeekL(poDS->fp, 0, SEEK_SET) != 0)
     520             :     {
     521           0 :         delete poDS;
     522           0 :         CPLError(CE_Failure, CPLE_FileIO,
     523             :                  "Unable to seek to start of grid file header.\n");
     524           0 :         return nullptr;
     525             :     }
     526             : 
     527             :     GInt32 nTag;
     528          33 :     if (VSIFReadL((void *)&nTag, sizeof(GInt32), 1, poDS->fp) != 1)
     529             :     {
     530           0 :         delete poDS;
     531           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read Tag.\n");
     532           0 :         return nullptr;
     533             :     }
     534             : 
     535          33 :     CPL_LSBPTR32(&nTag);
     536             : 
     537          33 :     if (nTag != nHEADER_TAG)
     538             :     {
     539           0 :         delete poDS;
     540           0 :         CPLError(CE_Failure, CPLE_FileIO, "Header tag not found.\n");
     541           0 :         return nullptr;
     542             :     }
     543             : 
     544             :     GUInt32 nSize;
     545          33 :     if (VSIFReadL((void *)&nSize, sizeof(GUInt32), 1, poDS->fp) != 1)
     546             :     {
     547           0 :         delete poDS;
     548           0 :         CPLError(CE_Failure, CPLE_FileIO,
     549             :                  "Unable to read file section size.\n");
     550           0 :         return nullptr;
     551             :     }
     552             : 
     553          33 :     CPL_LSBPTR32(&nSize);
     554             : 
     555             :     GInt32 nVersion;
     556          33 :     if (VSIFReadL((void *)&nVersion, sizeof(GInt32), 1, poDS->fp) != 1)
     557             :     {
     558           0 :         delete poDS;
     559           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read file version.\n");
     560           0 :         return nullptr;
     561             :     }
     562             : 
     563          33 :     CPL_LSBPTR32(&nVersion);
     564             : 
     565          33 :     if (nVersion != 1 && nVersion != 2)
     566             :     {
     567           0 :         delete poDS;
     568           0 :         CPLError(CE_Failure, CPLE_FileIO, "Incorrect file version (%d).",
     569             :                  nVersion);
     570           0 :         return nullptr;
     571             :     }
     572             : 
     573             :     // advance until the grid tag is found
     574          66 :     while (nTag != nGRID_TAG)
     575             :     {
     576          33 :         if (VSIFReadL((void *)&nTag, sizeof(GInt32), 1, poDS->fp) != 1)
     577             :         {
     578           0 :             delete poDS;
     579           0 :             CPLError(CE_Failure, CPLE_FileIO, "Unable to read Tag.\n");
     580           0 :             return nullptr;
     581             :         }
     582             : 
     583          33 :         CPL_LSBPTR32(&nTag);
     584             : 
     585          33 :         if (VSIFReadL((void *)&nSize, sizeof(GUInt32), 1, poDS->fp) != 1)
     586             :         {
     587           0 :             delete poDS;
     588           0 :             CPLError(CE_Failure, CPLE_FileIO,
     589             :                      "Unable to read file section size.\n");
     590           0 :             return nullptr;
     591             :         }
     592             : 
     593          33 :         CPL_LSBPTR32(&nSize);
     594             : 
     595          33 :         if (nTag != nGRID_TAG)
     596             :         {
     597           0 :             if (VSIFSeekL(poDS->fp, nSize, SEEK_CUR) != 0)
     598             :             {
     599           0 :                 delete poDS;
     600           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     601             :                          "Unable to seek to end of file section.\n");
     602           0 :                 return nullptr;
     603             :             }
     604             :         }
     605             :     }
     606             : 
     607             :     /* --------------------------------------------------------------------*/
     608             :     /*      Read the grid.                                                 */
     609             :     /* --------------------------------------------------------------------*/
     610             :     /* Parse number of Y axis grid rows */
     611             :     GInt32 nRows;
     612          33 :     if (VSIFReadL((void *)&nRows, sizeof(GInt32), 1, poDS->fp) != 1)
     613             :     {
     614           0 :         delete poDS;
     615           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster Y size.\n");
     616           0 :         return nullptr;
     617             :     }
     618          33 :     CPL_LSBPTR32(&nRows);
     619          33 :     poDS->nRasterYSize = nRows;
     620             : 
     621             :     /* Parse number of X axis grid columns */
     622             :     GInt32 nCols;
     623          33 :     if (VSIFReadL((void *)&nCols, sizeof(GInt32), 1, poDS->fp) != 1)
     624             :     {
     625           0 :         delete poDS;
     626           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster X size.\n");
     627           0 :         return nullptr;
     628             :     }
     629          33 :     CPL_LSBPTR32(&nCols);
     630          33 :     poDS->nRasterXSize = nCols;
     631             : 
     632          33 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     633             :     {
     634           0 :         delete poDS;
     635           0 :         return nullptr;
     636             :     }
     637             : 
     638             :     /* --------------------------------------------------------------------*/
     639             :     /*      Create band information objects.                               */
     640             :     /* --------------------------------------------------------------------*/
     641          33 :     GS7BGRasterBand *poBand = new GS7BGRasterBand(poDS, 1);
     642          33 :     poDS->SetBand(1, poBand);
     643             : 
     644             :     // find the min X Value of the grid
     645             :     double dfTemp;
     646          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     647             :     {
     648           0 :         delete poDS;
     649           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.\n");
     650           0 :         return nullptr;
     651             :     }
     652          33 :     CPL_LSBPTR64(&dfTemp);
     653          33 :     poBand->dfMinX = dfTemp;
     654             : 
     655             :     // find the min Y value of the grid
     656          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     657             :     {
     658           0 :         delete poDS;
     659           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.\n");
     660           0 :         return nullptr;
     661             :     }
     662          33 :     CPL_LSBPTR64(&dfTemp);
     663          33 :     poBand->dfMinY = dfTemp;
     664             : 
     665             :     // find the spacing between adjacent nodes in the X direction
     666             :     // (between columns)
     667          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     668             :     {
     669           0 :         delete poDS;
     670           0 :         CPLError(CE_Failure, CPLE_FileIO,
     671             :                  "Unable to read spacing in X value.\n");
     672           0 :         return nullptr;
     673             :     }
     674          33 :     CPL_LSBPTR64(&dfTemp);
     675          33 :     poBand->dfMaxX = poBand->dfMinX + (dfTemp * (nCols - 1));
     676             : 
     677             :     // find the spacing between adjacent nodes in the Y direction
     678             :     // (between rows)
     679          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     680             :     {
     681           0 :         delete poDS;
     682           0 :         CPLError(CE_Failure, CPLE_FileIO,
     683             :                  "Unable to read spacing in Y value.\n");
     684           0 :         return nullptr;
     685             :     }
     686          33 :     CPL_LSBPTR64(&dfTemp);
     687          33 :     poBand->dfMaxY = poBand->dfMinY + (dfTemp * (nRows - 1));
     688             : 
     689             :     // set the z min
     690          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     691             :     {
     692           0 :         delete poDS;
     693           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read Z min value.\n");
     694           0 :         return nullptr;
     695             :     }
     696          33 :     CPL_LSBPTR64(&dfTemp);
     697          33 :     poBand->dfMinZ = dfTemp;
     698             : 
     699             :     // set the z max
     700          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     701             :     {
     702           0 :         delete poDS;
     703           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read Z max value.\n");
     704           0 :         return nullptr;
     705             :     }
     706          33 :     CPL_LSBPTR64(&dfTemp);
     707          33 :     poBand->dfMaxZ = dfTemp;
     708             : 
     709             :     // read and ignore the rotation value
     710             :     //(This is not used in the current version).
     711          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     712             :     {
     713           0 :         delete poDS;
     714           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read rotation value.\n");
     715           0 :         return nullptr;
     716             :     }
     717             : 
     718             :     // read and set the cell blank value
     719          33 :     if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
     720             :     {
     721           0 :         delete poDS;
     722           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to Blank value.\n");
     723           0 :         return nullptr;
     724             :     }
     725          33 :     CPL_LSBPTR64(&dfTemp);
     726          33 :     poDS->dfNoData_Value = dfTemp;
     727             : 
     728             :     /* --------------------------------------------------------------------*/
     729             :     /*      Set the current offset of the grid data.                       */
     730             :     /* --------------------------------------------------------------------*/
     731          33 :     if (VSIFReadL((void *)&nTag, sizeof(GInt32), 1, poDS->fp) != 1)
     732             :     {
     733           0 :         delete poDS;
     734           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read Tag.\n");
     735           0 :         return nullptr;
     736             :     }
     737             : 
     738          33 :     CPL_LSBPTR32(&nTag);
     739          33 :     if (nTag != nDATA_TAG)
     740             :     {
     741           0 :         delete poDS;
     742           0 :         CPLError(CE_Failure, CPLE_FileIO, "Data tag not found.\n");
     743           0 :         return nullptr;
     744             :     }
     745             : 
     746          33 :     if (VSIFReadL((void *)&nSize, sizeof(GInt32), 1, poDS->fp) != 1)
     747             :     {
     748           0 :         delete poDS;
     749           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to data section size.\n");
     750           0 :         return nullptr;
     751             :     }
     752             : 
     753          33 :     poDS->nData_Position = (size_t)VSIFTellL(poDS->fp);
     754             : 
     755             :     /* --------------------------------------------------------------------*/
     756             :     /*      Initialize any PAM information.                                */
     757             :     /* --------------------------------------------------------------------*/
     758          33 :     poDS->SetDescription(poOpenInfo->pszFilename);
     759          33 :     poDS->TryLoadXML();
     760             : 
     761             :     /* -------------------------------------------------------------------- */
     762             :     /*      Check for external overviews.                                   */
     763             :     /* -------------------------------------------------------------------- */
     764          66 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
     765          33 :                                 poOpenInfo->GetSiblingFiles());
     766             : 
     767          33 :     return poDS;
     768             : }
     769             : 
     770             : /************************************************************************/
     771             : /*                          GetGeoTransform()                           */
     772             : /************************************************************************/
     773             : 
     774          23 : CPLErr GS7BGDataset::GetGeoTransform(double *padfGeoTransform)
     775             : {
     776          23 :     if (padfGeoTransform == nullptr)
     777           0 :         return CE_Failure;
     778             : 
     779          23 :     GS7BGRasterBand *poGRB = (GS7BGRasterBand *)GetRasterBand(1);
     780             : 
     781          23 :     if (poGRB == nullptr)
     782             :     {
     783           0 :         padfGeoTransform[0] = 0;
     784           0 :         padfGeoTransform[1] = 1;
     785           0 :         padfGeoTransform[2] = 0;
     786           0 :         padfGeoTransform[3] = 0;
     787           0 :         padfGeoTransform[4] = 0;
     788           0 :         padfGeoTransform[5] = 1;
     789           0 :         return CE_Failure;
     790             :     }
     791             : 
     792             :     /* check if we have a PAM GeoTransform stored */
     793          23 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     794          23 :     CPLErr eErr = GDALPamDataset::GetGeoTransform(padfGeoTransform);
     795          23 :     CPLPopErrorHandler();
     796             : 
     797          23 :     if (eErr == CE_None)
     798           0 :         return CE_None;
     799             : 
     800          23 :     if (nRasterXSize == 1 || nRasterYSize == 1)
     801           0 :         return CE_Failure;
     802             : 
     803             :     /* calculate pixel size first */
     804          23 :     padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX) / (nRasterXSize - 1);
     805          23 :     padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY) / (nRasterYSize - 1);
     806             : 
     807             :     /* then calculate image origin */
     808          23 :     padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2;
     809          23 :     padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2;
     810             : 
     811             :     /* tilt/rotation does not supported by the GS grids */
     812          23 :     padfGeoTransform[4] = 0.0;
     813          23 :     padfGeoTransform[2] = 0.0;
     814             : 
     815          23 :     return CE_None;
     816             : }
     817             : 
     818             : /************************************************************************/
     819             : /*                          SetGeoTransform()                           */
     820             : /************************************************************************/
     821             : 
     822           6 : CPLErr GS7BGDataset::SetGeoTransform(double *padfGeoTransform)
     823             : {
     824           6 :     if (eAccess == GA_ReadOnly)
     825             :     {
     826           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     827             :                  "Unable to set GeoTransform, dataset opened read only.\n");
     828           0 :         return CE_Failure;
     829             :     }
     830             : 
     831             :     GS7BGRasterBand *poGRB =
     832           6 :         cpl::down_cast<GS7BGRasterBand *>(GetRasterBand(1));
     833             : 
     834           6 :     if (padfGeoTransform == nullptr)
     835           0 :         return CE_Failure;
     836             : 
     837             :     /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
     838             :     /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0
     839             :     || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 )
     840             :     eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform );
     841             : 
     842             :     if( eErr != CE_None )
     843             :     return eErr;*/
     844             : 
     845           6 :     double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
     846           6 :     double dfMaxX =
     847           6 :         padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0];
     848           6 :     double dfMinY =
     849           6 :         padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
     850           6 :     double dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2;
     851             : 
     852             :     CPLErr eErr =
     853           6 :         WriteHeader(fp, poGRB->nRasterXSize, poGRB->nRasterYSize, dfMinX,
     854             :                     dfMaxX, dfMinY, dfMaxY, poGRB->dfMinZ, poGRB->dfMaxZ);
     855             : 
     856           6 :     if (eErr == CE_None)
     857             :     {
     858           6 :         poGRB->dfMinX = dfMinX;
     859           6 :         poGRB->dfMaxX = dfMaxX;
     860           6 :         poGRB->dfMinY = dfMinY;
     861           6 :         poGRB->dfMaxY = dfMaxY;
     862             :     }
     863             : 
     864           6 :     return eErr;
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                             WriteHeader()                            */
     869             : /************************************************************************/
     870             : 
     871          53 : CPLErr GS7BGDataset::WriteHeader(VSILFILE *fp, GInt32 nXSize, GInt32 nYSize,
     872             :                                  double dfMinX, double dfMaxX, double dfMinY,
     873             :                                  double dfMaxY, double dfMinZ, double dfMaxZ)
     874             : 
     875             : {
     876          53 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
     877             :     {
     878           0 :         CPLError(CE_Failure, CPLE_FileIO,
     879             :                  "Unable to seek to start of grid file.\n");
     880           0 :         return CE_Failure;
     881             :     }
     882             : 
     883          53 :     GInt32 nTemp = CPL_LSBWORD32(nHEADER_TAG);
     884          53 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
     885             :     {
     886           1 :         CPLError(CE_Failure, CPLE_FileIO,
     887             :                  "Unable to write header tag to grid file.\n");
     888           1 :         return CE_Failure;
     889             :     }
     890             : 
     891          52 :     nTemp = CPL_LSBWORD32(sizeof(GInt32));  // Size of version section.
     892          52 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
     893             :     {
     894           0 :         CPLError(CE_Failure, CPLE_FileIO,
     895             :                  "Unable to write size to grid file.\n");
     896           0 :         return CE_Failure;
     897             :     }
     898             : 
     899          52 :     nTemp = CPL_LSBWORD32(1);  // Version
     900          52 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
     901             :     {
     902           0 :         CPLError(CE_Failure, CPLE_FileIO,
     903             :                  "Unable to write size to grid file.\n");
     904           0 :         return CE_Failure;
     905             :     }
     906             : 
     907          52 :     nTemp = CPL_LSBWORD32(nGRID_TAG);  // Mark start of grid
     908          52 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
     909             :     {
     910           0 :         CPLError(CE_Failure, CPLE_FileIO,
     911             :                  "Unable to write size to grid file.\n");
     912           0 :         return CE_Failure;
     913             :     }
     914             : 
     915          52 :     nTemp = CPL_LSBWORD32(72);  // Grid info size (the remainder of the header)
     916          52 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
     917             :     {
     918           0 :         CPLError(CE_Failure, CPLE_FileIO,
     919             :                  "Unable to write size to grid file.\n");
     920           0 :         return CE_Failure;
     921             :     }
     922             : 
     923          52 :     nTemp = CPL_LSBWORD32(nYSize);
     924          52 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
     925             :     {
     926           0 :         CPLError(CE_Failure, CPLE_FileIO,
     927             :                  "Unable to write Y size to grid file.\n");
     928           0 :         return CE_Failure;
     929             :     }
     930             : 
     931          52 :     nTemp = CPL_LSBWORD32(nXSize);
     932          52 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
     933             :     {
     934           0 :         CPLError(CE_Failure, CPLE_FileIO,
     935             :                  "Unable to write X size to grid file.\n");
     936           0 :         return CE_Failure;
     937             :     }
     938             : 
     939          52 :     double dfTemp = dfMinX;
     940          52 :     CPL_LSBPTR64(&dfTemp);
     941          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
     942             :     {
     943           0 :         CPLError(CE_Failure, CPLE_FileIO,
     944             :                  "Unable to write minimum X value to grid file.\n");
     945           0 :         return CE_Failure;
     946             :     }
     947             : 
     948          52 :     dfTemp = dfMinY;
     949          52 :     CPL_LSBPTR64(&dfTemp);
     950          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
     951             :     {
     952           0 :         CPLError(CE_Failure, CPLE_FileIO,
     953             :                  "Unable to write minimum Y value to grid file.\n");
     954           0 :         return CE_Failure;
     955             :     }
     956             : 
     957             :     // Write node spacing in x direction
     958          52 :     dfTemp = (dfMaxX - dfMinX) / (nXSize - 1);
     959          52 :     CPL_LSBPTR64(&dfTemp);
     960          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
     961             :     {
     962           0 :         CPLError(CE_Failure, CPLE_FileIO,
     963             :                  "Unable to write spacing in X value.\n");
     964           0 :         return CE_Failure;
     965             :     }
     966             : 
     967             :     // Write node spacing in y direction
     968          52 :     dfTemp = (dfMaxY - dfMinY) / (nYSize - 1);
     969          52 :     CPL_LSBPTR64(&dfTemp);
     970          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
     971             :     {
     972           0 :         CPLError(CE_Failure, CPLE_FileIO,
     973             :                  "Unable to write spacing in Y value.\n");
     974           0 :         return CE_Failure;
     975             :     }
     976             : 
     977          52 :     dfTemp = dfMinZ;
     978          52 :     CPL_LSBPTR64(&dfTemp);
     979          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
     980             :     {
     981           0 :         CPLError(CE_Failure, CPLE_FileIO,
     982             :                  "Unable to write minimum Z value to grid file.\n");
     983           0 :         return CE_Failure;
     984             :     }
     985             : 
     986          52 :     dfTemp = dfMaxZ;
     987          52 :     CPL_LSBPTR64(&dfTemp);
     988          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
     989             :     {
     990           0 :         CPLError(CE_Failure, CPLE_FileIO,
     991             :                  "Unable to write maximum Z value to grid file.\n");
     992           0 :         return CE_Failure;
     993             :     }
     994             : 
     995          52 :     dfTemp = 0;  // Rotation value is zero
     996          52 :     CPL_LSBPTR64(&dfTemp);
     997          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
     998             :     {
     999           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1000             :                  "Unable to write rotation value to grid file.\n");
    1001           0 :         return CE_Failure;
    1002             :     }
    1003             : 
    1004          52 :     dfTemp = dfDefaultNoDataValue;
    1005          52 :     CPL_LSBPTR64(&dfTemp);
    1006          52 :     if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
    1007             :     {
    1008           1 :         CPLError(CE_Failure, CPLE_FileIO,
    1009             :                  "Unable to write cell blank value to grid file.\n");
    1010           1 :         return CE_Failure;
    1011             :     }
    1012             : 
    1013             :     // Only supports 1 band so go ahead and write band info here
    1014          51 :     nTemp = CPL_LSBWORD32(nDATA_TAG);  // Mark start of data
    1015          51 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
    1016             :     {
    1017           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to data tag to grid file.\n");
    1018           0 :         return CE_Failure;
    1019             :     }
    1020             : 
    1021          51 :     int nSize = nXSize * nYSize * (int)sizeof(double);
    1022          51 :     nTemp = CPL_LSBWORD32(nSize);  // Mark size of data
    1023          51 :     if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
    1024             :     {
    1025           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1026             :                  "Unable to write data size to grid file.\n");
    1027           0 :         return CE_Failure;
    1028             :     }
    1029             : 
    1030          51 :     return CE_None;
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                               Create()                               */
    1035             : /************************************************************************/
    1036             : 
    1037          33 : GDALDataset *GS7BGDataset::Create(const char *pszFilename, int nXSize,
    1038             :                                   int nYSize, int nBandsIn, GDALDataType eType,
    1039             :                                   CPL_UNUSED char **papszParamList)
    1040             : 
    1041             : {
    1042          33 :     if (nXSize <= 0 || nYSize <= 0)
    1043             :     {
    1044           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1045             :                  "Unable to create grid, both X and Y size must be "
    1046             :                  "non-negative.\n");
    1047             : 
    1048           0 :         return nullptr;
    1049             :     }
    1050             : 
    1051          33 :     if (eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 &&
    1052          21 :         eType != GDT_Int16 && eType != GDT_Float64)
    1053             :     {
    1054          18 :         CPLError(
    1055             :             CE_Failure, CPLE_AppDefined,
    1056             :             "GS7BG Grid only supports Byte, Int16, "
    1057             :             "Uint16, Float32, and Float64 datatypes.  Unable to create with "
    1058             :             "type %s.\n",
    1059             :             GDALGetDataTypeName(eType));
    1060             : 
    1061          18 :         return nullptr;
    1062             :     }
    1063             : 
    1064          15 :     if (nBandsIn > 1)
    1065             :     {
    1066           8 :         CPLError(CE_Failure, CPLE_NotSupported,
    1067             :                  "Unable to create copy, "
    1068             :                  "format only supports one raster band.\n");
    1069           8 :         return nullptr;
    1070             :     }
    1071             : 
    1072           7 :     VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
    1073             : 
    1074           7 :     if (fp == nullptr)
    1075             :     {
    1076           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1077             :                  "Attempt to create file '%s' failed.\n", pszFilename);
    1078           0 :         return nullptr;
    1079             :     }
    1080             : 
    1081             :     CPLErr eErr =
    1082           7 :         WriteHeader(fp, nXSize, nYSize, 0.0, nXSize, 0.0, nYSize, 0.0, 0.0);
    1083           7 :     if (eErr != CE_None)
    1084             :     {
    1085           0 :         VSIFCloseL(fp);
    1086           0 :         return nullptr;
    1087             :     }
    1088             : 
    1089           7 :     double dfVal = dfDefaultNoDataValue;
    1090           7 :     CPL_LSBPTR64(&dfVal);
    1091         627 :     for (int iRow = 0; iRow < nYSize; iRow++)
    1092             :     {
    1093       61020 :         for (int iCol = 0; iCol < nXSize; iCol++)
    1094             :         {
    1095       60400 :             if (VSIFWriteL((void *)&dfVal, sizeof(double), 1, fp) != 1)
    1096             :             {
    1097           0 :                 VSIFCloseL(fp);
    1098           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    1099             :                          "Unable to write grid cell.  Disk full?\n");
    1100           0 :                 return nullptr;
    1101             :             }
    1102             :         }
    1103             :     }
    1104             : 
    1105           7 :     VSIFCloseL(fp);
    1106             : 
    1107           7 :     return (GDALDataset *)GDALOpen(pszFilename, GA_Update);
    1108             : }
    1109             : 
    1110             : /************************************************************************/
    1111             : /*                             CreateCopy()                             */
    1112             : /************************************************************************/
    1113             : 
    1114          30 : GDALDataset *GS7BGDataset::CreateCopy(const char *pszFilename,
    1115             :                                       GDALDataset *poSrcDS, int bStrict,
    1116             :                                       CPL_UNUSED char **papszOptions,
    1117             :                                       GDALProgressFunc pfnProgress,
    1118             :                                       void *pProgressData)
    1119             : {
    1120          30 :     if (pfnProgress == nullptr)
    1121           0 :         pfnProgress = GDALDummyProgress;
    1122             : 
    1123          30 :     int nBands = poSrcDS->GetRasterCount();
    1124          30 :     if (nBands == 0)
    1125             :     {
    1126           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1127             :                  "Driver does not support source dataset with zero band.\n");
    1128           1 :         return nullptr;
    1129             :     }
    1130          29 :     else if (nBands > 1)
    1131             :     {
    1132           4 :         if (bStrict)
    1133             :         {
    1134           4 :             CPLError(CE_Failure, CPLE_NotSupported,
    1135             :                      "Unable to create copy, "
    1136             :                      "format only supports one raster band.\n");
    1137           4 :             return nullptr;
    1138             :         }
    1139             :         else
    1140           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1141             :                      "Format only supports one "
    1142             :                      "raster band, first band will be copied.\n");
    1143             :     }
    1144             : 
    1145          25 :     GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
    1146             : 
    1147          25 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    1148             :     {
    1149           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated\n");
    1150           0 :         return nullptr;
    1151             :     }
    1152             : 
    1153          25 :     VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
    1154             : 
    1155          25 :     if (fp == nullptr)
    1156             :     {
    1157           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1158             :                  "Attempt to create file '%s' failed.\n", pszFilename);
    1159           3 :         return nullptr;
    1160             :     }
    1161             : 
    1162          22 :     GInt32 nXSize = poSrcBand->GetXSize();
    1163          22 :     GInt32 nYSize = poSrcBand->GetYSize();
    1164             :     double adfGeoTransform[6];
    1165             : 
    1166          22 :     poSrcDS->GetGeoTransform(adfGeoTransform);
    1167             : 
    1168          22 :     double dfMinX = adfGeoTransform[0] + adfGeoTransform[1] / 2;
    1169          22 :     double dfMaxX = adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0];
    1170          22 :     double dfMinY = adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3];
    1171          22 :     double dfMaxY = adfGeoTransform[3] + adfGeoTransform[5] / 2;
    1172          22 :     CPLErr eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY,
    1173             :                               dfMaxY, 0.0, 0.0);
    1174             : 
    1175          22 :     if (eErr != CE_None)
    1176             :     {
    1177           2 :         VSIFCloseL(fp);
    1178           2 :         return nullptr;
    1179             :     }
    1180             : 
    1181             :     /* -------------------------------------------------------------------- */
    1182             :     /*      Copy band data.                                                 */
    1183             :     /* -------------------------------------------------------------------- */
    1184          20 :     double *pfData = (double *)VSI_MALLOC2_VERBOSE(nXSize, sizeof(double));
    1185          20 :     if (pfData == nullptr)
    1186             :     {
    1187           0 :         VSIFCloseL(fp);
    1188           0 :         return nullptr;
    1189             :     }
    1190             : 
    1191             :     int bSrcHasNDValue;
    1192          20 :     double dfSrcNoDataValue = poSrcBand->GetNoDataValue(&bSrcHasNDValue);
    1193          20 :     double dfMinZ = std::numeric_limits<double>::max();
    1194          20 :     double dfMaxZ = std::numeric_limits<double>::lowest();
    1195         186 :     for (GInt32 iRow = nYSize - 1; iRow >= 0; iRow--)
    1196             :     {
    1197         174 :         eErr = poSrcBand->RasterIO(GF_Read, 0, iRow, nXSize, 1, pfData, nXSize,
    1198             :                                    1, GDT_Float64, 0, 0, nullptr);
    1199             : 
    1200         174 :         if (eErr != CE_None)
    1201             :         {
    1202           0 :             VSIFCloseL(fp);
    1203           0 :             VSIFree(pfData);
    1204           0 :             return nullptr;
    1205             :         }
    1206             : 
    1207        2114 :         for (int iCol = 0; iCol < nXSize; iCol++)
    1208             :         {
    1209        1940 :             if (bSrcHasNDValue && pfData[iCol] == dfSrcNoDataValue)
    1210             :             {
    1211           0 :                 pfData[iCol] = dfDefaultNoDataValue;
    1212             :             }
    1213             :             else
    1214             :             {
    1215        1940 :                 if (pfData[iCol] > dfMaxZ)
    1216          22 :                     dfMaxZ = pfData[iCol];
    1217             : 
    1218        1940 :                 if (pfData[iCol] < dfMinZ)
    1219          27 :                     dfMinZ = pfData[iCol];
    1220             :             }
    1221             : 
    1222        1940 :             CPL_LSBPTR64(pfData + iCol);
    1223             :         }
    1224             : 
    1225         174 :         if (VSIFWriteL((void *)pfData, sizeof(double), nXSize, fp) !=
    1226         174 :             static_cast<unsigned>(nXSize))
    1227             :         {
    1228           8 :             VSIFCloseL(fp);
    1229           8 :             VSIFree(pfData);
    1230           8 :             CPLError(CE_Failure, CPLE_FileIO,
    1231             :                      "Unable to write grid row. Disk full?\n");
    1232           8 :             return nullptr;
    1233             :         }
    1234             : 
    1235         166 :         if (!pfnProgress(static_cast<double>(nYSize - iRow) / nYSize, nullptr,
    1236             :                          pProgressData))
    1237             :         {
    1238           0 :             VSIFCloseL(fp);
    1239           0 :             VSIFree(pfData);
    1240           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1241           0 :             return nullptr;
    1242             :         }
    1243             :     }
    1244             : 
    1245          12 :     VSIFree(pfData);
    1246             : 
    1247             :     /* write out the min and max values */
    1248          12 :     eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY,
    1249             :                        dfMinZ, dfMaxZ);
    1250             : 
    1251          12 :     if (eErr != CE_None)
    1252             :     {
    1253           0 :         VSIFCloseL(fp);
    1254           0 :         return nullptr;
    1255             :     }
    1256             : 
    1257          12 :     VSIFCloseL(fp);
    1258             : 
    1259          12 :     GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen(pszFilename, GA_Update);
    1260          12 :     if (poDS)
    1261             :     {
    1262          12 :         poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    1263             :     }
    1264             : 
    1265          12 :     return poDS;
    1266             : }
    1267             : 
    1268             : /************************************************************************/
    1269             : /*                          GDALRegister_GS7BG()                        */
    1270             : /************************************************************************/
    1271        1595 : void GDALRegister_GS7BG()
    1272             : 
    1273             : {
    1274        1595 :     if (GDALGetDriverByName("GS7BG") != nullptr)
    1275         302 :         return;
    1276             : 
    1277        1293 :     GDALDriver *poDriver = new GDALDriver();
    1278             : 
    1279        1293 :     poDriver->SetDescription("GS7BG");
    1280        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1281        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1282        1293 :                               "Golden Software 7 Binary Grid (.grd)");
    1283        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gs7bg.html");
    1284        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
    1285        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    1286        1293 :                               "Byte Int16 UInt16 Float32 Float64");
    1287        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1288             : 
    1289        1293 :     poDriver->pfnIdentify = GS7BGDataset::Identify;
    1290        1293 :     poDriver->pfnOpen = GS7BGDataset::Open;
    1291        1293 :     poDriver->pfnCreate = GS7BGDataset::Create;
    1292        1293 :     poDriver->pfnCreateCopy = GS7BGDataset::CreateCopy;
    1293             : 
    1294        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1295             : }

Generated by: LCOV version 1.14