• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.99
/frmts/saga/sagadataset.cpp
1
/******************************************************************************
2
 * Project:  SAGA GIS Binary Driver
3
 * Purpose:  Implements the SAGA GIS Binary Grid Format.
4
 * Author:   Volker Wichmann, wichmann@laserdata.at
5
 *   (Based on gsbgdataset.cpp by Kevin Locke and Frank Warmerdam)
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2009, Volker Wichmann <wichmann@laserdata.at>
9
 * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_conv.h"
15

16
#include <cassert>
17
#include <cfloat>
18
#include <climits>
19

20
#include "gdal_frmts.h"
21
#include "gdal_pam.h"
22
#include "ogr_spatialref.h"
23

24
#ifndef INT_MAX
25
#define INT_MAX 2147483647
26
#endif /* INT_MAX */
27

28
/* NODATA Values */
29
// #define SG_NODATA_GDT_Bit 0.0
30
constexpr GByte SG_NODATA_GDT_Byte = 255;
31
#define SG_NODATA_GDT_UInt16 65535
32
#define SG_NODATA_GDT_Int16 -32767
33
#define SG_NODATA_GDT_UInt32 4294967295U
34
#define SG_NODATA_GDT_Int32 -2147483647
35
#define SG_NODATA_GDT_Float32 -99999.0
36
#define SG_NODATA_GDT_Float64 -99999.0
37

38
/************************************************************************/
39
/* ==================================================================== */
40
/*                              SAGADataset                             */
41
/* ==================================================================== */
42
/************************************************************************/
43

44
class SAGARasterBand;
45

46
class SAGADataset final : public GDALPamDataset
47
{
48
    friend class SAGARasterBand;
49

50
    static CPLErr WriteHeader(const CPLString &osHDRFilename,
51
                              GDALDataType eType, int nXSize, int nYSize,
52
                              double dfMinX, double dfMinY, double dfCellsize,
53
                              double dfNoData, double dfZFactor,
54
                              bool bTopToBottom);
55
    VSILFILE *fp;
56
    OGRSpatialReference m_oSRS{};
57
    bool headerDirty = false;
58

59
  public:
60
    SAGADataset();
61
    virtual ~SAGADataset();
62

63
    static GDALDataset *Open(GDALOpenInfo *);
64
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
65
                               int nBandsIn, GDALDataType eType,
66
                               char **papszParamList);
67
    static GDALDataset *CreateCopy(const char *pszFilename,
68
                                   GDALDataset *poSrcDS, int bStrict,
69
                                   char **papszOptions,
70
                                   GDALProgressFunc pfnProgress,
71
                                   void *pProgressData);
72

73
    const OGRSpatialReference *GetSpatialRef() const override;
74
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
75

76
    virtual char **GetFileList() override;
77

78
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
79
    CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
80
};
81

82
/************************************************************************/
83
/* ==================================================================== */
84
/*                            SAGARasterBand                            */
85
/* ==================================================================== */
86
/************************************************************************/
87

88
class SAGARasterBand final : public GDALPamRasterBand
89
{
90
    friend class SAGADataset;
91

92
    double m_Xmin;
93
    double m_Ymin;
94
    double m_Cellsize;
95
    double m_NoData;
96
    int m_ByteOrder;
97
    int m_nBits;
98

99
    void SetDataType(GDALDataType eType);
100
    void SwapBuffer(void *pImage) const;
101

102
  public:
103
    SAGARasterBand(SAGADataset *, int);
104

105
    CPLErr IReadBlock(int, int, void *) override;
106
    CPLErr IWriteBlock(int, int, void *) override;
107

108
    double GetNoDataValue(int *pbSuccess = nullptr) override;
109
    CPLErr SetNoDataValue(double dfNoData) override;
110
};
111

112
/************************************************************************/
113
/*                           SAGARasterBand()                           */
114
/************************************************************************/
115

116
SAGARasterBand::SAGARasterBand(SAGADataset *poDS_, int nBand_)
108✔
117
    : m_Xmin(0.0), m_Ymin(0.0), m_Cellsize(0.0), m_NoData(0.0), m_ByteOrder(0),
118
      m_nBits(0)
108✔
119
{
120
    poDS = poDS_;
108✔
121
    nBand = nBand_;
108✔
122

123
    eDataType = GDT_Float32;
108✔
124

125
    nBlockXSize = poDS->GetRasterXSize();
108✔
126
    nBlockYSize = 1;
108✔
127
}
108✔
128

129
/************************************************************************/
130
/*                            SetDataType()                             */
131
/************************************************************************/
132

133
void SAGARasterBand::SetDataType(GDALDataType eType)
108✔
134

135
{
136
    eDataType = eType;
108✔
137
    return;
108✔
138
}
139

140
/************************************************************************/
141
/*                             SwapBuffer()                             */
142
/************************************************************************/
143

144
void SAGARasterBand::SwapBuffer(void *pImage) const
1,127✔
145
{
146

147
#ifdef CPL_LSB
148
    const bool bSwap = (m_ByteOrder == 1);
1,127✔
149
#else
150
    const bool bSwap = (m_ByteOrder == 0);
151
#endif
152

153
    if (bSwap)
1,127✔
154
    {
155
        if (m_nBits == 16)
×
156
        {
157
            short *pImage16 = reinterpret_cast<short *>(pImage);
×
158
            for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
×
159
            {
160
                CPL_SWAP16PTR(pImage16 + iPixel);
×
161
            }
162
        }
163
        else if (m_nBits == 32)
×
164
        {
165
            int *pImage32 = reinterpret_cast<int *>(pImage);
×
166
            for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
×
167
            {
168
                CPL_SWAP32PTR(pImage32 + iPixel);
×
169
            }
170
        }
171
        else if (m_nBits == 64)
×
172
        {
173
            double *pImage64 = reinterpret_cast<double *>(pImage);
×
174
            for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
×
175
            {
176
                CPL_SWAP64PTR(pImage64 + iPixel);
×
177
            }
178
        }
179
    }
180
}
1,127✔
181

