LCOV - code coverage report
Current view: top level - frmts/saga - sagadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 387 461 83.9 %
Date: 2025-01-18 12:42:00 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  SAGA GIS Binary Driver
       3             :  * Purpose:  Implements the SAGA GIS Binary Grid Format.
       4             :  * Author:   Volker Wichmann, wichmann@laserdata.at
       5             :  *   (Based on gsbgdataset.cpp by Kevin Locke and Frank Warmerdam)
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2009, Volker Wichmann <wichmann@laserdata.at>
       9             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_conv.h"
      15             : 
      16             : #include <cassert>
      17             : #include <cfloat>
      18             : #include <climits>
      19             : 
      20             : #include "gdal_frmts.h"
      21             : #include "gdal_pam.h"
      22             : #include "ogr_spatialref.h"
      23             : 
      24             : #ifndef INT_MAX
      25             : #define INT_MAX 2147483647
      26             : #endif /* INT_MAX */
      27             : 
      28             : /* NODATA Values */
      29             : // #define SG_NODATA_GDT_Bit 0.0
      30             : constexpr GByte SG_NODATA_GDT_Byte = 255;
      31             : #define SG_NODATA_GDT_UInt16 65535
      32             : #define SG_NODATA_GDT_Int16 -32767
      33             : #define SG_NODATA_GDT_UInt32 4294967295U
      34             : #define SG_NODATA_GDT_Int32 -2147483647
      35             : #define SG_NODATA_GDT_Float32 -99999.0
      36             : #define SG_NODATA_GDT_Float64 -99999.0
      37             : 
      38             : /************************************************************************/
      39             : /* ==================================================================== */
      40             : /*                              SAGADataset                             */
      41             : /* ==================================================================== */
      42             : /************************************************************************/
      43             : 
      44             : class SAGARasterBand;
      45             : 
      46             : class SAGADataset final : public GDALPamDataset
      47             : {
      48             :     friend class SAGARasterBand;
      49             : 
      50             :     static CPLErr WriteHeader(CPLString osHDRFilename, GDALDataType eType,
      51             :                               int nXSize, int nYSize, double dfMinX,
      52             :                               double dfMinY, double dfCellsize, double dfNoData,
      53             :                               double dfZFactor, bool bTopToBottom);
      54             :     VSILFILE *fp;
      55             :     OGRSpatialReference m_oSRS{};
      56             :     bool headerDirty = false;
      57             : 
      58             :   public:
      59             :     SAGADataset();
      60             :     virtual ~SAGADataset();
      61             : 
      62             :     static GDALDataset *Open(GDALOpenInfo *);
      63             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      64             :                                int nBandsIn, GDALDataType eType,
      65             :                                char **papszParamList);
      66             :     static GDALDataset *CreateCopy(const char *pszFilename,
      67             :                                    GDALDataset *poSrcDS, int bStrict,
      68             :                                    char **papszOptions,
      69             :                                    GDALProgressFunc pfnProgress,
      70             :                                    void *pProgressData);
      71             : 
      72             :     const OGRSpatialReference *GetSpatialRef() const override;
      73             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
      74             : 
      75             :     virtual char **GetFileList() override;
      76             : 
      77             :     CPLErr GetGeoTransform(double *padfGeoTransform) override;
      78             :     CPLErr SetGeoTransform(double *padfGeoTransform) override;
      79             : };
      80             : 
      81             : /************************************************************************/
      82             : /* ==================================================================== */
      83             : /*                            SAGARasterBand                            */
      84             : /* ==================================================================== */
      85             : /************************************************************************/
      86             : 
      87             : class SAGARasterBand final : public GDALPamRasterBand
      88             : {
      89             :     friend class SAGADataset;
      90             : 
      91             :     double m_Xmin;
      92             :     double m_Ymin;
      93             :     double m_Cellsize;
      94             :     double m_NoData;
      95             :     int m_ByteOrder;
      96             :     int m_nBits;
      97             : 
      98             :     void SetDataType(GDALDataType eType);
      99             :     void SwapBuffer(void *pImage) const;
     100             : 
     101             :   public:
     102             :     SAGARasterBand(SAGADataset *, int);
     103             : 
     104             :     CPLErr IReadBlock(int, int, void *) override;
     105             :     CPLErr IWriteBlock(int, int, void *) override;
     106             : 
     107             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     108             :     CPLErr SetNoDataValue(double dfNoData) override;
     109             : };
     110             : 
     111             : /************************************************************************/
     112             : /*                           SAGARasterBand()                           */
     113             : /************************************************************************/
     114             : 
     115         108 : SAGARasterBand::SAGARasterBand(SAGADataset *poDS_, int nBand_)
     116             :     : m_Xmin(0.0), m_Ymin(0.0), m_Cellsize(0.0), m_NoData(0.0), m_ByteOrder(0),
     117         108 :       m_nBits(0)
     118             : {
     119         108 :     poDS = poDS_;
     120         108 :     nBand = nBand_;
     121             : 
     122         108 :     eDataType = GDT_Float32;
     123             : 
     124         108 :     nBlockXSize = poDS->GetRasterXSize();
     125         108 :     nBlockYSize = 1;
     126         108 : }
     127             : 
     128             : /************************************************************************/
     129             : /*                            SetDataType()                             */
     130             : /************************************************************************/
     131             : 
     132         108 : void SAGARasterBand::SetDataType(GDALDataType eType)
     133             : 
     134             : {
     135         108 :     eDataType = eType;
     136         108 :     return;
     137             : }
     138             : 
     139             : /************************************************************************/
     140             : /*                             SwapBuffer()                             */
     141             : /************************************************************************/
     142             : 
     143        1127 : void SAGARasterBand::SwapBuffer(void *pImage) const
     144             : {
     145             : 
     146             : #ifdef CPL_LSB
     147        1127 :     const bool bSwap = (m_ByteOrder == 1);
     148             : #else
     149             :     const bool bSwap = (m_ByteOrder == 0);
     150             : #endif
     151             : 
     152        1127 :     if (bSwap)
     153             :     {
     154           0 :         if (m_nBits == 16)
     155             :         {
     156           0 :             short *pImage16 = reinterpret_cast<short *>(pImage);
     157           0 :             for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
     158             :             {
     159           0 :                 CPL_SWAP16PTR(pImage16 + iPixel);
     160             :             }
     161             :         }
     162           0 :         else if (m_nBits == 32)
     163             :         {
     164           0 :             int *pImage32 = reinterpret_cast<int *>(pImage);
     165           0 :             for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
     166             :             {
     167           0 :                 CPL_SWAP32PTR(pImage32 + iPixel);
     168             :             }
     169             :         }
     170           0 :         else if (m_nBits == 64)
     171             :         {
     172           0 :             double *pImage64 = reinterpret_cast<double *>(pImage);
     173           0 :             for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
     174             :             {
     175           0 :                 CPL_SWAP64PTR(pImage64 + iPixel);
     176             :             }
     177             :         }
     178             :     }
     179        1127 : }
     180             : 
     181             : /************************************************************************/
     182             : /*                             IReadBlock()                             */
     183             : /************************************************************************/
     184             : 
     185         367 : CPLErr SAGARasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     186             : 
     187             : {
     188         367 :     if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
     189           0 :         return CE_Failure;
     190             : 
     191         367 :     SAGADataset *poGDS = static_cast<SAGADataset *>(poDS);
     192         367 :     vsi_l_offset offset = static_cast<vsi_l_offset>(m_nBits / 8) *
     193         367 :                           nRasterXSize * (nRasterYSize - nBlockYOff - 1);
     194             : 
     195         367 :     if (VSIFSeekL(poGDS->fp, offset, SEEK_SET) != 0)
     196             :     {
     197           0 :         CPLError(CE_Failure, CPLE_FileIO,
     198             :                  "Unable to seek to beginning of grid row.\n");
     199           0 :         return CE_Failure;
     200             :     }
     201         367 :     if (VSIFReadL(pImage, m_nBits / 8, nBlockXSize, poGDS->fp) !=
     202         367 :         static_cast<unsigned>(nBlockXSize))
     203             :     {
     204           0 :         CPLError(CE_Failure, CPLE_FileIO,
     205             :                  "Unable to read block from grid file.\n");
     206           0 :         return CE_Failure;
     207             :     }
     208             : 
     209         367 :     SwapBuffer(pImage);
     210             : 
     211         367 :     return CE_None;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                            IWriteBlock()                             */
     216             : /************************************************************************/
     217             : 
     218         380 : CPLErr SAGARasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     219             : 
     220             : {
     221         380 :     if (eAccess == GA_ReadOnly)
     222             :     {
     223           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     224             :                  "Unable to write block, dataset opened read only.\n");
     225           0 :         return CE_Failure;
     226             :     }
     227             : 
     228         380 :     if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
     229           0 :         return CE_Failure;
     230             : 
     231         380 :     const vsi_l_offset offset = static_cast<vsi_l_offset>(m_nBits / 8) *
     232         380 :                                 nRasterXSize * (nRasterYSize - nBlockYOff - 1);
     233         380 :     SAGADataset *poGDS = static_cast<SAGADataset *>(poDS);
     234         380 :     assert(poGDS != nullptr);
     235             : 
     236         380 :     if (VSIFSeekL(poGDS->fp, offset, SEEK_SET) != 0)
     237             :     {
     238           0 :         CPLError(CE_Failure, CPLE_FileIO,
     239             :                  "Unable to seek to beginning of grid row.\n");
     240           0 :         return CE_Failure;
     241             :     }
     242             : 
     243         380 :     SwapBuffer(pImage);
     244             : 
     245             :     const bool bSuccess =
     246         380 :         (VSIFWriteL(pImage, m_nBits / 8, nBlockXSize, poGDS->fp) ==
     247         380 :          static_cast<unsigned>(nBlockXSize));
     248             : 
     249         380 :     SwapBuffer(pImage);
     250             : 
     251         380 :     if (!bSuccess)
     252             :     {
     253           0 :         CPLError(CE_Failure, CPLE_FileIO,
     254             :                  "Unable to write block to grid file.\n");
     255           0 :         return CE_Failure;
     256             :     }
     257             : 
     258         380 :     return CE_None;
     259             : }
     260             : 
     261             : /************************************************************************/
     262             : /*                           GetNoDataValue()                           */
     263             : /************************************************************************/
     264             : 
     265          72 : double SAGARasterBand::GetNoDataValue(int *pbSuccess)
     266             : {
     267          72 :     if (pbSuccess)
     268          59 :         *pbSuccess = TRUE;
     269             : 
     270          72 :     return m_NoData;
     271             : }
     272             : 
     273             : /************************************************************************/
     274             : /*                           SetNoDataValue()                           */
     275             : /************************************************************************/
     276             : 
     277           2 : CPLErr SAGARasterBand::SetNoDataValue(double dfNoData)
     278             : {
     279           2 :     if (eAccess == GA_ReadOnly)
     280             :     {
     281           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     282             :                  "Unable to set no data value, dataset opened read only.\n");
     283           1 :         return CE_Failure;
     284             :     }
     285             : 
     286           1 :     m_NoData = dfNoData;
     287           1 :     SAGADataset *poSAGADS = static_cast<SAGADataset *>(poDS);
     288           1 :     poSAGADS->headerDirty = true;
     289           1 :     return CE_None;
     290             : }
     291             : 
     292             : /************************************************************************/
     293             : /* ==================================================================== */
     294             : /*                              SAGADataset                             */
     295             : /* ==================================================================== */
     296             : /************************************************************************/
     297             : 
     298         108 : SAGADataset::SAGADataset() : fp(nullptr)
     299             : {
     300         108 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     301         108 : }
     302             : 
     303         216 : SAGADataset::~SAGADataset()
     304             : {
     305         108 :     if (headerDirty)
     306             :     {
     307          24 :         SAGARasterBand *poGRB = static_cast<SAGARasterBand *>(GetRasterBand(1));
     308          48 :         const CPLString osPath = CPLGetPathSafe(GetDescription());
     309          48 :         const CPLString osName = CPLGetBasenameSafe(GetDescription());
     310             :         const CPLString osFilename =
     311          24 :             CPLFormCIFilenameSafe(osPath, osName, ".sgrd");
     312          24 :         WriteHeader(osFilename, poGRB->GetRasterDataType(), poGRB->nRasterXSize,
     313             :                     poGRB->nRasterYSize, poGRB->m_Xmin, poGRB->m_Ymin,
     314             :                     poGRB->m_Cellsize, poGRB->m_NoData, 1.0, false);
     315             :     }
     316         108 :     FlushCache(true);
     317         108 :     if (fp != nullptr)
     318         108 :         VSIFCloseL(fp);
     319         216 : }
     320             : 
     321             : /************************************************************************/
     322             : /*                            GetFileList()                             */
     323             : /************************************************************************/
     324             : 
     325          25 : char **SAGADataset::GetFileList()
     326             : {
     327          50 :     const CPLString osPath = CPLGetPathSafe(GetDescription());
     328          25 :     const CPLString osName = CPLGetBasenameSafe(GetDescription());
     329             : 
     330             :     // Main data file, etc.
     331          25 :     char **papszFileList = GDALPamDataset::GetFileList();
     332             : 
     333          25 :     if (!EQUAL(CPLGetExtensionSafe(GetDescription()).c_str(), "sg-grd-z"))
     334             :     {
     335             :         // Header file.
     336          48 :         CPLString osFilename = CPLFormCIFilenameSafe(osPath, osName, ".sgrd");
     337          24 :         papszFileList = CSLAddString(papszFileList, osFilename);
     338             : 
     339             :         // projections file.
     340          24 :         osFilename = CPLFormCIFilenameSafe(osPath, osName, "prj");
     341             :         VSIStatBufL sStatBuf;
     342          24 :         if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
     343          10 :             papszFileList = CSLAddString(papszFileList, osFilename);
     344             :     }
     345             : 
     346          50 :     return papszFileList;
     347             : }
     348             : 
     349             : /************************************************************************/
     350             : /*                          GetSpatialRef()                             */
     351             : /************************************************************************/
     352             : 
     353           6 : const OGRSpatialReference *SAGADataset::GetSpatialRef() const
     354             : 
     355             : {
     356           6 :     if (!m_oSRS.IsEmpty())
     357           6 :         return &m_oSRS;
     358             : 
     359           0 :     return GDALPamDataset::GetSpatialRef();
     360             : }
     361             : 
     362             : /************************************************************************/
     363             : /*                           SetSpatialRef()                            */
     364             : /************************************************************************/
     365             : 
     366          23 : CPLErr SAGADataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     367             : 
     368             : {
     369             :     /* -------------------------------------------------------------------- */
     370             :     /*      Reset coordinate system on the dataset.                         */
     371             :     /* -------------------------------------------------------------------- */
     372          23 :     m_oSRS.Clear();
     373          23 :     if (poSRS == nullptr)
     374           0 :         return CE_None;
     375          23 :     m_oSRS = *poSRS;
     376             : 
     377             :     /* -------------------------------------------------------------------- */
     378             :     /*      Convert to ESRI WKT.                                            */
     379             :     /* -------------------------------------------------------------------- */
     380          23 :     char *pszESRI_SRS = nullptr;
     381          23 :     const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
     382          23 :     m_oSRS.exportToWkt(&pszESRI_SRS, apszOptions);
     383             : 
     384             :     /* -------------------------------------------------------------------- */
     385             :     /*      Write to .prj file.                                             */
     386             :     /* -------------------------------------------------------------------- */
     387             :     const CPLString osPrjFilename =
     388          23 :         CPLResetExtensionSafe(GetDescription(), "prj");
     389          23 :     VSILFILE *l_fp = VSIFOpenL(osPrjFilename.c_str(), "wt");
     390          23 :     if (l_fp != nullptr)
     391             :     {
     392          23 :         VSIFWriteL(pszESRI_SRS, 1, strlen(pszESRI_SRS), l_fp);
     393          23 :         VSIFWriteL(reinterpret_cast<void *>(const_cast<char *>("\n")), 1, 1,
     394             :                    l_fp);
     395          23 :         VSIFCloseL(l_fp);
     396             :     }
     397             : 
     398          23 :     CPLFree(pszESRI_SRS);
     399             : 
     400          23 :     return CE_None;
     401             : }
     402             : 
     403             : /************************************************************************/
     404             : /*                                Open()                                */
     405             : /************************************************************************/
     406             : 
     407       30197 : GDALDataset *SAGADataset::Open(GDALOpenInfo *poOpenInfo)
     408             : 
     409             : {
     410             :     /* -------------------------------------------------------------------- */
     411             :     /*  We assume the user is pointing to the binary (i.e. .sdat) file or a */
     412             :     /*  compressed raster (.sg-grd-z) file.                                 */
     413             :     /* -------------------------------------------------------------------- */
     414       30197 :     const auto &osExtension = poOpenInfo->osExtension;
     415             : 
     416       60128 :     if (!EQUAL(osExtension.c_str(), "sdat") &&
     417       29937 :         !EQUAL(osExtension.c_str(), "sg-grd-z"))
     418             :     {
     419       29922 :         return nullptr;
     420             :     }
     421             : 
     422         527 :     CPLString osPath, osFullname, osName, osHDRFilename;
     423             : 
     424         263 :     if (EQUAL(osExtension.c_str(), "sg-grd-z") &&
     425           2 :         !STARTS_WITH(poOpenInfo->pszFilename, "/vsizip"))
     426             :     {
     427           2 :         osPath = "/vsizip/{";
     428           2 :         osPath += poOpenInfo->pszFilename;
     429           2 :         osPath += "}/";
     430             : 
     431           2 :         char **filesinzip = VSIReadDir(osPath);
     432           2 :         if (filesinzip == nullptr)
     433           0 :             return nullptr;  // empty zip file
     434             : 
     435           2 :         CPLString file;
     436           4 :         for (int iFile = 0; filesinzip[iFile] != nullptr; iFile++)
     437             :         {
     438           4 :             if (EQUAL(CPLGetExtensionSafe(filesinzip[iFile]).c_str(), "sdat"))
     439             :             {
     440           2 :                 file = filesinzip[iFile];
     441           2 :                 break;
     442             :             }
     443             :         }
     444             : 
     445           2 :         CSLDestroy(filesinzip);
     446             : 
     447           2 :         osFullname = CPLFormFilenameSafe(osPath, file, nullptr);
     448           2 :         osName = CPLGetBasenameSafe(file);
     449           2 :         osHDRFilename = CPLFormFilenameSafe(
     450           4 :             osPath, CPLGetBasenameSafe(file).c_str(), "sgrd");
     451             :     }
     452             :     else
     453             :     {
     454         259 :         osFullname = poOpenInfo->pszFilename;
     455         259 :         osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     456         259 :         osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
     457         259 :         osHDRFilename = CPLFormCIFilenameSafe(
     458         518 :             osPath, CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str(),
     459         259 :             "sgrd");
     460             :     }
     461             : 
     462         261 :     VSILFILE *fp = VSIFOpenL(osHDRFilename, "r");
     463         261 :     if (fp == nullptr)
     464             :     {
     465         143 :         return nullptr;
     466             :     }
     467             : 
     468             :     /* -------------------------------------------------------------------- */
     469             :     /*      Is this file a SAGA header file?  Read a few lines of text      */
     470             :     /*      searching for something starting with nrows or ncols.           */
     471             :     /* -------------------------------------------------------------------- */
     472         118 :     int nRows = -1;
     473         118 :     int nCols = -1;
     474         118 :     double dXmin = 0.0;
     475         118 :     double dYmin = 0.0;
     476         118 :     double dCellsize = 0.0;
     477         118 :     double dNoData = 0.0;
     478         118 :     double dZFactor = 0.0;
     479         118 :     int nLineCount = 0;
     480         118 :     char szDataFormat[20] = "DOUBLE";
     481         118 :     char szByteOrderBig[10] = "FALSE";
     482         118 :     char szTopToBottom[10] = "FALSE";
     483             : 
     484         118 :     const char *pszLine = nullptr;
     485        1656 :     while ((pszLine = CPLReadLineL(fp)) != nullptr)
     486             :     {
     487        1538 :         nLineCount++;
     488             : 
     489        1538 :         if (nLineCount > 50 || strlen(pszLine) > 1000)
     490             :             break;
     491             : 
     492             :         char **papszTokens =
     493        1538 :             CSLTokenizeStringComplex(pszLine, " =", TRUE, FALSE);
     494        1538 :         if (CSLCount(papszTokens) < 2)
     495             :         {
     496         229 :             CSLDestroy(papszTokens);
     497         229 :             continue;
     498             :         }
     499             : 
     500        1309 :         char **papszHDR = CSLAddString(nullptr, pszLine);
     501             : 
     502        1309 :         if (STARTS_WITH_CI(papszTokens[0], "CELLCOUNT_X"))
     503         108 :             nCols = atoi(papszTokens[1]);
     504        1201 :         else if (STARTS_WITH_CI(papszTokens[0], "CELLCOUNT_Y"))
     505         108 :             nRows = atoi(papszTokens[1]);
     506        1093 :         else if (STARTS_WITH_CI(papszTokens[0], "POSITION_XMIN"))
     507         108 :             dXmin = CPLAtofM(papszTokens[1]);
     508         985 :         else if (STARTS_WITH_CI(papszTokens[0], "POSITION_YMIN"))
     509         108 :             dYmin = CPLAtofM(papszTokens[1]);
     510         877 :         else if (STARTS_WITH_CI(papszTokens[0], "CELLSIZE"))
     511         108 :             dCellsize = CPLAtofM(papszTokens[1]);
     512         769 :         else if (STARTS_WITH_CI(papszTokens[0], "NODATA_VALUE"))
     513         108 :             dNoData = CPLAtofM(papszTokens[1]);
     514         661 :         else if (STARTS_WITH_CI(papszTokens[0], "DATAFORMAT"))
     515         108 :             strncpy(szDataFormat, papszTokens[1], sizeof(szDataFormat) - 1);
     516         553 :         else if (STARTS_WITH_CI(papszTokens[0], "BYTEORDER_BIG"))
     517         109 :             strncpy(szByteOrderBig, papszTokens[1], sizeof(szByteOrderBig) - 1);
     518         444 :         else if (STARTS_WITH_CI(papszTokens[0], "TOPTOBOTTOM"))
     519         108 :             strncpy(szTopToBottom, papszTokens[1], sizeof(szTopToBottom) - 1);
     520         336 :         else if (STARTS_WITH_CI(papszTokens[0], "Z_FACTOR"))
     521         108 :             dZFactor = CPLAtofM(papszTokens[1]);
     522             : 
     523        1309 :         CSLDestroy(papszTokens);
     524        1309 :         CSLDestroy(papszHDR);
     525             :     }
     526             : 
     527         118 :     VSIFCloseL(fp);
     528             : 
     529             :     /* -------------------------------------------------------------------- */
     530             :     /*      Did we get the required keywords?  If not we return with        */
     531             :     /*      this never having been considered to be a match. This isn't     */
     532             :     /*      an error!                                                       */
     533             :     /* -------------------------------------------------------------------- */
     534         118 :     if (nRows == -1 || nCols == -1)
     535             :     {
     536          10 :         return nullptr;
     537             :     }
     538             : 
     539         108 :     if (!GDALCheckDatasetDimensions(nCols, nRows))
     540             :     {
     541           0 :         return nullptr;
     542             :     }
     543             : 
     544         108 :     if (STARTS_WITH_CI(szTopToBottom, "TRUE"))
     545             :     {
     546           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     547             :                  "Currently the SAGA Binary Grid driver does not support\n"
     548             :                  "SAGA grids written TOPTOBOTTOM.\n");
     549           0 :         return nullptr;
     550             :     }
     551         108 :     if (dZFactor != 1.0)
     552             :     {
     553           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     554             :                  "Currently the SAGA Binary Grid driver does not support\n"
     555             :                  "ZFACTORs other than 1.\n");
     556             :     }
     557             : 
     558             :     /* -------------------------------------------------------------------- */
     559             :     /*      Create a corresponding GDALDataset.                             */
     560             :     /* -------------------------------------------------------------------- */
     561         108 :     SAGADataset *poDS = new SAGADataset();
     562             : 
     563         108 :     poDS->eAccess = poOpenInfo->eAccess;
     564         108 :     if (poOpenInfo->eAccess == GA_ReadOnly)
     565          68 :         poDS->fp = VSIFOpenL(osFullname.c_str(), "rb");
     566             :     else
     567          40 :         poDS->fp = VSIFOpenL(osFullname.c_str(), "r+b");
     568             : 
     569         108 :     if (poDS->fp == nullptr)
     570             :     {
     571           0 :         delete poDS;
     572           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     573             :                  "VSIFOpenL(%s) failed unexpectedly.", osFullname.c_str());
     574           0 :         return nullptr;
     575             :     }
     576             : 
     577         108 :     poDS->nRasterXSize = nCols;
     578         108 :     poDS->nRasterYSize = nRows;
     579             : 
     580         108 :     SAGARasterBand *poBand = new SAGARasterBand(poDS, 1);
     581             : 
     582             :     /* -------------------------------------------------------------------- */
     583             :     /*      Figure out the byte order.                                      */
     584             :     /* -------------------------------------------------------------------- */
     585         108 :     if (STARTS_WITH_CI(szByteOrderBig, "TRUE"))
     586           0 :         poBand->m_ByteOrder = 1;
     587         108 :     else if (STARTS_WITH_CI(szByteOrderBig, "FALSE"))
     588         108 :         poBand->m_ByteOrder = 0;
     589             : 
     590             :     /* -------------------------------------------------------------------- */
     591             :     /*      Figure out the data type.                                       */
     592             :     /* -------------------------------------------------------------------- */
     593         108 :     if (EQUAL(szDataFormat, "BIT"))
     594             :     {
     595           0 :         poBand->SetDataType(GDT_Byte);
     596           0 :         poBand->m_nBits = 8;
     597             :     }
     598         108 :     else if (EQUAL(szDataFormat, "BYTE_UNSIGNED"))
     599             :     {
     600          13 :         poBand->SetDataType(GDT_Byte);
     601          13 :         poBand->m_nBits = 8;
     602             :     }
     603          95 :     else if (EQUAL(szDataFormat, "BYTE"))
     604             :     {
     605           0 :         poBand->SetDataType(GDT_Byte);
     606           0 :         poBand->m_nBits = 8;
     607             :     }
     608          95 :     else if (EQUAL(szDataFormat, "SHORTINT_UNSIGNED"))
     609             :     {
     610          13 :         poBand->SetDataType(GDT_UInt16);
     611          13 :         poBand->m_nBits = 16;
     612             :     }
     613          82 :     else if (EQUAL(szDataFormat, "SHORTINT"))
     614             :     {
     615          13 :         poBand->SetDataType(GDT_Int16);
     616          13 :         poBand->m_nBits = 16;
     617             :     }
     618          69 :     else if (EQUAL(szDataFormat, "INTEGER_UNSIGNED"))
     619             :     {
     620          13 :         poBand->SetDataType(GDT_UInt32);
     621          13 :         poBand->m_nBits = 32;
     622             :     }
     623          56 :     else if (EQUAL(szDataFormat, "INTEGER"))
     624             :     {
     625          13 :         poBand->SetDataType(GDT_Int32);
     626          13 :         poBand->m_nBits = 32;
     627             :     }
     628          43 :     else if (EQUAL(szDataFormat, "FLOAT"))
     629             :     {
     630          29 :         poBand->SetDataType(GDT_Float32);
     631          29 :         poBand->m_nBits = 32;
     632             :     }
     633          14 :     else if (EQUAL(szDataFormat, "DOUBLE"))
     634             :     {
     635          14 :         poBand->SetDataType(GDT_Float64);
     636          14 :         poBand->m_nBits = 64;
     637             :     }
     638             :     else
     639             :     {
     640           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     641             :                  "SAGA driver does not support the dataformat %s.",
     642             :                  szDataFormat);
     643           0 :         delete poBand;
     644           0 :         delete poDS;
     645           0 :         return nullptr;
     646             :     }
     647             : 
     648             :     /* -------------------------------------------------------------------- */
     649             :     /*      Save band information                                           */
     650             :     /* -------------------------------------------------------------------- */
     651         108 :     poBand->m_Xmin = dXmin;
     652         108 :     poBand->m_Ymin = dYmin;
     653         108 :     poBand->m_NoData = dNoData;
     654         108 :     poBand->m_Cellsize = dCellsize;
     655             : 
     656         108 :     poDS->SetBand(1, poBand);
     657             : 
     658             :     /* -------------------------------------------------------------------- */
     659             :     /*      Initialize any PAM information.                                 */
     660             :     /* -------------------------------------------------------------------- */
     661         108 :     poDS->SetDescription(poOpenInfo->pszFilename);
     662         108 :     poDS->TryLoadXML();
     663             : 
     664             :     /* -------------------------------------------------------------------- */
     665             :     /*      Check for a .prj file.                                          */
     666             :     /* -------------------------------------------------------------------- */
     667             :     const std::string osPrjFilename =
     668         108 :         CPLFormCIFilenameSafe(osPath, osName, "prj");
     669             : 
     670         108 :     fp = VSIFOpenL(osPrjFilename.c_str(), "r");
     671             : 
     672         108 :     if (fp != nullptr)
     673             :     {
     674          32 :         VSIFCloseL(fp);
     675             : 
     676          32 :         char **papszLines = CSLLoad(osPrjFilename.c_str());
     677             : 
     678          32 :         poDS->m_oSRS.importFromESRI(papszLines);
     679             : 
     680          32 :         CSLDestroy(papszLines);
     681             :     }
     682             : 
     683             :     /* -------------------------------------------------------------------- */
     684             :     /*      Check for external overviews.                                   */
     685             :     /* -------------------------------------------------------------------- */
     686         216 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
     687         108 :                                 poOpenInfo->GetSiblingFiles());
     688             : 
     689         108 :     return poDS;
     690             : }
     691             : 
     692             : /************************************************************************/
     693             : /*                          GetGeoTransform()                           */
     694             : /************************************************************************/
     695             : 
     696          11 : CPLErr SAGADataset::GetGeoTransform(double *padfGeoTransform)
     697             : {
     698          11 :     if (padfGeoTransform == nullptr)
     699           0 :         return CE_Failure;
     700             : 
     701          11 :     SAGARasterBand *poGRB = static_cast<SAGARasterBand *>(GetRasterBand(1));
     702             : 
     703          11 :     if (poGRB == nullptr)
     704             :     {
     705           0 :         padfGeoTransform[0] = 0;
     706           0 :         padfGeoTransform[1] = 1;
     707           0 :         padfGeoTransform[2] = 0;
     708           0 :         padfGeoTransform[3] = 0;
     709           0 :         padfGeoTransform[4] = 0;
     710           0 :         padfGeoTransform[5] = 1;
     711           0 :         return CE_Failure;
     712             :     }
     713             : 
     714             :     /* check if we have a PAM GeoTransform stored */
     715          11 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     716          11 :     CPLErr eErr = GDALPamDataset::GetGeoTransform(padfGeoTransform);
     717          11 :     CPLPopErrorHandler();
     718             : 
     719          11 :     if (eErr == CE_None)
     720           0 :         return CE_None;
     721             : 
     722          11 :     padfGeoTransform[1] = poGRB->m_Cellsize;
     723          11 :     padfGeoTransform[5] = poGRB->m_Cellsize * -1.0;
     724          11 :     padfGeoTransform[0] = poGRB->m_Xmin - poGRB->m_Cellsize / 2;
     725          11 :     padfGeoTransform[3] = poGRB->m_Ymin +
     726          11 :                           (nRasterYSize - 1) * poGRB->m_Cellsize +
     727          11 :                           poGRB->m_Cellsize / 2;
     728             : 
     729             :     /* tilt/rotation is not supported by SAGA grids */
     730          11 :     padfGeoTransform[4] = 0.0;
     731          11 :     padfGeoTransform[2] = 0.0;
     732             : 
     733          11 :     return CE_None;
     734             : }
     735             : 
     736             : /************************************************************************/
     737             : /*                          SetGeoTransform()                           */
     738             : /************************************************************************/
     739             : 
     740          23 : CPLErr SAGADataset::SetGeoTransform(double *padfGeoTransform)
     741             : {
     742             : 
     743          23 :     if (eAccess == GA_ReadOnly)
     744             :     {
     745           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     746             :                  "Unable to set GeoTransform, dataset opened read only.\n");
     747           0 :         return CE_Failure;
     748             :     }
     749             : 
     750          23 :     SAGARasterBand *poGRB = static_cast<SAGARasterBand *>(GetRasterBand(1));
     751             : 
     752          23 :     if (poGRB == nullptr || padfGeoTransform == nullptr)
     753           0 :         return CE_Failure;
     754             : 
     755          23 :     if (padfGeoTransform[1] != padfGeoTransform[5] * -1.0)
     756             :     {
     757           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     758             :                  "Unable to set GeoTransform, SAGA binary grids only support "
     759             :                  "the same cellsize in x-y.\n");
     760           0 :         return CE_Failure;
     761             :     }
     762             : 
     763          23 :     const double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
     764          23 :     const double dfMinY =
     765          23 :         padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
     766             : 
     767          23 :     poGRB->m_Xmin = dfMinX;
     768          23 :     poGRB->m_Ymin = dfMinY;
     769          23 :     poGRB->m_Cellsize = padfGeoTransform[1];
     770          23 :     headerDirty = true;
     771             : 
     772          23 :     return CE_None;
     773             : }
     774             : 
     775             : /************************************************************************/
     776             : /*                             WriteHeader()                            */
     777             : /************************************************************************/
     778             : 
     779          73 : CPLErr SAGADataset::WriteHeader(CPLString osHDRFilename, GDALDataType eType,
     780             :                                 int nXSize, int nYSize, double dfMinX,
     781             :                                 double dfMinY, double dfCellsize,
     782             :                                 double dfNoData, double dfZFactor,
     783             :                                 bool bTopToBottom)
     784             : 
     785             : {
     786          73 :     VSILFILE *fp = VSIFOpenL(osHDRFilename, "wt");
     787             : 
     788          73 :     if (fp == nullptr)
     789             :     {
     790           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to write .sgrd file %s.",
     791             :                  osHDRFilename.c_str());
     792           0 :         return CE_Failure;
     793             :     }
     794             : 
     795          73 :     VSIFPrintfL(fp, "NAME\t= %s\n", CPLGetBasenameSafe(osHDRFilename).c_str());
     796          73 :     VSIFPrintfL(fp, "DESCRIPTION\t=\n");
     797          73 :     VSIFPrintfL(fp, "UNIT\t=\n");
     798          73 :     VSIFPrintfL(fp, "DATAFILE_OFFSET\t= 0\n");
     799             : 
     800          73 :     if (eType == GDT_Int32)
     801           8 :         VSIFPrintfL(fp, "DATAFORMAT\t= INTEGER\n");
     802          65 :     else if (eType == GDT_UInt32)
     803           8 :         VSIFPrintfL(fp, "DATAFORMAT\t= INTEGER_UNSIGNED\n");
     804          57 :     else if (eType == GDT_Int16)
     805           8 :         VSIFPrintfL(fp, "DATAFORMAT\t= SHORTINT\n");
     806          49 :     else if (eType == GDT_UInt16)
     807           8 :         VSIFPrintfL(fp, "DATAFORMAT\t= SHORTINT_UNSIGNED\n");
     808          41 :     else if (eType == GDT_Byte)
     809          18 :         VSIFPrintfL(fp, "DATAFORMAT\t= BYTE_UNSIGNED\n");
     810          23 :     else if (eType == GDT_Float32)
     811          13 :         VSIFPrintfL(fp, "DATAFORMAT\t= FLOAT\n");
     812             :     else  // if( eType == GDT_Float64 )
     813          10 :         VSIFPrintfL(fp, "DATAFORMAT\t= DOUBLE\n");
     814             : #ifdef CPL_LSB
     815          73 :     VSIFPrintfL(fp, "BYTEORDER_BIG\t= FALSE\n");
     816             : #else
     817             :     VSIFPrintfL(fp, "BYTEORDER_BIG\t= TRUE\n");
     818             : #endif
     819             : 
     820          73 :     VSIFPrintfL(fp, "POSITION_XMIN\t= %.10f\n", dfMinX);
     821          73 :     VSIFPrintfL(fp, "POSITION_YMIN\t= %.10f\n", dfMinY);
     822          73 :     VSIFPrintfL(fp, "CELLCOUNT_X\t= %d\n", nXSize);
     823          73 :     VSIFPrintfL(fp, "CELLCOUNT_Y\t= %d\n", nYSize);
     824          73 :     VSIFPrintfL(fp, "CELLSIZE\t= %.10f\n", dfCellsize);
     825          73 :     VSIFPrintfL(fp, "Z_FACTOR\t= %f\n", dfZFactor);
     826          73 :     VSIFPrintfL(fp, "NODATA_VALUE\t= %f\n", dfNoData);
     827          73 :     if (bTopToBottom)
     828           0 :         VSIFPrintfL(fp, "TOPTOBOTTOM\t= TRUE\n");
     829             :     else
     830          73 :         VSIFPrintfL(fp, "TOPTOBOTTOM\t= FALSE\n");
     831             : 
     832          73 :     VSIFCloseL(fp);
     833             : 
     834          73 :     return CE_None;
     835             : }
     836             : 
     837             : /************************************************************************/
     838             : /*                               Create()                               */
     839             : /************************************************************************/
     840             : 
     841          81 : GDALDataset *SAGADataset::Create(const char *pszFilename, int nXSize,
     842             :                                  int nYSize, int nBandsIn, GDALDataType eType,
     843             :                                  char **papszParamList)
     844             : 
     845             : {
     846          81 :     if (nXSize <= 0 || nYSize <= 0)
     847             :     {
     848           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     849             :                  "Unable to create grid, both X and Y size must be "
     850             :                  "non-negative.\n");
     851             : 
     852           0 :         return nullptr;
     853             :     }
     854             : 
     855          81 :     if (nBandsIn != 1)
     856             :     {
     857          18 :         CPLError(CE_Failure, CPLE_IllegalArg,
     858             :                  "SAGA Binary Grid only supports 1 band");
     859          18 :         return nullptr;
     860             :     }
     861             : 
     862          63 :     if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 &&
     863          30 :         eType != GDT_UInt32 && eType != GDT_Int32 && eType != GDT_Float32 &&
     864             :         eType != GDT_Float64)
     865             :     {
     866          11 :         CPLError(CE_Failure, CPLE_AppDefined,
     867             :                  "SAGA Binary Grid only supports Byte, UInt16, Int16, "
     868             :                  "UInt32, Int32, Float32 and Float64 datatypes.  Unable to "
     869             :                  "create with type %s.\n",
     870             :                  GDALGetDataTypeName(eType));
     871             : 
     872          11 :         return nullptr;
     873             :     }
     874             : 
     875          52 :     VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
     876             : 
     877          52 :     if (fp == nullptr)
     878             :     {
     879           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     880             :                  "Attempt to create file '%s' failed.\n", pszFilename);
     881           3 :         return nullptr;
     882             :     }
     883             : 
     884          49 :     double dfNoDataVal = 0.0;
     885             : 
     886             :     const char *pszNoDataValue =
     887          49 :         CSLFetchNameValue(papszParamList, "NODATA_VALUE");
     888          49 :     if (pszNoDataValue)
     889             :     {
     890           2 :         dfNoDataVal = CPLAtofM(pszNoDataValue);
     891             :     }
     892             :     else
     893             :     {
     894          47 :         switch (eType) /* GDT_Byte, GDT_UInt16, GDT_Int16, GDT_UInt32  */
     895             :         {              /* GDT_Int32, GDT_Float32, GDT_Float64 */
     896          15 :             case (GDT_Byte):
     897             :             {
     898          15 :                 dfNoDataVal = SG_NODATA_GDT_Byte;
     899          15 :                 break;
     900             :             }
     901           5 :             case (GDT_UInt16):
     902             :             {
     903           5 :                 dfNoDataVal = SG_NODATA_GDT_UInt16;
     904           5 :                 break;
     905             :             }
     906           5 :             case (GDT_Int16):
     907             :             {
     908           5 :                 dfNoDataVal = SG_NODATA_GDT_Int16;
     909           5 :                 break;
     910             :             }
     911           5 :             case (GDT_UInt32):
     912             :             {
     913           5 :                 dfNoDataVal = SG_NODATA_GDT_UInt32;
     914           5 :                 break;
     915             :             }
     916           5 :             case (GDT_Int32):
     917             :             {
     918           5 :                 dfNoDataVal = SG_NODATA_GDT_Int32;
     919           5 :                 break;
     920             :             }
     921           6 :             default:
     922             :             case (GDT_Float32):
     923             :             {
     924           6 :                 dfNoDataVal = SG_NODATA_GDT_Float32;
     925           6 :                 break;
     926             :             }
     927           6 :             case (GDT_Float64):
     928             :             {
     929           6 :                 dfNoDataVal = SG_NODATA_GDT_Float64;
     930           6 :                 break;
     931             :             }
     932             :         }
     933             :     }
     934             : 
     935             :     double dfNoDataForAlignment;
     936          49 :     void *abyNoData = &dfNoDataForAlignment;
     937          49 :     GDALCopyWords(&dfNoDataVal, GDT_Float64, 0, abyNoData, eType, 0, 1);
     938             : 
     939          98 :     const CPLString osHdrFilename = CPLResetExtensionSafe(pszFilename, "sgrd");
     940          49 :     CPLErr eErr = WriteHeader(osHdrFilename, eType, nXSize, nYSize, 0.0, 0.0,
     941             :                               1.0, dfNoDataVal, 1.0, false);
     942             : 
     943          49 :     if (eErr != CE_None)
     944             :     {
     945           0 :         VSIFCloseL(fp);
     946           0 :         return nullptr;
     947             :     }
     948             : 
     949          49 :     if (CPLFetchBool(papszParamList, "FILL_NODATA", true))
     950             :     {
     951          23 :         const int nDataTypeSize = GDALGetDataTypeSize(eType) / 8;
     952             :         GByte *pabyNoDataBuf =
     953          23 :             reinterpret_cast<GByte *>(VSIMalloc2(nDataTypeSize, nXSize));
     954          23 :         if (pabyNoDataBuf == nullptr)
     955             :         {
     956           0 :             VSIFCloseL(fp);
     957           0 :             return nullptr;
     958             :         }
     959             : 
     960         889 :         for (int iCol = 0; iCol < nXSize; iCol++)
     961             :         {
     962         866 :             memcpy(pabyNoDataBuf + iCol * nDataTypeSize, abyNoData,
     963             :                    nDataTypeSize);
     964             :         }
     965             : 
     966         889 :         for (int iRow = 0; iRow < nYSize; iRow++)
     967             :         {
     968         866 :             if (VSIFWriteL(pabyNoDataBuf, nDataTypeSize, nXSize, fp) !=
     969         866 :                 static_cast<unsigned>(nXSize))
     970             :             {
     971           0 :                 VSIFCloseL(fp);
     972           0 :                 VSIFree(pabyNoDataBuf);
     973           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     974             :                          "Unable to write grid cell.  Disk full?\n");
     975           0 :                 return nullptr;
     976             :             }
     977             :         }
     978             : 
     979          23 :         VSIFree(pabyNoDataBuf);
     980             :     }
     981             : 
     982          49 :     VSIFCloseL(fp);
     983             : 
     984          49 :     return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
     985             : }
     986             : 
     987             : /************************************************************************/
     988             : /*                             CreateCopy()                             */
     989             : /************************************************************************/
     990             : 
     991          38 : GDALDataset *SAGADataset::CreateCopy(const char *pszFilename,
     992             :                                      GDALDataset *poSrcDS, int bStrict,
     993             :                                      CPL_UNUSED char **papszOptions,
     994             :                                      GDALProgressFunc pfnProgress,
     995             :                                      void *pProgressData)
     996             : {
     997          38 :     if (pfnProgress == nullptr)
     998           0 :         pfnProgress = GDALDummyProgress;
     999             : 
    1000          38 :     int nBands = poSrcDS->GetRasterCount();
    1001          38 :     if (nBands == 0)
    1002             :     {
    1003           1 :         CPLError(
    1004             :             CE_Failure, CPLE_NotSupported,
    1005             :             "SAGA driver does not support source dataset with zero band.\n");
    1006           1 :         return nullptr;
    1007             :     }
    1008          37 :     else if (nBands > 1)
    1009             :     {
    1010           4 :         if (bStrict)
    1011             :         {
    1012           4 :             CPLError(CE_Failure, CPLE_NotSupported,
    1013             :                      "Unable to create copy, SAGA Binary Grid "
    1014             :                      "format only supports one raster band.\n");
    1015           4 :             return nullptr;
    1016             :         }
    1017             :         else
    1018           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1019             :                      "SAGA Binary Grid format only supports one "
    1020             :                      "raster band, first band will be copied.\n");
    1021             :     }
    1022             : 
    1023          33 :     GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
    1024             : 
    1025          33 :     char **papszCreateOptions = CSLSetNameValue(nullptr, "FILL_NODATA", "NO");
    1026             : 
    1027          33 :     int bHasNoDataValue = FALSE;
    1028          33 :     const double dfNoDataValue = poSrcBand->GetNoDataValue(&bHasNoDataValue);
    1029          33 :     if (bHasNoDataValue)
    1030             :         papszCreateOptions =
    1031           2 :             CSLSetNameValue(papszCreateOptions, "NODATA_VALUE",
    1032             :                             CPLSPrintf("%.16g", dfNoDataValue));
    1033             : 
    1034             :     GDALDataset *poDstDS =
    1035          33 :         Create(pszFilename, poSrcBand->GetXSize(), poSrcBand->GetYSize(), 1,
    1036             :                poSrcBand->GetRasterDataType(), papszCreateOptions);
    1037          33 :     CSLDestroy(papszCreateOptions);
    1038             : 
    1039          33 :     if (poDstDS == nullptr)
    1040          17 :         return nullptr;
    1041             : 
    1042             :     /* -------------------------------------------------------------------- */
    1043             :     /*      Copy band data.                                                 */
    1044             :     /* -------------------------------------------------------------------- */
    1045             : 
    1046             :     CPLErr eErr =
    1047          16 :         GDALDatasetCopyWholeRaster((GDALDatasetH)poSrcDS, (GDALDatasetH)poDstDS,
    1048             :                                    nullptr, pfnProgress, pProgressData);
    1049             : 
    1050          16 :     if (eErr == CE_Failure)
    1051             :     {
    1052           0 :         delete poDstDS;
    1053           0 :         return nullptr;
    1054             :     }
    1055             : 
    1056             :     double adfGeoTransform[6];
    1057             : 
    1058          16 :     poSrcDS->GetGeoTransform(adfGeoTransform);
    1059          16 :     poDstDS->SetGeoTransform(adfGeoTransform);
    1060             : 
    1061          16 :     poDstDS->SetProjection(poSrcDS->GetProjectionRef());
    1062             : 
    1063          16 :     return poDstDS;
    1064             : }
    1065             : 
    1066             : /************************************************************************/
    1067             : /*                          GDALRegister_SAGA()                         */
    1068             : /************************************************************************/
    1069             : 
    1070        1682 : void GDALRegister_SAGA()
    1071             : 
    1072             : {
    1073        1682 :     if (GDALGetDriverByName("SAGA") != nullptr)
    1074         301 :         return;
    1075             : 
    1076        1381 :     GDALDriver *poDriver = new GDALDriver();
    1077             : 
    1078        1381 :     poDriver->SetDescription("SAGA");
    1079        1381 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1080        1381 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1081        1381 :                               "SAGA GIS Binary Grid (.sdat, .sg-grd-z)");
    1082        1381 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/sdat.html");
    1083        1381 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "sdat sg-grd-z");
    1084        1381 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    1085             :                               "Byte Int16 "
    1086        1381 :                               "UInt16 Int32 UInt32 Float32 Float64");
    1087             : 
    1088        1381 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1089             : 
    1090        1381 :     poDriver->pfnOpen = SAGADataset::Open;
    1091        1381 :     poDriver->pfnCreate = SAGADataset::Create;
    1092        1381 :     poDriver->pfnCreateCopy = SAGADataset::CreateCopy;
    1093             : 
    1094        1381 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1095             : }

Generated by: LCOV version 1.14