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

Generated by: LCOV version 1.14