182
/************************************************************************/
183
/*                             IReadBlock()                             */
184
/************************************************************************/
185

186
CPLErr SAGARasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
367✔
187

188
{
189
    if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
367✔
190
        return CE_Failure;
×
191

192
    SAGADataset *poGDS = static_cast<SAGADataset *>(poDS);
367✔
193
    vsi_l_offset offset = static_cast<vsi_l_offset>(m_nBits / 8) *
367✔
194
                          nRasterXSize * (nRasterYSize - nBlockYOff - 1);
367✔
195

196
    if (VSIFSeekL(poGDS->fp, offset, SEEK_SET) != 0)
367✔
197
    {
198
        CPLError(CE_Failure, CPLE_FileIO,
×
199
                 "Unable to seek to beginning of grid row.\n");
200
        return CE_Failure;
×
201
    }
202
    if (VSIFReadL(pImage, m_nBits / 8, nBlockXSize, poGDS->fp) !=
367✔
203
        static_cast<unsigned>(nBlockXSize))
367✔
204
    {
205
        CPLError(CE_Failure, CPLE_FileIO,
×
206
                 "Unable to read block from grid file.\n");
207
        return CE_Failure;
×
208
    }
209

210
    SwapBuffer(pImage);
367✔
211

212
    return CE_None;
367✔
213
}
214

215
/************************************************************************/
216
/*                            IWriteBlock()                             */
217
/************************************************************************/
218

219
CPLErr SAGARasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
380✔
220

221
{
222
    if (eAccess == GA_ReadOnly)
380✔
223
    {
224
        CPLError(CE_Failure, CPLE_NoWriteAccess,
×
225
                 "Unable to write block, dataset opened read only.\n");
226
        return CE_Failure;
×
227
    }
228

229
    if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
380✔
230
        return CE_Failure;
×
231

232
    const vsi_l_offset offset = static_cast<vsi_l_offset>(m_nBits / 8) *
380✔
233
                                nRasterXSize * (nRasterYSize - nBlockYOff - 1);
380✔
234
    SAGADataset *poGDS = static_cast<SAGADataset *>(poDS);
380✔
235
    assert(poGDS != nullptr);
380✔
236

237
    if (VSIFSeekL(poGDS->fp, offset, SEEK_SET) != 0)
380✔
238
    {
239
        CPLError(CE_Failure, CPLE_FileIO,
×
240
                 "Unable to seek to beginning of grid row.\n");
241
        return CE_Failure;
×
242
    }
243

244
    SwapBuffer(pImage);
380✔
245

246
    const bool bSuccess =
247
        (VSIFWriteL(pImage, m_nBits / 8, nBlockXSize, poGDS->fp) ==
380✔
248
         static_cast<unsigned>(nBlockXSize));
380✔
249

250
    SwapBuffer(pImage);
380✔
251

252
    if (!bSuccess)
380✔
253
    {
254
        CPLError(CE_Failure, CPLE_FileIO,
×
255
                 "Unable to write block to grid file.\n");
256
        return CE_Failure;
×
257
    }
258

259
    return CE_None;
380✔
260
}
261

262
/************************************************************************/
263
/*                           GetNoDataValue()                           */
264
/************************************************************************/
265

266
double SAGARasterBand::GetNoDataValue(int *pbSuccess)
72✔
267
{
268
    if (pbSuccess)
72✔
269
        *pbSuccess = TRUE;
59✔
270

271
    return m_NoData;
72✔
272
}
273

274
/************************************************************************/
275
/*                           SetNoDataValue()                           */
276
/************************************************************************/
277

278
CPLErr SAGARasterBand::SetNoDataValue(double dfNoData)
2✔
279
{
280
    if (eAccess == GA_ReadOnly)
2✔
281
    {
282
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1✔
283
                 "Unable to set no data value, dataset opened read only.\n");
284
        return CE_Failure;
1✔
285
    }
286

287
    m_NoData = dfNoData;
1✔
288
    SAGADataset *poSAGADS = static_cast<SAGADataset *>(poDS);
1✔
289
    poSAGADS->headerDirty = true;
1✔
290
    return CE_None;
1✔
291
}
292

293
/************************************************************************/
294
/* ==================================================================== */
295
/*                              SAGADataset                             */
296
/* ==================================================================== */
297
/************************************************************************/
298

299
SAGADataset::SAGADataset() : fp(nullptr)
108✔
300
{
301
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
108✔
302
}
108✔
303

304
SAGADataset::~SAGADataset()
216✔
305
{
306
    if (headerDirty)
108✔
307
    {
308
        SAGARasterBand *poGRB = static_cast<SAGARasterBand *>(GetRasterBand(1));
24✔
309
        const CPLString osPath = CPLGetPathSafe(GetDescription());
48✔
310
        const CPLString osName = CPLGetBasenameSafe(GetDescription());
48✔
311
        const CPLString osFilename =
312
            CPLFormCIFilenameSafe(osPath, osName, ".sgrd");
48✔
313
        WriteHeader(osFilename, poGRB->GetRasterDataType(), poGRB->nRasterXSize,
24✔
314
                    poGRB->nRasterYSize, poGRB->m_Xmin, poGRB->m_Ymin,
315
                    poGRB->m_Cellsize, poGRB->m_NoData, 1.0, false);
316
    }
317
    FlushCache(true);
108✔
318
    if (fp != nullptr)
108✔
319
        VSIFCloseL(fp);
108✔
320
}
216✔
321

