LCOV - code coverage report
Current view: top level - frmts/saga - sagadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 383 457 83.8 %
Date: 2024-11-21 22:18:42 Functions: 20 20 100.0 %

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

Generated by: LCOV version 1.14