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

Generated by: LCOV version 1.14