322
/************************************************************************/
323
/*                            GetFileList()                             */
324
/************************************************************************/
325

326
char **SAGADataset::GetFileList()
25✔
327
{
328
    const CPLString osPath = CPLGetPathSafe(GetDescription());
50✔
329
    const CPLString osName = CPLGetBasenameSafe(GetDescription());
25✔
330

331
    // Main data file, etc.
332
    char **papszFileList = GDALPamDataset::GetFileList();
25✔
333

334
    if (!EQUAL(CPLGetExtensionSafe(GetDescription()).c_str(), "sg-grd-z"))
25✔
335
    {
336
        // Header file.
337
        CPLString osFilename = CPLFormCIFilenameSafe(osPath, osName, ".sgrd");
48✔
338
        papszFileList = CSLAddString(papszFileList, osFilename);
24✔
339

340
        // projections file.
341
        osFilename = CPLFormCIFilenameSafe(osPath, osName, "prj");
24✔
342
        VSIStatBufL sStatBuf;
343
        if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
24✔
344
            papszFileList = CSLAddString(papszFileList, osFilename);
10✔
345
    }
346

347
    return papszFileList;
50✔
348
}
349

350
/************************************************************************/
351
/*                          GetSpatialRef()                             */
352
/************************************************************************/
353

354
const OGRSpatialReference *SAGADataset::GetSpatialRef() const
6✔
355

356
{
357
    if (!m_oSRS.IsEmpty())
6✔
358
        return &m_oSRS;
6✔
359

360
    return GDALPamDataset::GetSpatialRef();
×
361
}
362

363
/************************************************************************/
364
/*                           SetSpatialRef()                            */
365
/************************************************************************/
366

367
CPLErr SAGADataset::SetSpatialRef(const OGRSpatialReference *poSRS)
23✔
368

369
{
370
    /* -------------------------------------------------------------------- */
371
    /*      Reset coordinate system on the dataset.                         */
372
    /* -------------------------------------------------------------------- */
373
    m_oSRS.Clear();
23✔
374
    if (poSRS == nullptr)
23✔
375
        return CE_None;
×
376
    m_oSRS = *poSRS;
23✔
377

378
    /* -------------------------------------------------------------------- */
379
    /*      Convert to ESRI WKT.                                            */
380
    /* -------------------------------------------------------------------- */
381
    char *pszESRI_SRS = nullptr;
23✔
382
    const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
23✔
383
    m_oSRS.exportToWkt(&pszESRI_SRS, apszOptions);
23✔
384

385
    /* -------------------------------------------------------------------- */
386
    /*      Write to .prj file.                                             */
387
    /* -------------------------------------------------------------------- */
388
    const CPLString osPrjFilename =
389
        CPLResetExtensionSafe(GetDescription(), "prj");
23✔
390
    VSILFILE *l_fp = VSIFOpenL(osPrjFilename.c_str(), "wt");
23✔
391
    if (l_fp != nullptr)
23✔
392
    {
393
        VSIFWriteL(pszESRI_SRS, 1, strlen(pszESRI_SRS), l_fp);
23✔
394
        VSIFWriteL(reinterpret_cast<void *>(const_cast<char *>("\n")), 1, 1,
23✔
395
                   l_fp);
396
        VSIFCloseL(l_fp);
23✔
397
    }
398

399
    CPLFree(pszESRI_SRS);
23✔
400

401
    return CE_None;
23✔
402
}
403

404
/************************************************************************/
405
/*                                Open()                                */
406
/************************************************************************/
407

408
GDALDataset *SAGADataset::Open(GDALOpenInfo *poOpenInfo)
32,613✔
409

