LCOV - code coverage report
Current view: top level - frmts/gsg - gs7bgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 367 558 65.8 %
Date: 2024-05-06 13:02:59 Functions: 18 20 90.0 %

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

Generated by: LCOV version 1.14