LCOV - code coverage report
Current view: top level - frmts/gsg - gs7bgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 378 558 67.7 %
Date: 2026-03-26 23:25:44 Functions: 19 21 90.5 %

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

Generated by: LCOV version 1.14