410
{
411
    /* -------------------------------------------------------------------- */
412
    /*  We assume the user is pointing to the binary (i.e. .sdat) file or a */
413
    /*  compressed raster (.sg-grd-z) file.                                 */
414
    /* -------------------------------------------------------------------- */
415
    const auto &osExtension = poOpenInfo->osExtension;
32,613✔
416

417
    if (!EQUAL(osExtension.c_str(), "sdat") &&
64,971✔
418
        !EQUAL(osExtension.c_str(), "sg-grd-z"))
32,360✔
419
    {
420
        return nullptr;
32,354✔
421
    }
422

423
    CPLString osPath, osFullname, osName, osHDRFilename;
523✔
424

425
    if (EQUAL(osExtension.c_str(), "sg-grd-z") &&
263✔
426
        !STARTS_WITH(poOpenInfo->pszFilename, "/vsizip"))
2✔
427
    {
428
        osPath = "/vsizip/{";
2✔
429
        osPath += poOpenInfo->pszFilename;
2✔
430
        osPath += "}/";
2✔
431

432
        char **filesinzip = VSIReadDir(osPath);
2✔
433
        if (filesinzip == nullptr)
2✔
434
            return nullptr;  // empty zip file
×
435

436
        CPLString file;
2✔
437
        for (int iFile = 0; filesinzip[iFile] != nullptr; iFile++)
4✔
438
        {
439
            if (EQUAL(CPLGetExtensionSafe(filesinzip[iFile]).c_str(), "sdat"))
4✔
440
            {
441
                file = filesinzip[iFile];
2✔
442
                break;
2✔
443
            }
444
        }
445

446
        CSLDestroy(filesinzip);
2✔
447

448
        osFullname = CPLFormFilenameSafe(osPath, file, nullptr);
2✔
449
        osName = CPLGetBasenameSafe(file);
2✔
450
        osHDRFilename = CPLFormFilenameSafe(
2✔
451
            osPath, CPLGetBasenameSafe(file).c_str(), "sgrd");
4✔
452
    }
453
    else
454
    {
455
        osFullname = poOpenInfo->pszFilename;
259✔
456
        osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
259✔
457
        osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
259✔
458
        osHDRFilename = CPLFormCIFilenameSafe(
259✔
459
            osPath, CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str(),
518✔
460
            "sgrd");
259✔
461
    }
462

463
    VSILFILE *fp = VSIFOpenL(osHDRFilename, "r");
261✔
464
    if (fp == nullptr)
261✔
465
    {
466
        return nullptr;
143✔
467
    }
468

469
    /* -------------------------------------------------------------------- */
470
    /*      Is this file a SAGA header file?  Read a few lines of text      */
471
    /*      searching for something starting with nrows or ncols.           */
472
    /* -------------------------------------------------------------------- */
473
    int nRows = -1;
118✔
474
    int nCols = -1;
118✔
475
    double dXmin = 0.0;
118✔
476
    double dYmin = 0.0;
118✔
477
    double dCellsize = 0.0;
118✔
478
    double dNoData = 0.0;
118✔
479
    double dZFactor = 0.0;
118✔
480
    int nLineCount = 0;
118✔
481
    char szDataFormat[20] = "DOUBLE";
118✔
482
    char szByteOrderBig[10] = "FALSE";
118✔
483
    char szTopToBottom[10] = "FALSE";
118✔
484

485
    const char *pszLine = nullptr;
118✔
486
    while ((pszLine = CPLReadLineL(fp)) != nullptr)
1,656✔
487
    {
488
        nLineCount++;
1,538✔
489

490
        if (nLineCount > 50 || strlen(pszLine) > 1000)
1,538✔
491
            break;
492

493
        char **papszTokens =
494
            CSLTokenizeStringComplex(pszLine, " =", TRUE, FALSE);
1,538✔
495
        if (CSLCount(papszTokens) < 2)
1,538✔
496
        {
497
            CSLDestroy(papszTokens);
229✔
498
            continue;
229✔
499
        }
500

501
        char **papszHDR = CSLAddString(nullptr, pszLine);
1,309✔
502

503
        if (STARTS_WITH_CI(papszTokens[0], "CELLCOUNT_X"))
1,309✔
504
            nCols = atoi(papszTokens[1]);
108✔
505
        else if (STARTS_WITH_CI(papszTokens[0], "CELLCOUNT_Y"))
1,201✔
506
            nRows = atoi(papszTokens[1]);
108✔
507
        else if (STARTS_WITH_CI(papszTokens[0], "POSITION_XMIN"))
1,093✔
508
            dXmin = CPLAtofM(papszTokens[1]);
108✔
509
        else if (STARTS_WITH_CI(papszTokens[0], "POSITION_YMIN"))
985✔
510
            dYmin = CPLAtofM(papszTokens[1]);
108✔
511
        else if (STARTS_WITH_CI(papszTokens[0], "CELLSIZE"))
877✔
512
            dCellsize = CPLAtofM(papszTokens[1]);
108✔
513
        else if (STARTS_WITH_CI(papszTokens[0], "NODATA_VALUE"))
769✔
514
            dNoData = CPLAtofM(papszTokens[1]);
108✔
515
        else if (STARTS_WITH_CI(papszTokens[0], "DATAFORMAT"))
661✔
516
            strncpy(szDataFormat, papszTokens[1], sizeof(szDataFormat) - 1);
108✔
517
        else if (STARTS_WITH_CI(papszTokens[0], "BYTEORDER_BIG"))
553✔
518
            strncpy(szByteOrderBig, papszTokens[1], sizeof(szByteOrderBig) - 1);
109✔
519
        else if (STARTS_WITH_CI(papszTokens[0], "TOPTOBOTTOM"))
444✔
520
            strncpy(szTopToBottom, papszTokens[1], sizeof(szTopToBottom) - 1);
108✔
521
        else if (STARTS_WITH_CI(papszTokens[0], "Z_FACTOR"))
336✔
522
            dZFactor = CPLAtofM(papszTokens[1]);
108✔
523

524
        CSLDestroy(papszTokens);
1,309✔
525
        CSLDestroy(papszHDR);
1,309✔
526
    }
527

528
    VSIFCloseL(fp);
118✔
529

530
    /* -------------------------------------------------------------------- */
531
    /*      Did we get the required keywords?  If not we return with        */
532
    /*      this never having been considered to be a match. This isn't     */
533
    /*      an error!                                                       */
534
    /* -------------------------------------------------------------------- */
535
    if (nRows == -1 || nCols == -1)
118✔
536
    {
537
        return nullptr;
10✔
538
    }
539

540
    if (!GDALCheckDatasetDimensions(nCols, nRows))
108✔
541
    {
542
        return nullptr;
×
543
    }
544

545
    if (STARTS_WITH_CI(szTopToBottom, "TRUE"))
108✔
546
    {
547
        CPLError(CE_Failure, CPLE_AppDefined,
×
548
                 "Currently the SAGA Binary Grid driver does not support\n"
549
                 "SAGA grids written TOPTOBOTTOM.\n");
550
        return nullptr;
×
551
    }
552
    if (dZFactor != 1.0)
108✔
553
    {
554
        CPLError(CE_Warning, CPLE_AppDefined,
×
555
                 "Currently the SAGA Binary Grid driver does not support\n"
556
                 "ZFACTORs other than 1.\n");
557
    }
558

559
    /* -------------------------------------------------------------------- */
560
    /*      Create a corresponding GDALDataset.                             */
561
    /* -------------------------------------------------------------------- */
562
    SAGADataset *poDS = new SAGADataset();
108✔
563

564
    poDS->eAccess = poOpenInfo->eAccess;
108✔
565
    if (poOpenInfo->eAccess == GA_ReadOnly)
108✔
566
        poDS->fp = VSIFOpenL(osFullname.c_str(), "rb");
68✔
567
    else
568
        poDS->fp = VSIFOpenL(osFullname.c_str(), "r+b");
40✔
569

570
    if (poDS->fp == nullptr)
108✔
571
    {
572
        delete poDS;
×
573
        CPLError(CE_Failure, CPLE_OpenFailed,
×
574
                 "VSIFOpenL(%s) failed unexpectedly.", osFullname.c_str());
575
        return nullptr;
×
576
    }
577

578
    poDS->nRasterXSize = nCols;
108✔
579
    poDS->nRasterYSize = nRows;
108✔
580

581
    SAGARasterBand *poBand = new SAGARasterBand(poDS, 1);
108✔
582

583
    /* -------------------------------------------------------------------- */
584
    /*      Figure out the byte order.                                      */
585
    /* -------------------------------------------------------------------- */
586
    if (STARTS_WITH_CI(szByteOrderBig, "TRUE"))
108✔
587
        poBand->m_ByteOrder = 1;
×
588
    else if (STARTS_WITH_CI(szByteOrderBig, "FALSE"))
108✔
589
        poBand->m_ByteOrder = 0;
108✔
590

591
    /* -------------------------------------------------------------------- */
592
    /*      Figure out the data type.                                       */
593
    /* -------------------------------------------------------------------- */
594
    if (EQUAL(szDataFormat, "BIT"))
108✔
595
    {
596
        poBand->SetDataType(GDT_Byte);
×
597
        poBand->m_nBits = 8;
×
598
    }
599
    else if (EQUAL(szDataFormat, "BYTE_UNSIGNED"))
108✔
600
    {
601
        poBand->SetDataType(GDT_Byte);
13✔
602
        poBand->m_nBits = 8;
13✔
603
    }
604
    else if (EQUAL(szDataFormat, "BYTE"))
95✔
605
    {
606
        poBand->SetDataType(GDT_Byte);
×
607
        poBand->m_nBits = 8;
×
608
    }
609
    else if (EQUAL(szDataFormat, "SHORTINT_UNSIGNED"))
95✔
610
    {
611
        poBand->SetDataType(GDT_UInt16);
13✔
612
        poBand->m_nBits = 16;
13✔
613
    }
614
    else if (EQUAL(szDataFormat, "SHORTINT"))
82✔
615
    {
616
        poBand->SetDataType(GDT_Int16);
13✔
617
        poBand->m_nBits = 16;
13✔
618
    }
619
    else if (EQUAL(szDataFormat, "INTEGER_UNSIGNED"))
69✔
620
    {
621
        poBand->SetDataType(GDT_UInt32);
13✔
622
        poBand->m_nBits = 32;
13✔
623
    }
624
    else if (EQUAL(szDataFormat, "INTEGER"))
56✔
625
    {
626
        poBand->SetDataType(GDT_Int32);
13✔
627
        poBand->m_nBits = 32;
13✔
628
    }
629
    else if (EQUAL(szDataFormat, "FLOAT"))
43✔
630
    {
631
        poBand->SetDataType(GDT_Float32);
29✔
632
        poBand->m_nBits = 32;
29✔
633
    }
634
    else if (EQUAL(szDataFormat, "DOUBLE"))
14✔
635
    {
636
        poBand->SetDataType(GDT_Float64);
14✔
637
        poBand->m_nBits = 64;
14✔
638
    }
639
    else
640
    {
641
        CPLError(CE_Failure, CPLE_NotSupported,
×
642
                 "SAGA driver does not support the dataformat %s.",
643
                 szDataFormat);
644
        delete poBand;
×
645
        delete poDS;
×
646
        return nullptr;
×
647
    }
648

649
    /* -------------------------------------------------------------------- */
650
    /*      Save band information                                           */
651
    /* -------------------------------------------------------------------- */
652
    poBand->m_Xmin = dXmin;
108✔
653
    poBand->m_Ymin = dYmin;
108✔
654
    poBand->m_NoData = dNoData;
108✔
655
    poBand->m_Cellsize = dCellsize;
108✔
656

657
    poDS->SetBand(1, poBand);
108✔
658

659
    /* -------------------------------------------------------------------- */
660
    /*      Initialize any PAM information.                                 */
661
    /* -------------------------------------------------------------------- */
662
    poDS->SetDescription(poOpenInfo->pszFilename);
108✔
663
    poDS->TryLoadXML();
108✔
664

665
    /* -------------------------------------------------------------------- */
666
    /*      Check for a .prj file.                                          */
667
    /* -------------------------------------------------------------------- */
668
    const std::string osPrjFilename =
669
        CPLFormCIFilenameSafe(osPath, osName, "prj");
108✔
670

671
    fp = VSIFOpenL(osPrjFilename.c_str(), "r");
108✔
672

673
    if (fp != nullptr)
108✔
674
    {
675
        VSIFCloseL(fp);
32✔
676

677
        char **papszLines = CSLLoad(osPrjFilename.c_str());
32✔
678

679
        poDS->m_oSRS.importFromESRI(papszLines);
32✔
680

681
        CSLDestroy(papszLines);
32✔
682
    }
683

684
    /* -------------------------------------------------------------------- */
685
    /*      Check for external overviews.                                   */
686
    /* -------------------------------------------------------------------- */
687
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
216✔
688
                                poOpenInfo->GetSiblingFiles());
