LCOV - code coverage report
Current view: top level - frmts/gsg - gsbgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 326 440 74.1 %
Date: 2026-02-12 23:49:34 Functions: 19 21 90.5 %

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

Generated by: LCOV version 1.14