LCOV - code coverage report
Current view: top level - frmts/gsg - gsbgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 319 445 71.7 %
Date: 2024-05-06 22:33:47 Functions: 18 20 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Implements the Golden Software Binary Grid Format.
       5             :  * Author:   Kevin Locke, kwl7@cornell.edu
       6             :  *           (Based largely on aaigriddataset.cpp by Frank Warmerdam)
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2006, Kevin Locke <kwl7@cornell.edu>
      10             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "cpl_conv.h"
      32             : 
      33             : #include <assert.h>
      34             : #include <float.h>
      35             : #include <limits.h>
      36             : #include <limits>
      37             : 
      38             : #include "gdal_frmts.h"
      39             : #include "gdal_pam.h"
      40             : 
      41             : /************************************************************************/
      42             : /* ==================================================================== */
      43             : /*                              GSBGDataset                             */
      44             : /* ==================================================================== */
      45             : /************************************************************************/
      46             : 
      47             : class GSBGRasterBand;
      48             : 
      49             : class GSBGDataset final : public GDALPamDataset
      50             : {
      51             :     friend class GSBGRasterBand;
      52             : 
      53             :     static const float fNODATA_VALUE;
      54             :     static const size_t nHEADER_SIZE;
      55             : 
      56             :     static CPLErr WriteHeader(VSILFILE *fp, GInt16 nXSize, GInt16 nYSize,
      57             :                               double dfMinX, double dfMaxX, double dfMinY,
      58             :                               double dfMaxY, double dfMinZ, double dfMaxZ);
      59             : 
      60             :     VSILFILE *fp;
      61             : 
      62             :   public:
      63          45 :     GSBGDataset() : fp(nullptr)
      64             :     {
      65          45 :     }
      66             : 
      67             :     ~GSBGDataset();
      68             : 
      69             :     static int Identify(GDALOpenInfo *);
      70             :     static GDALDataset *Open(GDALOpenInfo *);
      71             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      72             :                                int nBands, GDALDataType eType,
      73             :                                char **papszParamList);
      74             :     static GDALDataset *CreateCopy(const char *pszFilename,
      75             :                                    GDALDataset *poSrcDS, int bStrict,
      76             :                                    char **papszOptions,
      77             :                                    GDALProgressFunc pfnProgress,
      78             :                                    void *pProgressData);
      79             : 
      80             :     CPLErr GetGeoTransform(double *padfGeoTransform) override;
      81             :     CPLErr SetGeoTransform(double *padfGeoTransform) override;
      82             : };
      83             : 
      84             : /* NOTE:  This is not mentioned in the spec, but Surfer 8 uses this value */
      85             : /* 0x7effffee (Little Endian: eeffff7e) */
      86             : const float GSBGDataset::fNODATA_VALUE = 1.701410009187828e+38f;
      87             : 
      88             : const size_t GSBGDataset::nHEADER_SIZE = 56;
      89             : 
      90             : /************************************************************************/
      91             : /* ==================================================================== */
      92             : /*                            GSBGRasterBand                            */
      93             : /* ==================================================================== */
      94             : /************************************************************************/
      95             : 
      96             : class GSBGRasterBand final : public GDALPamRasterBand
      97             : {
      98             :     friend class GSBGDataset;
      99             : 
     100             :     double dfMinX;
     101             :     double dfMaxX;
     102             :     double dfMinY;
     103             :     double dfMaxY;
     104             :     double dfMinZ;
     105             :     double dfMaxZ;
     106             : 
     107             :     float *pafRowMinZ;
     108             :     float *pafRowMaxZ;
     109             :     int nMinZRow;
     110             :     int nMaxZRow;
     111             : 
     112             :     CPLErr ScanForMinMaxZ();
     113             : 
     114             :   public:
     115             :     GSBGRasterBand(GSBGDataset *, int);
     116             :     ~GSBGRasterBand();
     117             : 
     118             :     CPLErr IReadBlock(int, int, void *) override;
     119             :     CPLErr IWriteBlock(int, int, void *) override;
     120             : 
     121             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     122             :     double GetMinimum(int *pbSuccess = nullptr) override;
     123             :     double GetMaximum(int *pbSuccess = nullptr) override;
     124             : };
     125             : 
     126             : /************************************************************************/
     127             : /*                           GSBGRasterBand()                           */
     128             : /************************************************************************/
     129             : 
     130          45 : GSBGRasterBand::GSBGRasterBand(GSBGDataset *poDSIn, int nBandIn)
     131             :     : dfMinX(0.0), dfMaxX(0.0), dfMinY(0.0), dfMaxY(0.0), dfMinZ(0.0),
     132             :       dfMaxZ(0.0), pafRowMinZ(nullptr), pafRowMaxZ(nullptr), nMinZRow(-1),
     133          45 :       nMaxZRow(-1)
     134             : {
     135          45 :     this->poDS = poDSIn;
     136          45 :     this->nBand = nBandIn;
     137             : 
     138          45 :     eDataType = GDT_Float32;
     139             : 
     140          45 :     nBlockXSize = poDS->GetRasterXSize();
     141          45 :     nBlockYSize = 1;
     142          45 : }
     143             : 
     144             : /************************************************************************/
     145             : /*                           ~GSBGRasterBand()                          */
     146             : /************************************************************************/
     147             : 
     148          90 : GSBGRasterBand::~GSBGRasterBand()
     149             : 
     150             : {
     151          45 :     if (pafRowMinZ != nullptr)
     152           1 :         CPLFree(pafRowMinZ);
     153          45 :     if (pafRowMaxZ != nullptr)
     154           1 :         CPLFree(pafRowMaxZ);
     155          90 : }
     156             : 
     157             : /************************************************************************/
     158             : /*                          ScanForMinMaxZ()                            */
     159             : /************************************************************************/
     160             : 
     161           1 : CPLErr GSBGRasterBand::ScanForMinMaxZ()
     162             : 
     163             : {
     164           1 :     float *pafRowVals = (float *)VSI_MALLOC2_VERBOSE(nRasterXSize, 4);
     165             : 
     166           1 :     if (pafRowVals == nullptr)
     167             :     {
     168           0 :         return CE_Failure;
     169             :     }
     170             : 
     171           1 :     double dfNewMinZ = std::numeric_limits<double>::max();
     172           1 :     double dfNewMaxZ = std::numeric_limits<double>::lowest();
     173           1 :     int nNewMinZRow = 0;
     174           1 :     int nNewMaxZRow = 0;
     175             : 
     176             :     /* Since we have to scan, lets calc. statistics too */
     177           1 :     double dfSum = 0.0;
     178           1 :     double dfSum2 = 0.0;
     179           1 :     unsigned long nValuesRead = 0;
     180          21 :     for (int iRow = 0; iRow < nRasterYSize; iRow++)
     181             :     {
     182          20 :         CPLErr eErr = IReadBlock(0, iRow, pafRowVals);
     183          20 :         if (eErr != CE_None)
     184             :         {
     185           0 :             VSIFree(pafRowVals);
     186           0 :             return CE_Failure;
     187             :         }
     188             : 
     189          20 :         pafRowMinZ[iRow] = std::numeric_limits<float>::max();
     190          20 :         pafRowMaxZ[iRow] = std::numeric_limits<float>::lowest();
     191         420 :         for (int iCol = 0; iCol < nRasterXSize; iCol++)
     192             :         {
     193         400 :             if (pafRowVals[iCol] == GSBGDataset::fNODATA_VALUE)
     194         400 :                 continue;
     195             : 
     196           0 :             if (pafRowVals[iCol] < pafRowMinZ[iRow])
     197           0 :                 pafRowMinZ[iRow] = pafRowVals[iCol];
     198             : 
     199           0 :             if (pafRowVals[iCol] > pafRowMinZ[iRow])
     200           0 :                 pafRowMaxZ[iRow] = pafRowVals[iCol];
     201             : 
     202           0 :             dfSum += pafRowVals[iCol];
     203           0 :             dfSum2 += static_cast<double>(pafRowVals[iCol]) * pafRowVals[iCol];
     204           0 :             nValuesRead++;
     205             :         }
     206             : 
     207          20 :         if (pafRowMinZ[iRow] < dfNewMinZ)
     208             :         {
     209           1 :             dfNewMinZ = pafRowMinZ[iRow];
     210           1 :             nNewMinZRow = iRow;
     211             :         }
     212             : 
     213          20 :         if (pafRowMaxZ[iRow] > dfNewMaxZ)
     214             :         {
     215           1 :             dfNewMaxZ = pafRowMaxZ[iRow];
     216           1 :             nNewMaxZRow = iRow;
     217             :         }
     218             :     }
     219             : 
     220           1 :     VSIFree(pafRowVals);
     221             : 
     222           1 :     if (nValuesRead == 0)
     223             :     {
     224           1 :         dfMinZ = 0.0;
     225           1 :         dfMaxZ = 0.0;
     226           1 :         nMinZRow = 0;
     227           1 :         nMaxZRow = 0;
     228           1 :         return CE_None;
     229             :     }
     230             : 
     231           0 :     dfMinZ = dfNewMinZ;
     232           0 :     dfMaxZ = dfNewMaxZ;
     233           0 :     nMinZRow = nNewMinZRow;
     234           0 :     nMaxZRow = nNewMaxZRow;
     235             : 
     236           0 :     double dfMean = dfSum / nValuesRead;
     237           0 :     double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean));
     238           0 :     SetStatistics(dfMinZ, dfMaxZ, dfMean, dfStdDev);
     239             : 
     240           0 :     return CE_None;
     241             : }
     242             : 
     243             : /************************************************************************/
     244             : /*                             IReadBlock()                             */
     245             : /************************************************************************/
     246             : 
     247         140 : CPLErr GSBGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     248             : 
     249             : {
     250         140 :     if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
     251           0 :         return CE_Failure;
     252             : 
     253         140 :     GSBGDataset *poGDS = reinterpret_cast<GSBGDataset *>(poDS);
     254         280 :     if (VSIFSeekL(poGDS->fp,
     255         140 :                   GSBGDataset::nHEADER_SIZE +
     256         140 :                       4 * 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(float), 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             :     float *pfImage = (float *)pImage;
     275             :     for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
     276             :     {
     277             :         CPL_LSBPTR32(pfImage + iPixel);
     278             :     }
     279             : #endif
     280             : 
     281         140 :     return CE_None;
     282             : }
     283             : 
     284             : /************************************************************************/
     285             : /*                            IWriteBlock()                             */
     286             : /************************************************************************/
     287             : 
     288          20 : CPLErr GSBGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     289             : 
     290             : {
     291          20 :     if (eAccess == GA_ReadOnly)
     292             :     {
     293           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     294             :                  "Unable to write block, dataset opened read only.\n");
     295           0 :         return CE_Failure;
     296             :     }
     297             : 
     298          20 :     if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
     299           0 :         return CE_Failure;
     300             : 
     301          20 :     GSBGDataset *poGDS = cpl::down_cast<GSBGDataset *>(poDS);
     302             : 
     303          20 :     if (pafRowMinZ == nullptr || pafRowMaxZ == nullptr || nMinZRow < 0 ||
     304          19 :         nMaxZRow < 0)
     305             :     {
     306           1 :         pafRowMinZ = (float *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(float));
     307           1 :         if (pafRowMinZ == nullptr)
     308             :         {
     309           0 :             return CE_Failure;
     310             :         }
     311             : 
     312           1 :         pafRowMaxZ = (float *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(float));
     313           1 :         if (pafRowMaxZ == nullptr)
     314             :         {
     315           0 :             VSIFree(pafRowMinZ);
     316           0 :             pafRowMinZ = nullptr;
     317           0 :             return CE_Failure;
     318             :         }
     319             : 
     320           1 :         CPLErr eErr = ScanForMinMaxZ();
     321           1 :         if (eErr != CE_None)
     322           0 :             return eErr;
     323             :     }
     324             : 
     325          40 :     if (VSIFSeekL(poGDS->fp,
     326          20 :                   GSBGDataset::nHEADER_SIZE +
     327          20 :                       static_cast<vsi_l_offset>(4) * nRasterXSize *
     328          20 :                           (nRasterYSize - nBlockYOff - 1),
     329          20 :                   SEEK_SET) != 0)
     330             :     {
     331           0 :         CPLError(CE_Failure, CPLE_FileIO,
     332             :                  "Unable to seek to beginning of grid row.\n");
     333           0 :         return CE_Failure;
     334             :     }
     335             : 
     336          20 :     float *pfImage = (float *)pImage;
     337          20 :     pafRowMinZ[nBlockYOff] = std::numeric_limits<float>::max();
     338          20 :     pafRowMaxZ[nBlockYOff] = std::numeric_limits<float>::lowest();
     339         420 :     for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
     340             :     {
     341         400 :         if (pfImage[iPixel] != GSBGDataset::fNODATA_VALUE)
     342             :         {
     343         400 :             if (pfImage[iPixel] < pafRowMinZ[nBlockYOff])
     344          78 :                 pafRowMinZ[nBlockYOff] = pfImage[iPixel];
     345             : 
     346         400 :             if (pfImage[iPixel] > pafRowMaxZ[nBlockYOff])
     347          43 :                 pafRowMaxZ[nBlockYOff] = pfImage[iPixel];
     348             :         }
     349             : 
     350         400 :         CPL_LSBPTR32(pfImage + iPixel);
     351             :     }
     352             : 
     353          20 :     if (VSIFWriteL(pImage, sizeof(float), nBlockXSize, poGDS->fp) !=
     354          20 :         static_cast<unsigned>(nBlockXSize))
     355             :     {
     356           0 :         CPLError(CE_Failure, CPLE_FileIO,
     357             :                  "Unable to write block to grid file.\n");
     358           0 :         return CE_Failure;
     359             :     }
     360             : 
     361             :     /* Update min/max Z values as appropriate */
     362          20 :     bool bHeaderNeedsUpdate = false;
     363          20 :     if (nMinZRow == nBlockYOff && pafRowMinZ[nBlockYOff] > dfMinZ)
     364             :     {
     365           1 :         double dfNewMinZ = std::numeric_limits<double>::max();
     366          21 :         for (int iRow = 0; iRow < nRasterYSize; iRow++)
     367             :         {
     368          20 :             if (pafRowMinZ[iRow] < dfNewMinZ)
     369             :             {
     370           1 :                 dfNewMinZ = pafRowMinZ[iRow];
     371           1 :                 nMinZRow = iRow;
     372             :             }
     373             :         }
     374             : 
     375           1 :         if (dfNewMinZ != dfMinZ)
     376             :         {
     377           1 :             dfMinZ = dfNewMinZ;
     378           1 :             bHeaderNeedsUpdate = true;
     379             :         }
     380             :     }
     381             : 
     382          20 :     if (nMaxZRow == nBlockYOff && pafRowMaxZ[nBlockYOff] < dfMaxZ)
     383             :     {
     384           0 :         double dfNewMaxZ = std::numeric_limits<double>::lowest();
     385           0 :         for (int iRow = 0; iRow < nRasterYSize; iRow++)
     386             :         {
     387           0 :             if (pafRowMaxZ[iRow] > dfNewMaxZ)
     388             :             {
     389           0 :                 dfNewMaxZ = pafRowMaxZ[iRow];
     390           0 :                 nMaxZRow = iRow;
     391             :             }
     392             :         }
     393             : 
     394           0 :         if (dfNewMaxZ != dfMaxZ)
     395             :         {
     396           0 :             dfMaxZ = dfNewMaxZ;
     397           0 :             bHeaderNeedsUpdate = true;
     398             :         }
     399             :     }
     400             : 
     401          20 :     if (pafRowMinZ[nBlockYOff] < dfMinZ || pafRowMaxZ[nBlockYOff] > dfMaxZ)
     402             :     {
     403           6 :         if (pafRowMinZ[nBlockYOff] < dfMinZ)
     404             :         {
     405           3 :             dfMinZ = pafRowMinZ[nBlockYOff];
     406           3 :             nMinZRow = nBlockYOff;
     407             :         }
     408             : 
     409           6 :         if (pafRowMaxZ[nBlockYOff] > dfMaxZ)
     410             :         {
     411           4 :             dfMaxZ = pafRowMaxZ[nBlockYOff];
     412           4 :             nMaxZRow = nBlockYOff;
     413             :         }
     414             : 
     415           6 :         bHeaderNeedsUpdate = true;
     416             :     }
     417             : 
     418          20 :     if (bHeaderNeedsUpdate && dfMaxZ > dfMinZ)
     419             :     {
     420          12 :         CPLErr eErr = poGDS->WriteHeader(poGDS->fp, (GInt16)nRasterXSize,
     421           6 :                                          (GInt16)nRasterYSize, dfMinX, dfMaxX,
     422             :                                          dfMinY, dfMaxY, dfMinZ, dfMaxZ);
     423           6 :         return eErr;
     424             :     }
     425             : 
     426          14 :     return CE_None;
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                           GetNoDataValue()                           */
     431             : /************************************************************************/
     432             : 
     433          21 : double GSBGRasterBand::GetNoDataValue(int *pbSuccess)
     434             : {
     435          21 :     if (pbSuccess)
     436          15 :         *pbSuccess = TRUE;
     437             : 
     438          21 :     return GSBGDataset::fNODATA_VALUE;
     439             : }
     440             : 
     441             : /************************************************************************/
     442             : /*                             GetMinimum()                             */
     443             : /************************************************************************/
     444             : 
     445           0 : double GSBGRasterBand::GetMinimum(int *pbSuccess)
     446             : {
     447           0 :     if (pbSuccess)
     448           0 :         *pbSuccess = TRUE;
     449             : 
     450           0 :     return dfMinZ;
     451             : }
     452             : 
     453             : /************************************************************************/
     454             : /*                             GetMaximum()                             */
     455             : /************************************************************************/
     456             : 
     457           0 : double GSBGRasterBand::GetMaximum(int *pbSuccess)
     458             : {
     459           0 :     if (pbSuccess)
     460           0 :         *pbSuccess = TRUE;
     461             : 
     462           0 :     return dfMaxZ;
     463             : }
     464             : 
     465             : /************************************************************************/
     466             : /* ==================================================================== */
     467             : /*                              GSBGDataset                             */
     468             : /* ==================================================================== */
     469             : /************************************************************************/
     470             : 
     471          90 : GSBGDataset::~GSBGDataset()
     472             : 
     473             : {
     474          45 :     FlushCache(true);
     475          45 :     if (fp != nullptr)
     476          45 :         VSIFCloseL(fp);
     477          90 : }
     478             : 
     479             : /************************************************************************/
     480             : /*                              Identify()                              */
     481             : /************************************************************************/
     482             : 
     483       50240 : int GSBGDataset::Identify(GDALOpenInfo *poOpenInfo)
     484             : 
     485             : {
     486             :     /* Check for signature */
     487       50240 :     if (poOpenInfo->nHeaderBytes < 4 ||
     488        4358 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "DSBB"))
     489             :     {
     490       50150 :         return FALSE;
     491             :     }
     492             : 
     493          90 :     return TRUE;
     494             : }
     495             : 
     496             : /************************************************************************/
     497             : /*                                Open()                                */
     498             : /************************************************************************/
     499             : 
     500          45 : GDALDataset *GSBGDataset::Open(GDALOpenInfo *poOpenInfo)
     501             : 
     502             : {
     503          45 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     504             :     {
     505           0 :         return nullptr;
     506             :     }
     507             : 
     508             :     /* -------------------------------------------------------------------- */
     509             :     /*      Create a corresponding GDALDataset.                             */
     510             :     /* -------------------------------------------------------------------- */
     511          90 :     auto poDS = std::make_unique<GSBGDataset>();
     512             : 
     513          45 :     poDS->eAccess = poOpenInfo->eAccess;
     514          45 :     poDS->fp = poOpenInfo->fpL;
     515          45 :     poOpenInfo->fpL = nullptr;
     516             : 
     517             :     /* -------------------------------------------------------------------- */
     518             :     /*      Read the header.                                                */
     519             :     /* -------------------------------------------------------------------- */
     520          45 :     if (VSIFSeekL(poDS->fp, 4, SEEK_SET) != 0)
     521             :     {
     522           0 :         CPLError(CE_Failure, CPLE_FileIO,
     523             :                  "Unable to seek to start of grid file header.\n");
     524           0 :         return nullptr;
     525             :     }
     526             : 
     527             :     /* Parse number of X axis grid rows */
     528             :     GInt16 nTemp;
     529          45 :     if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
     530             :     {
     531           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster X size.\n");
     532           0 :         return nullptr;
     533             :     }
     534          45 :     poDS->nRasterXSize = CPL_LSBWORD16(nTemp);
     535             : 
     536          45 :     if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
     537             :     {
     538           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster Y size.\n");
     539           0 :         return nullptr;
     540             :     }
     541          45 :     poDS->nRasterYSize = CPL_LSBWORD16(nTemp);
     542             : 
     543          45 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     544             :     {
     545           0 :         return nullptr;
     546             :     }
     547             : 
     548             :     /* -------------------------------------------------------------------- */
     549             :     /*      Create band information objects.                                */
     550             :     /* -------------------------------------------------------------------- */
     551          45 :     GSBGRasterBand *poBand = new GSBGRasterBand(poDS.get(), 1);
     552          45 :     poDS->SetBand(1, poBand);
     553             : 
     554             :     double dfTemp;
     555          45 :     if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
     556             :     {
     557           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.\n");
     558           0 :         return nullptr;
     559             :     }
     560          45 :     CPL_LSBPTR64(&dfTemp);
     561          45 :     poBand->dfMinX = dfTemp;
     562             : 
     563          45 :     if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
     564             :     {
     565           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum X value.\n");
     566           0 :         return nullptr;
     567             :     }
     568          45 :     CPL_LSBPTR64(&dfTemp);
     569          45 :     poBand->dfMaxX = dfTemp;
     570             : 
     571          45 :     if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
     572             :     {
     573           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Y value.\n");
     574           0 :         return nullptr;
     575             :     }
     576          45 :     CPL_LSBPTR64(&dfTemp);
     577          45 :     poBand->dfMinY = dfTemp;
     578             : 
     579          45 :     if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
     580             :     {
     581           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Y value.\n");
     582           0 :         return nullptr;
     583             :     }
     584          45 :     CPL_LSBPTR64(&dfTemp);
     585          45 :     poBand->dfMaxY = dfTemp;
     586             : 
     587          45 :     if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
     588             :     {
     589           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Z value.\n");
     590           0 :         return nullptr;
     591             :     }
     592          45 :     CPL_LSBPTR64(&dfTemp);
     593          45 :     poBand->dfMinZ = dfTemp;
     594             : 
     595          45 :     if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
     596             :     {
     597           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Z value.\n");
     598           0 :         return nullptr;
     599             :     }
     600          45 :     CPL_LSBPTR64(&dfTemp);
     601          45 :     poBand->dfMaxZ = dfTemp;
     602             : 
     603             :     /* -------------------------------------------------------------------- */
     604             :     /*      Initialize any PAM information.                                 */
     605             :     /* -------------------------------------------------------------------- */
     606          45 :     poDS->SetDescription(poOpenInfo->pszFilename);
     607          45 :     poDS->TryLoadXML();
     608             : 
     609             :     /* -------------------------------------------------------------------- */
     610             :     /*      Check for external overviews.                                   */
     611             :     /* -------------------------------------------------------------------- */
     612          45 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename,
     613             :                                 poOpenInfo->GetSiblingFiles());
     614             : 
     615          45 :     return poDS.release();
     616             : }
     617             : 
     618             : /************************************************************************/
     619             : /*                          GetGeoTransform()                           */
     620             : /************************************************************************/
     621             : 
     622          29 : CPLErr GSBGDataset::GetGeoTransform(double *padfGeoTransform)
     623             : {
     624          29 :     if (padfGeoTransform == nullptr)
     625           0 :         return CE_Failure;
     626             : 
     627          29 :     GSBGRasterBand *poGRB = cpl::down_cast<GSBGRasterBand *>(GetRasterBand(1));
     628             : 
     629             :     /* check if we have a PAM GeoTransform stored */
     630          29 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     631          29 :     CPLErr eErr = GDALPamDataset::GetGeoTransform(padfGeoTransform);
     632          29 :     CPLPopErrorHandler();
     633             : 
     634          29 :     if (eErr == CE_None)
     635           0 :         return CE_None;
     636             : 
     637          29 :     if (nRasterXSize == 1 || nRasterYSize == 1)
     638           0 :         return CE_Failure;
     639             : 
     640             :     /* calculate pixel size first */
     641          29 :     padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX) / (nRasterXSize - 1);
     642          29 :     padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY) / (nRasterYSize - 1);
     643             : 
     644             :     /* then calculate image origin */
     645          29 :     padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2;
     646          29 :     padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2;
     647             : 
     648             :     /* tilt/rotation does not supported by the GS grids */
     649          29 :     padfGeoTransform[4] = 0.0;
     650          29 :     padfGeoTransform[2] = 0.0;
     651             : 
     652          29 :     return CE_None;
     653             : }
     654             : 
     655             : /************************************************************************/
     656             : /*                          SetGeoTransform()                           */
     657             : /************************************************************************/
     658             : 
     659          12 : CPLErr GSBGDataset::SetGeoTransform(double *padfGeoTransform)
     660             : {
     661          12 :     if (eAccess == GA_ReadOnly)
     662             :     {
     663           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     664             :                  "Unable to set GeoTransform, dataset opened read only.\n");
     665           0 :         return CE_Failure;
     666             :     }
     667             : 
     668          12 :     GSBGRasterBand *poGRB = cpl::down_cast<GSBGRasterBand *>(GetRasterBand(1));
     669             : 
     670          12 :     if (padfGeoTransform == nullptr)
     671           0 :         return CE_Failure;
     672             : 
     673             :     /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
     674             :     // CPLErr eErr = CE_None;
     675             :     /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0
     676             :         || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 )
     677             :         eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform );
     678             : 
     679             :     if( eErr != CE_None )
     680             :         return eErr;*/
     681             : 
     682          12 :     double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
     683          12 :     double dfMaxX =
     684          12 :         padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0];
     685          12 :     double dfMinY =
     686          12 :         padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
     687          12 :     double dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2;
     688             : 
     689          24 :     CPLErr eErr = WriteHeader(fp, (GInt16)poGRB->nRasterXSize,
     690          12 :                               (GInt16)poGRB->nRasterYSize, dfMinX, dfMaxX,
     691             :                               dfMinY, dfMaxY, poGRB->dfMinZ, poGRB->dfMaxZ);
     692             : 
     693          12 :     if (eErr == CE_None)
     694             :     {
     695          12 :         poGRB->dfMinX = dfMinX;
     696          12 :         poGRB->dfMaxX = dfMaxX;
     697          12 :         poGRB->dfMinY = dfMinY;
     698          12 :         poGRB->dfMaxY = dfMaxY;
     699             :     }
     700             : 
     701          12 :     return eErr;
     702             : }
     703             : 
     704             : /************************************************************************/
     705             : /*                             WriteHeader()                            */
     706             : /************************************************************************/
     707             : 
     708          65 : CPLErr GSBGDataset::WriteHeader(VSILFILE *fp, GInt16 nXSize, GInt16 nYSize,
     709             :                                 double dfMinX, double dfMaxX, double dfMinY,
     710             :                                 double dfMaxY, double dfMinZ, double dfMaxZ)
     711             : 
     712             : {
     713          65 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
     714             :     {
     715           0 :         CPLError(CE_Failure, CPLE_FileIO,
     716             :                  "Unable to seek to start of grid file.\n");
     717           0 :         return CE_Failure;
     718             :     }
     719             : 
     720          65 :     if (VSIFWriteL((void *)"DSBB", 1, 4, fp) != 4)
     721             :     {
     722           1 :         CPLError(CE_Failure, CPLE_FileIO,
     723             :                  "Unable to write signature to grid file.\n");
     724           1 :         return CE_Failure;
     725             :     }
     726             : 
     727          64 :     GInt16 nTemp = CPL_LSBWORD16(nXSize);
     728          64 :     if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
     729             :     {
     730           0 :         CPLError(CE_Failure, CPLE_FileIO,
     731             :                  "Unable to write raster X size to grid file.\n");
     732           0 :         return CE_Failure;
     733             :     }
     734             : 
     735          64 :     nTemp = CPL_LSBWORD16(nYSize);
     736          64 :     if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
     737             :     {
     738           0 :         CPLError(CE_Failure, CPLE_FileIO,
     739             :                  "Unable to write raster Y size to grid file.\n");
     740           0 :         return CE_Failure;
     741             :     }
     742             : 
     743          64 :     double dfTemp = dfMinX;
     744          64 :     CPL_LSBPTR64(&dfTemp);
     745          64 :     if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
     746             :     {
     747           0 :         CPLError(CE_Failure, CPLE_FileIO,
     748             :                  "Unable to write minimum X value to grid file.\n");
     749           0 :         return CE_Failure;
     750             :     }
     751             : 
     752          64 :     dfTemp = dfMaxX;
     753          64 :     CPL_LSBPTR64(&dfTemp);
     754          64 :     if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
     755             :     {
     756           0 :         CPLError(CE_Failure, CPLE_FileIO,
     757             :                  "Unable to write maximum X value to grid file.\n");
     758           0 :         return CE_Failure;
     759             :     }
     760             : 
     761          64 :     dfTemp = dfMinY;
     762          64 :     CPL_LSBPTR64(&dfTemp);
     763          64 :     if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
     764             :     {
     765           0 :         CPLError(CE_Failure, CPLE_FileIO,
     766             :                  "Unable to write minimum Y value to grid file.\n");
     767           0 :         return CE_Failure;
     768             :     }
     769             : 
     770          64 :     dfTemp = dfMaxY;
     771          64 :     CPL_LSBPTR64(&dfTemp);
     772          64 :     if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
     773             :     {
     774           0 :         CPLError(CE_Failure, CPLE_FileIO,
     775             :                  "Unable to write maximum Y value to grid file.\n");
     776           0 :         return CE_Failure;
     777             :     }
     778             : 
     779          64 :     dfTemp = dfMinZ;
     780          64 :     CPL_LSBPTR64(&dfTemp);
     781          64 :     if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
     782             :     {
     783           1 :         CPLError(CE_Failure, CPLE_FileIO,
     784             :                  "Unable to write minimum Z value to grid file.\n");
     785           1 :         return CE_Failure;
     786             :     }
     787             : 
     788          63 :     dfTemp = dfMaxZ;
     789          63 :     CPL_LSBPTR64(&dfTemp);
     790          63 :     if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
     791             :     {
     792           0 :         CPLError(CE_Failure, CPLE_FileIO,
     793             :                  "Unable to write maximum Z value to grid file.\n");
     794           0 :         return CE_Failure;
     795             :     }
     796             : 
     797          63 :     return CE_None;
     798             : }
     799             : 
     800             : /************************************************************************/
     801             : /*                               Create()                               */
     802             : /************************************************************************/
     803             : 
     804          33 : GDALDataset *GSBGDataset::Create(const char *pszFilename, int nXSize,
     805             :                                  int nYSize, int /* nBands */,
     806             :                                  GDALDataType eType,
     807             :                                  CPL_UNUSED char **papszParamList)
     808             : {
     809          33 :     if (nXSize <= 0 || nYSize <= 0)
     810             :     {
     811           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     812             :                  "Unable to create grid, both X and Y size must be "
     813             :                  "non-negative.\n");
     814             : 
     815           0 :         return nullptr;
     816             :     }
     817          66 :     else if (nXSize > std::numeric_limits<short>::max() ||
     818          33 :              nYSize > std::numeric_limits<short>::max())
     819             :     {
     820           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     821             :                  "Unable to create grid, Golden Software Binary Grid format "
     822             :                  "only supports sizes up to %dx%d.  %dx%d not supported.\n",
     823           0 :                  std::numeric_limits<short>::max(),
     824           0 :                  std::numeric_limits<short>::max(), nXSize, nYSize);
     825             : 
     826           0 :         return nullptr;
     827             :     }
     828             : 
     829          33 :     if (eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 &&
     830             :         eType != GDT_Int16)
     831             :     {
     832          20 :         CPLError(CE_Failure, CPLE_AppDefined,
     833             :                  "Golden Software Binary Grid only supports Byte, Int16, "
     834             :                  "Uint16, and Float32 datatypes.  Unable to create with "
     835             :                  "type %s.\n",
     836             :                  GDALGetDataTypeName(eType));
     837             : 
     838          20 :         return nullptr;
     839             :     }
     840             : 
     841          13 :     VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
     842             : 
     843          13 :     if (fp == nullptr)
     844             :     {
     845           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     846             :                  "Attempt to create file '%s' failed.\n", pszFilename);
     847           0 :         return nullptr;
     848             :     }
     849             : 
     850          13 :     CPLErr eErr = WriteHeader(fp, (GInt16)nXSize, (GInt16)nYSize, 0.0, nXSize,
     851             :                               0.0, nYSize, 0.0, 0.0);
     852          13 :     if (eErr != CE_None)
     853             :     {
     854           0 :         VSIFCloseL(fp);
     855           0 :         return nullptr;
     856             :     }
     857             : 
     858          13 :     float fVal = fNODATA_VALUE;
     859          13 :     CPL_LSBPTR32(&fVal);
     860        1233 :     for (int iRow = 0; iRow < nYSize; iRow++)
     861             :     {
     862      121620 :         for (int iCol = 0; iCol < nXSize; iCol++)
     863             :         {
     864      120400 :             if (VSIFWriteL((void *)&fVal, 4, 1, fp) != 1)
     865             :             {
     866           0 :                 VSIFCloseL(fp);
     867           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     868             :                          "Unable to write grid cell.  Disk full?\n");
     869           0 :                 return nullptr;
     870             :             }
     871             :         }
     872             :     }
     873             : 
     874          13 :     VSIFCloseL(fp);
     875             : 
     876          13 :     return (GDALDataset *)GDALOpen(pszFilename, GA_Update);
     877             : }
     878             : 
     879             : /************************************************************************/
     880             : /*                             CreateCopy()                             */
     881             : /************************************************************************/
     882             : 
     883          30 : GDALDataset *GSBGDataset::CreateCopy(const char *pszFilename,
     884             :                                      GDALDataset *poSrcDS, int bStrict,
     885             :                                      CPL_UNUSED char **papszOptions,
     886             :                                      GDALProgressFunc pfnProgress,
     887             :                                      void *pProgressData)
     888             : {
     889          30 :     if (pfnProgress == nullptr)
     890           0 :         pfnProgress = GDALDummyProgress;
     891             : 
     892          30 :     int nBands = poSrcDS->GetRasterCount();
     893          30 :     if (nBands == 0)
     894             :     {
     895           1 :         CPLError(
     896             :             CE_Failure, CPLE_NotSupported,
     897             :             "GSBG driver does not support source dataset with zero band.\n");
     898           1 :         return nullptr;
     899             :     }
     900          29 :     else if (nBands > 1)
     901             :     {
     902           4 :         if (bStrict)
     903             :         {
     904           4 :             CPLError(CE_Failure, CPLE_NotSupported,
     905             :                      "Unable to create copy, Golden Software Binary Grid "
     906             :                      "format only supports one raster band.\n");
     907           4 :             return nullptr;
     908             :         }
     909             :         else
     910           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     911             :                      "Golden Software Binary Grid format only supports one "
     912             :                      "raster band, first band will be copied.\n");
     913             :     }
     914             : 
     915          25 :     GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
     916          50 :     if (poSrcBand->GetXSize() > std::numeric_limits<short>::max() ||
     917          25 :         poSrcBand->GetYSize() > std::numeric_limits<short>::max())
     918             :     {
     919           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     920             :                  "Unable to create grid, Golden Software Binary Grid format "
     921             :                  "only supports sizes up to %dx%d.  %dx%d not supported.\n",
     922           0 :                  std::numeric_limits<short>::max(),
     923           0 :                  std::numeric_limits<short>::max(), poSrcBand->GetXSize(),
     924             :                  poSrcBand->GetYSize());
     925             : 
     926           0 :         return nullptr;
     927             :     }
     928             : 
     929          25 :     if (!pfnProgress(0.0, nullptr, pProgressData))
     930             :     {
     931           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated\n");
     932           0 :         return nullptr;
     933             :     }
     934             : 
     935          25 :     VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
     936             : 
     937          25 :     if (fp == nullptr)
     938             :     {
     939           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     940             :                  "Attempt to create file '%s' failed.\n", pszFilename);
     941           3 :         return nullptr;
     942             :     }
     943             : 
     944          22 :     GInt16 nXSize = (GInt16)poSrcBand->GetXSize();
     945          22 :     GInt16 nYSize = (GInt16)poSrcBand->GetYSize();
     946             :     double adfGeoTransform[6];
     947             : 
     948          22 :     poSrcDS->GetGeoTransform(adfGeoTransform);
     949             : 
     950          22 :     double dfMinX = adfGeoTransform[0] + adfGeoTransform[1] / 2;
     951          22 :     double dfMaxX = adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0];
     952          22 :     double dfMinY = adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3];
     953          22 :     double dfMaxY = adfGeoTransform[3] + adfGeoTransform[5] / 2;
     954          22 :     CPLErr eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY,
     955             :                               dfMaxY, 0.0, 0.0);
     956             : 
     957          22 :     if (eErr != CE_None)
     958             :     {
     959           2 :         VSIFCloseL(fp);
     960           2 :         return nullptr;
     961             :     }
     962             : 
     963             :     /* -------------------------------------------------------------------- */
     964             :     /*      Copy band data.                                                 */
     965             :     /* -------------------------------------------------------------------- */
     966          20 :     float *pfData = (float *)VSI_MALLOC2_VERBOSE(nXSize, sizeof(float));
     967          20 :     if (pfData == nullptr)
     968             :     {
     969           0 :         VSIFCloseL(fp);
     970           0 :         return nullptr;
     971             :     }
     972             : 
     973             :     int bSrcHasNDValue;
     974          20 :     float fSrcNoDataValue = (float)poSrcBand->GetNoDataValue(&bSrcHasNDValue);
     975          20 :     double dfMinZ = std::numeric_limits<double>::max();
     976          20 :     double dfMaxZ = std::numeric_limits<double>::lowest();
     977         185 :     for (GInt16 iRow = nYSize - 1; iRow >= 0; iRow--)
     978             :     {
     979         173 :         eErr = poSrcBand->RasterIO(GF_Read, 0, iRow, nXSize, 1, pfData, nXSize,
     980             :                                    1, GDT_Float32, 0, 0, nullptr);
     981             : 
     982         173 :         if (eErr != CE_None)
     983             :         {
     984           0 :             VSIFCloseL(fp);
     985           0 :             VSIFree(pfData);
     986           0 :             return nullptr;
     987             :         }
     988             : 
     989        2103 :         for (int iCol = 0; iCol < nXSize; iCol++)
     990             :         {
     991        1930 :             if (bSrcHasNDValue && pfData[iCol] == fSrcNoDataValue)
     992             :             {
     993           0 :                 pfData[iCol] = fNODATA_VALUE;
     994             :             }
     995             :             else
     996             :             {
     997        1930 :                 if (pfData[iCol] > dfMaxZ)
     998          22 :                     dfMaxZ = pfData[iCol];
     999             : 
    1000        1930 :                 if (pfData[iCol] < dfMinZ)
    1001          27 :                     dfMinZ = pfData[iCol];
    1002             :             }
    1003             : 
    1004        1930 :             CPL_LSBPTR32(pfData + iCol);
    1005             :         }
    1006             : 
    1007         173 :         if (VSIFWriteL((void *)pfData, 4, nXSize, fp) !=
    1008         173 :             static_cast<unsigned>(nXSize))
    1009             :         {
    1010           8 :             VSIFCloseL(fp);
    1011           8 :             VSIFree(pfData);
    1012           8 :             CPLError(CE_Failure, CPLE_FileIO,
    1013             :                      "Unable to write grid row. Disk full?\n");
    1014           8 :             return nullptr;
    1015             :         }
    1016             : 
    1017         165 :         if (!pfnProgress(static_cast<double>(nYSize - iRow) / nYSize, nullptr,
    1018             :                          pProgressData))
    1019             :         {
    1020           0 :             VSIFCloseL(fp);
    1021           0 :             VSIFree(pfData);
    1022           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1023           0 :             return nullptr;
    1024             :         }
    1025             :     }
    1026             : 
    1027          12 :     VSIFree(pfData);
    1028             : 
    1029             :     /* write out the min and max values */
    1030          12 :     eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY,
    1031             :                        dfMinZ, dfMaxZ);
    1032             : 
    1033          12 :     if (eErr != CE_None)
    1034             :     {
    1035           0 :         VSIFCloseL(fp);
    1036           0 :         return nullptr;
    1037             :     }
    1038             : 
    1039          12 :     VSIFCloseL(fp);
    1040             : 
    1041          12 :     GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen(pszFilename, GA_Update);
    1042          12 :     if (poDS)
    1043             :     {
    1044          12 :         poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    1045             :     }
    1046          12 :     return poDS;
    1047             : }
    1048             : 
    1049             : /************************************************************************/
    1050             : /*                          GDALRegister_GSBG()                         */
    1051             : /************************************************************************/
    1052             : 
    1053        1520 : void GDALRegister_GSBG()
    1054             : 
    1055             : {
    1056        1520 :     if (GDALGetDriverByName("GSBG") != nullptr)
    1057         301 :         return;
    1058             : 
    1059        1219 :     GDALDriver *poDriver = new GDALDriver();
    1060             : 
    1061        1219 :     poDriver->SetDescription("GSBG");
    1062        1219 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1063        1219 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1064        1219 :                               "Golden Software Binary Grid (.grd)");
    1065        1219 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gsbg.html");
    1066        1219 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
    1067        1219 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    1068        1219 :                               "Byte Int16 UInt16 Float32");
    1069        1219 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1070             : 
    1071        1219 :     poDriver->pfnIdentify = GSBGDataset::Identify;
    1072        1219 :     poDriver->pfnOpen = GSBGDataset::Open;
    1073        1219 :     poDriver->pfnCreate = GSBGDataset::Create;
    1074        1219 :     poDriver->pfnCreateCopy = GSBGDataset::CreateCopy;
    1075             : 
    1076        1219 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1077             : }

Generated by: LCOV version 1.14