108✔
689

690
    return poDS;
108✔
691
}
692

693
/************************************************************************/
694
/*                          GetGeoTransform()                           */
695
/************************************************************************/
696

697
CPLErr SAGADataset::GetGeoTransform(GDALGeoTransform &gt) const
11✔
698
{
699
    const SAGARasterBand *poGRB =
700
        cpl::down_cast<const SAGARasterBand *>(GetRasterBand(1));
11✔
701

702
    if (poGRB == nullptr)
11✔
703
    {
704
        gt = GDALGeoTransform();
×
705
        return CE_Failure;
×
706
    }
707

708
    /* check if we have a PAM GeoTransform stored */
709
    CPLPushErrorHandler(CPLQuietErrorHandler);
11✔
710
    CPLErr eErr = GDALPamDataset::GetGeoTransform(gt);
11✔
711
    CPLPopErrorHandler();
11✔
712

713
    if (eErr == CE_None)
11✔
714
        return CE_None;
×
715

716
    gt[1] = poGRB->m_Cellsize;
11✔
717
    gt[5] = poGRB->m_Cellsize * -1.0;
11✔
718
    gt[0] = poGRB->m_Xmin - poGRB->m_Cellsize / 2;
11✔
719
    gt[3] = poGRB->m_Ymin + (nRasterYSize - 1) * poGRB->m_Cellsize +
22✔
720
            poGRB->m_Cellsize / 2;
11✔
721

722
    /* tilt/rotation is not supported by SAGA grids */
723
    gt[4] = 0.0;
11✔
724
    gt[2] = 0.0;
11✔
725

726
    return CE_None;
11✔
727
}
728

