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

Generated by: LCOV version 1.14