729
/************************************************************************/
730
/*                          SetGeoTransform()                           */
731
/************************************************************************/
732

733
CPLErr SAGADataset::SetGeoTransform(const GDALGeoTransform &gt)
23✔
734
{
735

736
    if (eAccess == GA_ReadOnly)
23✔
737
    {
738
        CPLError(CE_Failure, CPLE_NoWriteAccess,
×
739
                 "Unable to set GeoTransform, dataset opened read only.\n");
740
        return CE_Failure;
×
741
    }
742

743
    SAGARasterBand *poGRB = static_cast<SAGARasterBand *>(GetRasterBand(1));
23✔
744

745
    if (poGRB == nullptr)
23✔
746
        return CE_Failure;
×
747

748
    if (gt[1] != gt[5] * -1.0)
23✔
749
    {
750
        CPLError(CE_Failure, CPLE_NotSupported,
×
751
                 "Unable to set GeoTransform, SAGA binary grids only support "
752
                 "the same cellsize in x-y.\n");
753
        return CE_Failure;
×
754
    }
755

756
    const double dfMinX = gt[0] + gt[1] / 2;
23✔
757
    const double dfMinY = gt[5] * (nRasterYSize - 0.5) + gt[3];
23✔
758

759
    poGRB->m_Xmin = dfMinX;
23✔
760
    poGRB->m_Ymin = dfMinY;
23✔
761
    poGRB->m_Cellsize = gt[1];
23✔
762
    headerDirty = true;
23✔
763

764
    return CE_None;
23✔
765
}
766

767
/************************************************************************/
768
/*                             WriteHeader()                            */
769
/************************************************************************/
770

771
CPLErr SAGADataset::WriteHeader(const CPLString &osHDRFilename,
73✔
772
                                GDALDataType eType, int nXSize, int nYSize,
773
                                double dfMinX, double dfMinY, double dfCellsize,
774
                                double dfNoData, double dfZFactor,
775
                                bool bTopToBottom)
776

777
{
778
    VSILFILE *fp = VSIFOpenL(osHDRFilename, "wt");
73✔
779

780
    if (fp == nullptr)
73✔
781
    {
782
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to write .sgrd file %s.",
×
783
                 osHDRFilename.c_str());
784
        return CE_Failure;
×
785
    }
786

787
    VSIFPrintfL(fp, "NAME\t= %s\n", CPLGetBasenameSafe(osHDRFilename).c_str());
73✔
788
    VSIFPrintfL(fp, "DESCRIPTION\t=\n");
73✔
789
    VSIFPrintfL(fp, "UNIT\t=\n");
73✔
790
    VSIFPrintfL(fp, "DATAFILE_OFFSET\t= 0\n");
73✔
791

792
    if (eType == GDT_Int32)
73✔
793
        VSIFPrintfL(fp, "DATAFORMAT\t= INTEGER\n");
8✔
794
    else if (eType == GDT_UInt32)
65✔
795
        VSIFPrintfL(fp, "DATAFORMAT\t= INTEGER_UNSIGNED\n");
8✔
796
    else if (eType == GDT_Int16)
57✔
797
        VSIFPrintfL(fp, "DATAFORMAT\t= SHORTINT\n");
8✔
798
    else if (eType == GDT_UInt16)
49✔
799
        VSIFPrintfL(fp, "DATAFORMAT\t= SHORTINT_UNSIGNED\n");
8✔
800
    else if (eType == GDT_Byte)
41✔
801
        VSIFPrintfL(fp, "DATAFORMAT\t= BYTE_UNSIGNED\n");
18✔
802
    else if (eType == GDT_Float32)
23✔
803
        VSIFPrintfL(fp, "DATAFORMAT\t= FLOAT\n");
13✔
804
    else  // if( eType == GDT_Float64 )
805
        VSIFPrintfL(fp, "DATAFORMAT\t= DOUBLE\n");
10✔
806
#ifdef CPL_LSB
807
    VSIFPrintfL(fp, "BYTEORDER_BIG\t= FALSE\n");
73✔
808
#else
809
    VSIFPrintfL(fp, "BYTEORDER_BIG\t= TRUE\n");
810
#endif
811

812
    VSIFPrintfL(fp, "POSITION_XMIN\t= %.10f\n", dfMinX);
73✔
813
    VSIFPrintfL(fp, "POSITION_YMIN\t= %.10f\n", dfMinY);
73✔
814
    VSIFPrintfL(fp, "CELLCOUNT_X\t= %d\n", nXSize);
73✔
815
    VSIFPrintfL(fp, "CELLCOUNT_Y\t= %d\n", nYSize);
73✔
816
    VSIFPrintfL(fp, "CELLSIZE\t= %.10f\n", dfCellsize);
73✔
817
    VSIFPrintfL(fp, "Z_FACTOR\t= %f\n", dfZFactor);
73✔
818
    VSIFPrintfL(fp, "NODATA_VALUE\t= %f\n", dfNoData);
73✔
819
    if (bTopToBottom)
73✔
820
        VSIFPrintfL(fp, "TOPTOBOTTOM\t= TRUE\n");
×
821
    else
822
        VSIFPrintfL(fp, "TOPTOBOTTOM\t= FALSE\n");
73✔
823

824
    VSIFCloseL(fp);
73✔
825

826
    return CE_None;
73✔
827
}
828

829
/************************************************************************/
830
/*                               Create()                               */
831
/************************************************************************/
832

833
GDALDataset *SAGADataset::Create(const char *pszFilename, int nXSize,
81✔
834
                                 int nYSize, int nBandsIn, GDALDataType eType,
835
                                 char **papszParamList)
836

837
{
838
    if (nXSize <= 0 || nYSize <= 0)
81✔
839
    {
840
        CPLError(CE_Failure, CPLE_IllegalArg,
×
841
                 "Unable to create grid, both X and Y size must be "
842
                 "non-negative.\n");
843

844
        return nullptr;
×
845
    }
846

847
    if (nBandsIn != 1)
81✔
848
    {
849
        CPLError(CE_Failure, CPLE_IllegalArg,
18✔
850
                 "SAGA Binary Grid only supports 1 band");
851
        return nullptr;
18✔
852
    }
853

854
    if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 &&
63✔
855
        eType != GDT_UInt32 && eType != GDT_Int32 && eType != GDT_Float32 &&
30✔
856
        eType != GDT_Float64)
857
    {
858
        CPLError(CE_Failure, CPLE_AppDefined,
11✔
859
                 "SAGA Binary Grid only supports Byte, UInt16, Int16, "
860
                 "UInt32, Int32, Float32 and Float64 datatypes.  Unable to "
861
                 "create with type %s.\n",
862
                 GDALGetDataTypeName(eType));
863

864
        return nullptr;
11✔
865
    }
866

867
    VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
52✔
868

869
    if (fp == nullptr)
52✔
870
    {
871
        CPLError(CE_Failure, CPLE_OpenFailed,
3✔
872
                 "Attempt to create file '%s' failed.\n", pszFilename);
873
        return nullptr;
3✔
874
    }
875

876
    double dfNoDataVal = 0.0;
49✔
877

878
    const char *pszNoDataValue =
879
        CSLFetchNameValue(papszParamList, "NODATA_VALUE");
49✔
880
    if (pszNoDataValue)
49✔
881
    {
882
        dfNoDataVal = CPLAtofM(pszNoDataValue);
2✔
883
    }
884
    else
885
    {
886
        switch (eType) /* GDT_Byte, GDT_UInt16, GDT_Int16, GDT_UInt32  */
47✔
887
        {              /* GDT_Int32, GDT_Float32, GDT_Float64 */
888
            case (GDT_Byte):
15✔
889
            {
890
                dfNoDataVal = SG_NODATA_GDT_Byte;
15✔
891
                break;
15✔
892
            }
893
            case (GDT_UInt16):
5✔
894
            {
895
                dfNoDataVal = SG_NODATA_GDT_UInt16;
5✔
896
                break;
5✔
897
            }
898
            case (GDT_Int16):
5✔
899
            {
900
                dfNoDataVal = SG_NODATA_GDT_Int16;
5✔
901
                break;
5✔
902
            }
903
            case (GDT_UInt32):
5✔
904
            {
905
                dfNoDataVal = SG_NODATA_GDT_UInt32;
5✔
906
                break;
5✔
907
            }
908
            case (GDT_Int32):
5✔
909
            {
910
                dfNoDataVal = SG_NODATA_GDT_Int32;
5✔
911
                break;
5✔
912
            }
913
            default:
6✔
914
            case (GDT_Float32):
915
            {
916
                dfNoDataVal = SG_NODATA_GDT_Float32;
6✔
917
                break;
6✔
918
            }
919
            case (GDT_Float64):
6✔
920
            {
921
                dfNoDataVal = SG_NODATA_GDT_Float64;
6✔
922
                break;
6✔
923
            }
924
        }
925
    }
926

927
    double dfNoDataForAlignment;
928
    void *abyNoData = &dfNoDataForAlignment;
49✔
929
    GDALCopyWords(&dfNoDataVal, GDT_Float64, 0, abyNoData, eType, 0, 1);
49✔
930

931
    const CPLString osHdrFilename = CPLResetExtensionSafe(pszFilename, "sgrd");
98✔
932
    CPLErr eErr = WriteHeader(osHdrFilename, eType, nXSize, nYSize, 0.0, 0.0,
49✔
933
                              1.0, dfNoDataVal, 1.0, false);
934

935
    if (eErr != CE_None)
49✔
936
    {
937
        VSIFCloseL(fp);
×
938
        return nullptr;
×
939
    }
940

941
    if (CPLFetchBool(papszParamList, "FILL_NODATA", true))
49✔
942
    {
943
        const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
23✔
944
        GByte *pabyNoDataBuf =
945
            reinterpret_cast<GByte *>(VSIMalloc2(nDataTypeSize, nXSize));
23✔
946
        if (pabyNoDataBuf == nullptr)
23✔
947
        {
948
            VSIFCloseL(fp);
×
949
            return nullptr;
×
950
        }
951

952
        for (int iCol = 0; iCol < nXSize; iCol++)
889✔
953
        {
954
            memcpy(pabyNoDataBuf + static_cast<size_t>(iCol) * nDataTypeSize,
866✔
955
                   abyNoData, nDataTypeSize);
956
        }
957

958
        for (int iRow = 0; iRow < nYSize; iRow++)
889✔
959
        {
960
            if (VSIFWriteL(pabyNoDataBuf, nDataTypeSize, nXSize, fp) !=
866✔
961
                static_cast<unsigned>(nXSize))
866✔
962
            {
963
                VSIFCloseL(fp);
×
964
                VSIFree(pabyNoDataBuf);
×
965
                CPLError(CE_Failure, CPLE_FileIO,
×
966
                         "Unable to write grid cell.  Disk full?\n");
967
                return nullptr;
×
968
            }
969
        }
970

971
        VSIFree(pabyNoDataBuf);
23✔
972
    }
973

974
    VSIFCloseL(fp);
49✔
975

976
    return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
49✔
977
}
978

979
/************************************************************************/
980
/*                             CreateCopy()                             */
981
/************************************************************************/
982

983
GDALDataset *SAGADataset::CreateCopy(const char *pszFilename,
38✔
984
                                     GDALDataset *poSrcDS, int bStrict,
985
                                     CPL_UNUSED char **papszOptions,
986
                                     GDALProgressFunc pfnProgress,
987
                                     void *pProgressData)
988
{
989
    if (pfnProgress == nullptr)
38✔
990
        pfnProgress = GDALDummyProgress;
×
991

992
    int nBands = poSrcDS->GetRasterCount();
38✔
993
    if (nBands == 0)
38✔
994
    {
995
        CPLError(
1✔
996
            CE_Failure, CPLE_NotSupported,
997
            "SAGA driver does not support source dataset with zero band.\n");
998
        return nullptr;
1✔
999
    }
1000
    else if (nBands > 1)
37✔
1001
    {
1002
        if (bStrict)
4✔
1003
        {
1004
            CPLError(CE_Failure, CPLE_NotSupported,
4✔
1005
                     "Unable to create copy, SAGA Binary Grid "
1006
                     "format only supports one raster band.\n");
1007
            return nullptr;
4✔
1008
        }
1009
        else
1010
            CPLError(CE_Warning, CPLE_NotSupported,
×
1011
                     "SAGA Binary Grid format only supports one "
1012
                     "raster band, first band will be copied.\n");
1013
    }
1014

1015
    GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
33✔
1016

1017
    char **papszCreateOptions = CSLSetNameValue(nullptr, "FILL_NODATA", "NO");
33✔
1018

1019
    int bHasNoDataValue = FALSE;
33✔
1020
    const double dfNoDataValue = poSrcBand->GetNoDataValue(&bHasNoDataValue);
33✔
1021
    if (bHasNoDataValue)
33✔
1022
        papszCreateOptions =
1023
            CSLSetNameValue(papszCreateOptions, "NODATA_VALUE",
2✔
1024
                            CPLSPrintf("%.16g", dfNoDataValue));
1025

1026
    GDALDataset *poDstDS =
1027
        Create(pszFilename, poSrcBand->GetXSize(), poSrcBand->GetYSize(), 1,
33✔
1028
               poSrcBand->GetRasterDataType(), papszCreateOptions);
1029
    CSLDestroy(papszCreateOptions);
33✔
1030

1031
    if (poDstDS == nullptr)
33✔
1032
        return nullptr;
17✔
1033

1034
    /* -------------------------------------------------------------------- */
1035
    /*      Copy band data.                                                 */
1036
    /* -------------------------------------------------------------------- */
1037

1038
    CPLErr eErr =
1039
        GDALDatasetCopyWholeRaster((GDALDatasetH)poSrcDS, (GDALDatasetH)poDstDS,
16✔
1040
                                   nullptr, pfnProgress, pProgressData);
1041

1042
    if (eErr == CE_Failure)
16✔
1043
    {
1044
        delete poDstDS;
×
1045
        return nullptr;
×
1046
    }
1047

1048
    GDALGeoTransform gt;
16✔
1049
    if (poSrcDS->GetGeoTransform(gt) == CE_None)
16✔
1050
        poDstDS->SetGeoTransform(gt);
16✔
1051

1052
    poDstDS->SetProjection(poSrcDS->GetProjectionRef());
16✔
1053

1054
    return poDstDS;
16✔
1055
}
1056

1057
/************************************************************************/
1058
/*                          GDALRegister_SAGA()                         */
1059
/************************************************************************/
1060

1061
void GDALRegister_SAGA()
1,911✔
1062

1063
{
1064
    if (GDALGetDriverByName("SAGA") != nullptr)
1,911✔
1065
        return;
282✔
1066

1067
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1068

1069
    poDriver->SetDescription("SAGA");
1,629✔
1070
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1071
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1,629✔
1072
                              "SAGA GIS Binary Grid (.sdat, .sg-grd-z)");
1,629✔
1073
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/sdat.html");
1,629✔
1074
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "sdat sg-grd-z");
1,629✔
1075
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1,629✔
1076
                              "Byte Int16 "
1077
                              "UInt16 Int32 UInt32 Float32 Float64");
1,629✔
1078

1079
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1080

1081
    poDriver->pfnOpen = SAGADataset::Open;
1,629✔
1082
    poDriver->pfnCreate = SAGADataset::Create;
1,629✔
1083
    poDriver->pfnCreateCopy = SAGADataset::CreateCopy;
1,629✔
1084

1085
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1086
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc