Coverage Report

Created: 2026-11-12 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gsg/gsbgdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Implements the Golden Software Binary Grid Format.
5
 * Author:   Kevin Locke, kwl7@cornell.edu
6
 *           (Based largely on aaigriddataset.cpp by Frank Warmerdam)
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2006, Kevin Locke <kwl7@cornell.edu>
10
 * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "cpl_conv.h"
16
17
#include <assert.h>
18
#include <float.h>
19
#include <limits.h>
20
#include <limits>
21
22
#include "gdal_frmts.h"
23
#include "gdal_pam.h"
24
#include "gdal_driver.h"
25
#include "gdal_drivermanager.h"
26
#include "gdal_openinfo.h"
27
#include "gdal_cpp_functions.h"
28
29
/************************************************************************/
30
/* ==================================================================== */
31
/*                              GSBGDataset                             */
32
/* ==================================================================== */
33
/************************************************************************/
34
35
class GSBGRasterBand;
36
37
class GSBGDataset final : public GDALPamDataset
38
{
39
    friend class GSBGRasterBand;
40
41
    static const float fNODATA_VALUE;
42
    static const size_t nHEADER_SIZE;
43
44
    static CPLErr WriteHeader(VSILFILE *fp, int nXSize, int nYSize,
45
                              double dfMinX, double dfMaxX, double dfMinY,
46
                              double dfMaxY, double dfMinZ, double dfMaxZ);
47
48
    VSILFILE *fp = nullptr;
49
50
  public:
51
56
    GSBGDataset() = default;
52
53
    ~GSBGDataset() override;
54
55
    static int Identify(GDALOpenInfo *);
56
    static GDALDataset *Open(GDALOpenInfo *);
57
    static GDALPamDataset *OpenPAM(GDALOpenInfo *);
58
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
59
                               int nBands, GDALDataType eType, CSLConstList);
60
    static GDALDataset *CreateCopy(const char *pszFilename,
61
                                   GDALDataset *poSrcDS, int bStrict,
62
                                   CSLConstList papszOptions,
63
                                   GDALProgressFunc pfnProgress,
64
                                   void *pProgressData);
65
66
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
67
    CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
68
};
69
70
/* NOTE:  This is not mentioned in the spec, but Surfer 8 uses this value */
71
/* 0x7effffee (Little Endian: eeffff7e) */
72
const float GSBGDataset::fNODATA_VALUE = 1.701410009187828e+38f;
73
74
const size_t GSBGDataset::nHEADER_SIZE = 56;
75
76
/************************************************************************/
77
/* ==================================================================== */
78
/*                            GSBGRasterBand                            */
79
/* ==================================================================== */
80
/************************************************************************/
81
82
class GSBGRasterBand final : public GDALPamRasterBand
83
{
84
    friend class GSBGDataset;
85
86
    double dfMinX;
87
    double dfMaxX;
88
    double dfMinY;
89
    double dfMaxY;
90
    double dfMinZ;
91
    double dfMaxZ;
92
93
    float *pafRowMinZ;
94
    float *pafRowMaxZ;
95
    int nMinZRow;
96
    int nMaxZRow;
97
98
    CPLErr ScanForMinMaxZ();
99
100
  public:
101
    GSBGRasterBand(GSBGDataset *, int);
102
    ~GSBGRasterBand() override;
103
104
    CPLErr IReadBlock(int, int, void *) override;
105
    CPLErr IWriteBlock(int, int, void *) override;
106
107
    double GetNoDataValue(int *pbSuccess = nullptr) override;
108
    double GetMinimum(int *pbSuccess = nullptr) override;
109
    double GetMaximum(int *pbSuccess = nullptr) override;
110
};
111
112
/************************************************************************/
113
/*                           GSBGRasterBand()                           */
114
/************************************************************************/
115
116
GSBGRasterBand::GSBGRasterBand(GSBGDataset *poDSIn, int nBandIn)
117
32
    : dfMinX(0.0), dfMaxX(0.0), dfMinY(0.0), dfMaxY(0.0), dfMinZ(0.0),
118
32
      dfMaxZ(0.0), pafRowMinZ(nullptr), pafRowMaxZ(nullptr), nMinZRow(-1),
119
32
      nMaxZRow(-1)
120
32
{
121
32
    this->poDS = poDSIn;
122
32
    this->nBand = nBandIn;
123
124
32
    eDataType = GDT_Float32;
125
126
32
    nBlockXSize = poDS->GetRasterXSize();
127
32
    nBlockYSize = 1;
128
32
}
129
130
/************************************************************************/
131
/*                          ~GSBGRasterBand()                           */
132
/************************************************************************/
133
134
GSBGRasterBand::~GSBGRasterBand()
135
136
32
{
137
32
    if (pafRowMinZ != nullptr)
138
0
        CPLFree(pafRowMinZ);
139
32
    if (pafRowMaxZ != nullptr)
140
0
        CPLFree(pafRowMaxZ);
141
32
}
142
143
/************************************************************************/
144
/*                           ScanForMinMaxZ()                           */
145
/************************************************************************/
146
147
CPLErr GSBGRasterBand::ScanForMinMaxZ()
148
149
0
{
150
0
    float *pafRowVals = (float *)VSI_MALLOC2_VERBOSE(nRasterXSize, 4);
151
152
0
    if (pafRowVals == nullptr)
153
0
    {
154
0
        return CE_Failure;
155
0
    }
156
157
0
    double dfNewMinZ = std::numeric_limits<double>::max();
158
0
    double dfNewMaxZ = std::numeric_limits<double>::lowest();
159
0
    int nNewMinZRow = 0;
160
0
    int nNewMaxZRow = 0;
161
162
    /* Since we have to scan, lets calc. statistics too */
163
0
    double dfSum = 0.0;
164
0
    double dfSum2 = 0.0;
165
0
    unsigned long nValuesRead = 0;
166
0
    for (int iRow = 0; iRow < nRasterYSize; iRow++)
167
0
    {
168
0
        CPLErr eErr = IReadBlock(0, iRow, pafRowVals);
169
0
        if (eErr != CE_None)
170
0
        {
171
0
            VSIFree(pafRowVals);
172
0
            return CE_Failure;
173
0
        }
174
175
0
        pafRowMinZ[iRow] = std::numeric_limits<float>::max();
176
0
        pafRowMaxZ[iRow] = std::numeric_limits<float>::lowest();
177
0
        for (int iCol = 0; iCol < nRasterXSize; iCol++)
178
0
        {
179
0
            if (pafRowVals[iCol] == GSBGDataset::fNODATA_VALUE)
180
0
                continue;
181
182
0
            if (pafRowVals[iCol] < pafRowMinZ[iRow])
183
0
                pafRowMinZ[iRow] = pafRowVals[iCol];
184
185
0
            if (pafRowVals[iCol] > pafRowMinZ[iRow])
186
0
                pafRowMaxZ[iRow] = pafRowVals[iCol];
187
188
0
            dfSum += pafRowVals[iCol];
189
0
            dfSum2 += static_cast<double>(pafRowVals[iCol]) * pafRowVals[iCol];
190
0
            nValuesRead++;
191
0
        }
192
193
0
        if (pafRowMinZ[iRow] < dfNewMinZ)
194
0
        {
195
0
            dfNewMinZ = pafRowMinZ[iRow];
196
0
            nNewMinZRow = iRow;
197
0
        }
198
199
0
        if (pafRowMaxZ[iRow] > dfNewMaxZ)
200
0
        {
201
0
            dfNewMaxZ = pafRowMaxZ[iRow];
202
0
            nNewMaxZRow = iRow;
203
0
        }
204
0
    }
205
206
0
    VSIFree(pafRowVals);
207
208
0
    if (nValuesRead == 0)
209
0
    {
210
0
        dfMinZ = 0.0;
211
0
        dfMaxZ = 0.0;
212
0
        nMinZRow = 0;
213
0
        nMaxZRow = 0;
214
0
        return CE_None;
215
0
    }
216
217
0
    dfMinZ = dfNewMinZ;
218
0
    dfMaxZ = dfNewMaxZ;
219
0
    nMinZRow = nNewMinZRow;
220
0
    nMaxZRow = nNewMaxZRow;
221
222
0
    double dfMean = dfSum / nValuesRead;
223
0
    double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean));
224
0
    SetStatistics(dfMinZ, dfMaxZ, dfMean, dfStdDev);
225
226
0
    return CE_None;
227
0
}
228
229
/************************************************************************/
230
/*                             IReadBlock()                             */
231
/************************************************************************/
232
233
CPLErr GSBGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
234
235
199
{
236
199
    if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
237
0
        return CE_Failure;
238
239
199
    GSBGDataset *poGDS = cpl::down_cast<GSBGDataset *>(poDS);
240
199
    if (VSIFSeekL(poGDS->fp,
241
199
                  GSBGDataset::nHEADER_SIZE +
242
199
                      4 * static_cast<vsi_l_offset>(nRasterXSize) *
243
199
                          (nRasterYSize - nBlockYOff - 1),
244
199
                  SEEK_SET) != 0)
245
0
    {
246
0
        CPLError(CE_Failure, CPLE_FileIO,
247
0
                 "Unable to seek to beginning of grid row.");
248
0
        return CE_Failure;
249
0
    }
250
251
199
    if (VSIFReadL(pImage, sizeof(float), nBlockXSize, poGDS->fp) !=
252
199
        static_cast<unsigned>(nBlockXSize))
253
199
    {
254
199
        CPLError(CE_Failure, CPLE_FileIO,
255
199
                 "Unable to read block from grid file.");
256
199
        return CE_Failure;
257
199
    }
258
259
#ifdef CPL_MSB
260
    float *pfImage = (float *)pImage;
261
    for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
262
    {
263
        CPL_LSBPTR32(pfImage + iPixel);
264
    }
265
#endif
266
267
0
    return CE_None;
268
199
}
269
270
/************************************************************************/
271
/*                            IWriteBlock()                             */
272
/************************************************************************/
273
274
CPLErr GSBGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
275
276
0
{
277
0
    if (eAccess == GA_ReadOnly)
278
0
    {
279
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
280
0
                 "Unable to write block, dataset opened read only.");
281
0
        return CE_Failure;
282
0
    }
283
284
0
    if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
285
0
        return CE_Failure;
286
287
0
    GSBGDataset *poGDS = cpl::down_cast<GSBGDataset *>(poDS);
288
289
0
    if (pafRowMinZ == nullptr || pafRowMaxZ == nullptr || nMinZRow < 0 ||
290
0
        nMaxZRow < 0)
291
0
    {
292
0
        pafRowMinZ = (float *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(float));
293
0
        if (pafRowMinZ == nullptr)
294
0
        {
295
0
            return CE_Failure;
296
0
        }
297
298
0
        pafRowMaxZ = (float *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(float));
299
0
        if (pafRowMaxZ == nullptr)
300
0
        {
301
0
            VSIFree(pafRowMinZ);
302
0
            pafRowMinZ = nullptr;
303
0
            return CE_Failure;
304
0
        }
305
306
0
        CPLErr eErr = ScanForMinMaxZ();
307
0
        if (eErr != CE_None)
308
0
            return eErr;
309
0
    }
310
311
0
    if (VSIFSeekL(poGDS->fp,
312
0
                  GSBGDataset::nHEADER_SIZE +
313
0
                      static_cast<vsi_l_offset>(4) * nRasterXSize *
314
0
                          (nRasterYSize - nBlockYOff - 1),
315
0
                  SEEK_SET) != 0)
316
0
    {
317
0
        CPLError(CE_Failure, CPLE_FileIO,
318
0
                 "Unable to seek to beginning of grid row.");
319
0
        return CE_Failure;
320
0
    }
321
322
0
    float *pfImage = (float *)pImage;
323
0
    pafRowMinZ[nBlockYOff] = std::numeric_limits<float>::max();
324
0
    pafRowMaxZ[nBlockYOff] = std::numeric_limits<float>::lowest();
325
0
    for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
326
0
    {
327
0
        if (pfImage[iPixel] != GSBGDataset::fNODATA_VALUE)
328
0
        {
329
0
            if (pfImage[iPixel] < pafRowMinZ[nBlockYOff])
330
0
                pafRowMinZ[nBlockYOff] = pfImage[iPixel];
331
332
0
            if (pfImage[iPixel] > pafRowMaxZ[nBlockYOff])
333
0
                pafRowMaxZ[nBlockYOff] = pfImage[iPixel];
334
0
        }
335
336
0
        CPL_LSBPTR32(pfImage + iPixel);
337
0
    }
338
339
0
    if (VSIFWriteL(pImage, sizeof(float), nBlockXSize, poGDS->fp) !=
340
0
        static_cast<unsigned>(nBlockXSize))
341
0
    {
342
0
        CPLError(CE_Failure, CPLE_FileIO,
343
0
                 "Unable to write block to grid file.");
344
0
        return CE_Failure;
345
0
    }
346
347
    /* Update min/max Z values as appropriate */
348
0
    bool bHeaderNeedsUpdate = false;
349
0
    if (nMinZRow == nBlockYOff && pafRowMinZ[nBlockYOff] > dfMinZ)
350
0
    {
351
0
        double dfNewMinZ = std::numeric_limits<double>::max();
352
0
        for (int iRow = 0; iRow < nRasterYSize; iRow++)
353
0
        {
354
0
            if (pafRowMinZ[iRow] < dfNewMinZ)
355
0
            {
356
0
                dfNewMinZ = pafRowMinZ[iRow];
357
0
                nMinZRow = iRow;
358
0
            }
359
0
        }
360
361
0
        if (dfNewMinZ != dfMinZ)
362
0
        {
363
0
            dfMinZ = dfNewMinZ;
364
0
            bHeaderNeedsUpdate = true;
365
0
        }
366
0
    }
367
368
0
    if (nMaxZRow == nBlockYOff && pafRowMaxZ[nBlockYOff] < dfMaxZ)
369
0
    {
370
0
        double dfNewMaxZ = std::numeric_limits<double>::lowest();
371
0
        for (int iRow = 0; iRow < nRasterYSize; iRow++)
372
0
        {
373
0
            if (pafRowMaxZ[iRow] > dfNewMaxZ)
374
0
            {
375
0
                dfNewMaxZ = pafRowMaxZ[iRow];
376
0
                nMaxZRow = iRow;
377
0
            }
378
0
        }
379
380
0
        if (dfNewMaxZ != dfMaxZ)
381
0
        {
382
0
            dfMaxZ = dfNewMaxZ;
383
0
            bHeaderNeedsUpdate = true;
384
0
        }
385
0
    }
386
387
0
    if (pafRowMinZ[nBlockYOff] < dfMinZ || pafRowMaxZ[nBlockYOff] > dfMaxZ)
388
0
    {
389
0
        if (pafRowMinZ[nBlockYOff] < dfMinZ)
390
0
        {
391
0
            dfMinZ = pafRowMinZ[nBlockYOff];
392
0
            nMinZRow = nBlockYOff;
393
0
        }
394
395
0
        if (pafRowMaxZ[nBlockYOff] > dfMaxZ)
396
0
        {
397
0
            dfMaxZ = pafRowMaxZ[nBlockYOff];
398
0
            nMaxZRow = nBlockYOff;
399
0
        }
400
401
0
        bHeaderNeedsUpdate = true;
402
0
    }
403
404
0
    if (bHeaderNeedsUpdate && dfMaxZ > dfMinZ)
405
0
    {
406
0
        CPLErr eErr =
407
0
            poGDS->WriteHeader(poGDS->fp, nRasterXSize, nRasterYSize, dfMinX,
408
0
                               dfMaxX, dfMinY, dfMaxY, dfMinZ, dfMaxZ);
409
0
        return eErr;
410
0
    }
411
412
0
    return CE_None;
413
0
}
414
415
/************************************************************************/
416
/*                           GetNoDataValue()                           */
417
/************************************************************************/
418
419
double GSBGRasterBand::GetNoDataValue(int *pbSuccess)
420
417
{
421
417
    if (pbSuccess)
422
411
        *pbSuccess = TRUE;
423
424
417
    return GSBGDataset::fNODATA_VALUE;
425
417
}
426
427
/************************************************************************/
428
/*                             GetMinimum()                             */
429
/************************************************************************/
430
431
double GSBGRasterBand::GetMinimum(int *pbSuccess)
432
10
{
433
10
    if (pbSuccess)
434
10
        *pbSuccess = TRUE;
435
436
10
    return dfMinZ;
437
10
}
438
439
/************************************************************************/
440
/*                             GetMaximum()                             */
441
/************************************************************************/
442
443
double GSBGRasterBand::GetMaximum(int *pbSuccess)
444
10
{
445
10
    if (pbSuccess)
446
10
        *pbSuccess = TRUE;
447
448
10
    return dfMaxZ;
449
10
}
450
451
/************************************************************************/
452
/* ==================================================================== */
453
/*                              GSBGDataset                             */
454
/* ==================================================================== */
455
/************************************************************************/
456
457
GSBGDataset::~GSBGDataset()
458
459
56
{
460
56
    FlushCache(true);
461
56
    if (fp != nullptr)
462
56
        VSIFCloseL(fp);
463
56
}
464
465
/************************************************************************/
466
/*                              Identify()                              */
467
/************************************************************************/
468
469
int GSBGDataset::Identify(GDALOpenInfo *poOpenInfo)
470
471
524k
{
472
    /* Check for signature */
473
524k
    if (poOpenInfo->nHeaderBytes < 4 ||
474
110k
        !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "DSBB"))
475
524k
    {
476
524k
        return FALSE;
477
524k
    }
478
479
112
    return TRUE;
480
524k
}
481
482
/************************************************************************/
483
/*                                Open()                                */
484
/************************************************************************/
485
486
GDALDataset *GSBGDataset::Open(GDALOpenInfo *poOpenInfo)
487
56
{
488
56
    return OpenPAM(poOpenInfo);
489
56
}
490
491
GDALPamDataset *GSBGDataset::OpenPAM(GDALOpenInfo *poOpenInfo)
492
493
56
{
494
56
    if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
495
0
    {
496
0
        return nullptr;
497
0
    }
498
499
    /* -------------------------------------------------------------------- */
500
    /*      Create a corresponding GDALDataset.                             */
501
    /* -------------------------------------------------------------------- */
502
56
    auto poDS = std::make_unique<GSBGDataset>();
503
504
56
    poDS->eAccess = poOpenInfo->eAccess;
505
56
    poDS->fp = poOpenInfo->fpL;
506
56
    poOpenInfo->fpL = nullptr;
507
508
    /* -------------------------------------------------------------------- */
509
    /*      Read the header.                                                */
510
    /* -------------------------------------------------------------------- */
511
56
    if (VSIFSeekL(poDS->fp, 4, SEEK_SET) != 0)
512
0
    {
513
0
        CPLError(CE_Failure, CPLE_FileIO,
514
0
                 "Unable to seek to start of grid file header.");
515
0
        return nullptr;
516
0
    }
517
518
    /* Parse number of X axis grid rows */
519
56
    GInt16 nTemp;
520
56
    if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
521
0
    {
522
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster X size.");
523
0
        return nullptr;
524
0
    }
525
56
    poDS->nRasterXSize = CPL_LSBWORD16(nTemp);
526
527
56
    if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
528
0
    {
529
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster Y size.");
530
0
        return nullptr;
531
0
    }
532
56
    poDS->nRasterYSize = CPL_LSBWORD16(nTemp);
533
534
56
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
535
24
    {
536
24
        return nullptr;
537
24
    }
538
539
    /* -------------------------------------------------------------------- */
540
    /*      Create band information objects.                                */
541
    /* -------------------------------------------------------------------- */
542
32
    GSBGRasterBand *poBand = new GSBGRasterBand(poDS.get(), 1);
543
32
    poDS->SetBand(1, poBand);
544
545
32
    double dfTemp;
546
32
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
547
1
    {
548
1
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.");
549
1
        return nullptr;
550
1
    }
551
31
    CPL_LSBPTR64(&dfTemp);
552
31
    poBand->dfMinX = dfTemp;
553
554
31
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
555
1
    {
556
1
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum X value.");
557
1
        return nullptr;
558
1
    }
559
30
    CPL_LSBPTR64(&dfTemp);
560
30
    poBand->dfMaxX = dfTemp;
561
562
30
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
563
2
    {
564
2
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Y value.");
565
2
        return nullptr;
566
2
    }
567
28
    CPL_LSBPTR64(&dfTemp);
568
28
    poBand->dfMinY = dfTemp;
569
570
28
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
571
0
    {
572
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Y value.");
573
0
        return nullptr;
574
0
    }
575
28
    CPL_LSBPTR64(&dfTemp);
576
28
    poBand->dfMaxY = dfTemp;
577
578
28
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
579
0
    {
580
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Z value.");
581
0
        return nullptr;
582
0
    }
583
28
    CPL_LSBPTR64(&dfTemp);
584
28
    poBand->dfMinZ = dfTemp;
585
586
28
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
587
0
    {
588
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Z value.");
589
0
        return nullptr;
590
0
    }
591
28
    CPL_LSBPTR64(&dfTemp);
592
28
    poBand->dfMaxZ = dfTemp;
593
594
    /* -------------------------------------------------------------------- */
595
    /*      Initialize any PAM information.                                 */
596
    /* -------------------------------------------------------------------- */
597
28
    poDS->SetDescription(poOpenInfo->pszFilename);
598
28
    poDS->TryLoadXML();
599
600
    /* -------------------------------------------------------------------- */
601
    /*      Check for external overviews.                                   */
602
    /* -------------------------------------------------------------------- */
603
28
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename,
604
28
                                poOpenInfo->GetSiblingFiles());
605
606
28
    return poDS.release();
607
28
}
608
609
/************************************************************************/
610
/*                          GetGeoTransform()                           */
611
/************************************************************************/
612
613
CPLErr GSBGDataset::GetGeoTransform(GDALGeoTransform &gt) const
614
27
{
615
27
    const GSBGRasterBand *poGRB =
616
27
        cpl::down_cast<const GSBGRasterBand *>(GetRasterBand(1));
617
618
    /* check if we have a PAM GeoTransform stored */
619
27
    CPLPushErrorHandler(CPLQuietErrorHandler);
620
27
    CPLErr eErr = GDALPamDataset::GetGeoTransform(gt);
621
27
    CPLPopErrorHandler();
622
623
27
    if (eErr == CE_None)
624
0
        return CE_None;
625
626
27
    if (nRasterXSize == 1 || nRasterYSize == 1)
627
0
        return CE_Failure;
628
629
    /* calculate pixel size first */
630
27
    gt.xscale = (poGRB->dfMaxX - poGRB->dfMinX) / (nRasterXSize - 1);
631
27
    gt.yscale = (poGRB->dfMinY - poGRB->dfMaxY) / (nRasterYSize - 1);
632
633
    /* then calculate image origin */
634
27
    gt.xorig = poGRB->dfMinX - gt.xscale / 2;
635
27
    gt.yorig = poGRB->dfMaxY - gt.yscale / 2;
636
637
    /* tilt/rotation does not supported by the GS grids */
638
27
    gt.yrot = 0.0;
639
27
    gt.xrot = 0.0;
640
641
27
    return CE_None;
642
27
}
643
644
/************************************************************************/
645
/*                          SetGeoTransform()                           */
646
/************************************************************************/
647
648
CPLErr GSBGDataset::SetGeoTransform(const GDALGeoTransform &gt)
649
0
{
650
0
    if (eAccess == GA_ReadOnly)
651
0
    {
652
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
653
0
                 "Unable to set GeoTransform, dataset opened read only.");
654
0
        return CE_Failure;
655
0
    }
656
657
0
    GSBGRasterBand *poGRB = cpl::down_cast<GSBGRasterBand *>(GetRasterBand(1));
658
659
    /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
660
    // CPLErr eErr = CE_None;
661
    /*if( gt.xrot != 0.0 || gt.yrot != 0.0
662
        || gt.xscale < 0.0 || gt.yscale < 0.0 )
663
        eErr = GDALPamDataset::SetGeoTransform( gt );
664
665
    if( eErr != CE_None )
666
        return eErr;*/
667
668
0
    double dfMinX = gt.xorig + gt.xscale / 2;
669
0
    double dfMaxX = gt.xscale * (nRasterXSize - 0.5) + gt.xorig;
670
0
    double dfMinY = gt.yscale * (nRasterYSize - 0.5) + gt.yorig;
671
0
    double dfMaxY = gt.yorig + gt.yscale / 2;
672
673
0
    CPLErr eErr =
674
0
        WriteHeader(fp, poGRB->nRasterXSize, poGRB->nRasterYSize, dfMinX,
675
0
                    dfMaxX, dfMinY, dfMaxY, poGRB->dfMinZ, poGRB->dfMaxZ);
676
677
0
    if (eErr == CE_None)
678
0
    {
679
0
        poGRB->dfMinX = dfMinX;
680
0
        poGRB->dfMaxX = dfMaxX;
681
0
        poGRB->dfMinY = dfMinY;
682
0
        poGRB->dfMaxY = dfMaxY;
683
0
    }
684
685
0
    return eErr;
686
0
}
687
688
/************************************************************************/
689
/*                            WriteHeader()                             */
690
/************************************************************************/
691
692
CPLErr GSBGDataset::WriteHeader(VSILFILE *fp, int nXSize, int nYSize,
693
                                double dfMinX, double dfMaxX, double dfMinY,
694
                                double dfMaxY, double dfMinZ, double dfMaxZ)
695
696
0
{
697
0
    if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
698
0
    {
699
0
        CPLError(CE_Failure, CPLE_FileIO,
700
0
                 "Unable to seek to start of grid file.");
701
0
        return CE_Failure;
702
0
    }
703
704
0
    if (VSIFWriteL((void *)"DSBB", 1, 4, fp) != 4)
705
0
    {
706
0
        CPLError(CE_Failure, CPLE_FileIO,
707
0
                 "Unable to write signature to grid file.");
708
0
        return CE_Failure;
709
0
    }
710
711
0
    assert(nXSize >= 0 && nXSize <= std::numeric_limits<int16_t>::max());
712
0
    GInt16 nTemp = CPL_LSBWORD16(static_cast<int16_t>(nXSize));
713
0
    if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
714
0
    {
715
0
        CPLError(CE_Failure, CPLE_FileIO,
716
0
                 "Unable to write raster X size to grid file.");
717
0
        return CE_Failure;
718
0
    }
719
720
0
    assert(nYSize >= 0 && nYSize <= std::numeric_limits<int16_t>::max());
721
0
    nTemp = CPL_LSBWORD16(static_cast<int16_t>(nYSize));
722
0
    if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
723
0
    {
724
0
        CPLError(CE_Failure, CPLE_FileIO,
725
0
                 "Unable to write raster Y size to grid file.");
726
0
        return CE_Failure;
727
0
    }
728
729
0
    double dfTemp = dfMinX;
730
0
    CPL_LSBPTR64(&dfTemp);
731
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
732
0
    {
733
0
        CPLError(CE_Failure, CPLE_FileIO,
734
0
                 "Unable to write minimum X value to grid file.");
735
0
        return CE_Failure;
736
0
    }
737
738
0
    dfTemp = dfMaxX;
739
0
    CPL_LSBPTR64(&dfTemp);
740
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
741
0
    {
742
0
        CPLError(CE_Failure, CPLE_FileIO,
743
0
                 "Unable to write maximum X value to grid file.");
744
0
        return CE_Failure;
745
0
    }
746
747
0
    dfTemp = dfMinY;
748
0
    CPL_LSBPTR64(&dfTemp);
749
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
750
0
    {
751
0
        CPLError(CE_Failure, CPLE_FileIO,
752
0
                 "Unable to write minimum Y value to grid file.");
753
0
        return CE_Failure;
754
0
    }
755
756
0
    dfTemp = dfMaxY;
757
0
    CPL_LSBPTR64(&dfTemp);
758
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
759
0
    {
760
0
        CPLError(CE_Failure, CPLE_FileIO,
761
0
                 "Unable to write maximum Y value to grid file.");
762
0
        return CE_Failure;
763
0
    }
764
765
0
    dfTemp = dfMinZ;
766
0
    CPL_LSBPTR64(&dfTemp);
767
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
768
0
    {
769
0
        CPLError(CE_Failure, CPLE_FileIO,
770
0
                 "Unable to write minimum Z value to grid file.");
771
0
        return CE_Failure;
772
0
    }
773
774
0
    dfTemp = dfMaxZ;
775
0
    CPL_LSBPTR64(&dfTemp);
776
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
777
0
    {
778
0
        CPLError(CE_Failure, CPLE_FileIO,
779
0
                 "Unable to write maximum Z value to grid file.");
780
0
        return CE_Failure;
781
0
    }
782
783
0
    return CE_None;
784
0
}
785
786
/************************************************************************/
787
/*                        GSBGCreateCheckDims()                         */
788
/************************************************************************/
789
790
static bool GSBGCreateCheckDims(int nXSize, int nYSize)
791
0
{
792
0
    if (nXSize <= 1 || nYSize <= 1)
793
0
    {
794
0
        CPLError(CE_Failure, CPLE_IllegalArg,
795
0
                 "Unable to create grid, both X and Y size must be "
796
0
                 "larger or equal to 2.");
797
0
        return false;
798
0
    }
799
0
    if (nXSize > std::numeric_limits<short>::max() ||
800
0
        nYSize > std::numeric_limits<short>::max())
801
0
    {
802
0
        CPLError(CE_Failure, CPLE_IllegalArg,
803
0
                 "Unable to create grid, Golden Software Binary Grid format "
804
0
                 "only supports sizes up to %dx%d.  %dx%d not supported.",
805
0
                 std::numeric_limits<short>::max(),
806
0
                 std::numeric_limits<short>::max(), nXSize, nYSize);
807
0
        return false;
808
0
    }
809
0
    return true;
810
0
}
811
812
/************************************************************************/
813
/*                               Create()                               */
814
/************************************************************************/
815
816
GDALDataset *GSBGDataset::Create(const char *pszFilename, int nXSize,
817
                                 int nYSize, int /* nBands */,
818
                                 GDALDataType eType, CSLConstList)
819
0
{
820
0
    if (!GSBGCreateCheckDims(nXSize, nYSize))
821
0
    {
822
0
        return nullptr;
823
0
    }
824
825
0
    if (eType != GDT_UInt8 && eType != GDT_Float32 && eType != GDT_UInt16 &&
826
0
        eType != GDT_Int16)
827
0
    {
828
0
        CPLError(CE_Failure, CPLE_AppDefined,
829
0
                 "Golden Software Binary Grid only supports Byte, Int16, "
830
0
                 "Uint16, and Float32 datatypes.  Unable to create with "
831
0
                 "type %s.\n",
832
0
                 GDALGetDataTypeName(eType));
833
834
0
        return nullptr;
835
0
    }
836
837
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
838
839
0
    if (fp == nullptr)
840
0
    {
841
0
        CPLError(CE_Failure, CPLE_OpenFailed,
842
0
                 "Attempt to create file '%s' failed.\n", pszFilename);
843
0
        return nullptr;
844
0
    }
845
846
0
    CPLErr eErr =
847
0
        WriteHeader(fp, nXSize, nYSize, 0.0, nXSize, 0.0, nYSize, 0.0, 0.0);
848
0
    if (eErr != CE_None)
849
0
    {
850
0
        VSIFCloseL(fp);
851
0
        return nullptr;
852
0
    }
853
854
0
    float fVal = fNODATA_VALUE;
855
0
    CPL_LSBPTR32(&fVal);
856
0
    for (int iRow = 0; iRow < nYSize; iRow++)
857
0
    {
858
0
        for (int iCol = 0; iCol < nXSize; iCol++)
859
0
        {
860
0
            if (VSIFWriteL((void *)&fVal, 4, 1, fp) != 1)
861
0
            {
862
0
                VSIFCloseL(fp);
863
0
                CPLError(CE_Failure, CPLE_FileIO,
864
0
                         "Unable to write grid cell.  Disk full?");
865
0
                return nullptr;
866
0
            }
867
0
        }
868
0
    }
869
870
0
    VSIFCloseL(fp);
871
872
0
    GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
873
0
    return Open(&oOpenInfo);
874
0
}
875
876
/************************************************************************/
877
/*                             CreateCopy()                             */
878
/************************************************************************/
879
880
GDALDataset *GSBGDataset::CreateCopy(const char *pszFilename,
881
                                     GDALDataset *poSrcDS, int bStrict,
882
                                     CPL_UNUSED CSLConstList papszOptions,
883
                                     GDALProgressFunc pfnProgress,
884
                                     void *pProgressData)
885
0
{
886
0
    if (pfnProgress == nullptr)
887
0
        pfnProgress = GDALDummyProgress;
888
889
0
    int nBands = poSrcDS->GetRasterCount();
890
0
    if (nBands == 0)
891
0
    {
892
0
        CPLError(
893
0
            CE_Failure, CPLE_NotSupported,
894
0
            "GSBG driver does not support source datasets with zero bands.");
895
0
        return nullptr;
896
0
    }
897
0
    else if (nBands > 1)
898
0
    {
899
0
        if (bStrict)
900
0
        {
901
0
            CPLError(CE_Failure, CPLE_NotSupported,
902
0
                     "Unable to create copy, Golden Software Binary Grid "
903
0
                     "format only supports one raster band.");
904
0
            return nullptr;
905
0
        }
906
0
        else
907
0
            CPLError(CE_Warning, CPLE_NotSupported,
908
0
                     "Golden Software Binary Grid format only supports one "
909
0
                     "raster band, first band will be copied.");
910
0
    }
911
912
0
    GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
913
0
    if (!GSBGCreateCheckDims(poSrcBand->GetXSize(), poSrcBand->GetYSize()))
914
0
    {
915
0
        return nullptr;
916
0
    }
917
918
0
    if (!pfnProgress(0.0, nullptr, pProgressData))
919
0
    {
920
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
921
0
        return nullptr;
922
0
    }
923
924
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
925
926
0
    if (fp == nullptr)
927
0
    {
928
0
        CPLError(CE_Failure, CPLE_OpenFailed,
929
0
                 "Attempt to create file '%s' failed.\n", pszFilename);
930
0
        return nullptr;
931
0
    }
932
933
0
    const int nXSize = poSrcBand->GetXSize();
934
0
    const int nYSize = poSrcBand->GetYSize();
935
0
    GDALGeoTransform gt;
936
0
    poSrcDS->GetGeoTransform(gt);
937
938
0
    double dfMinX = gt.xorig + gt.xscale / 2;
939
0
    double dfMaxX = gt.xscale * (nXSize - 0.5) + gt.xorig;
940
0
    double dfMinY = gt.yscale * (nYSize - 0.5) + gt.yorig;
941
0
    double dfMaxY = gt.yorig + gt.yscale / 2;
942
0
    CPLErr eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY,
943
0
                              dfMaxY, 0.0, 0.0);
944
945
0
    if (eErr != CE_None)
946
0
    {
947
0
        VSIFCloseL(fp);
948
0
        return nullptr;
949
0
    }
950
951
    /* -------------------------------------------------------------------- */
952
    /*      Copy band data.                                                 */
953
    /* -------------------------------------------------------------------- */
954
0
    float *pfData = (float *)VSI_MALLOC2_VERBOSE(nXSize, sizeof(float));
955
0
    if (pfData == nullptr)
956
0
    {
957
0
        VSIFCloseL(fp);
958
0
        return nullptr;
959
0
    }
960
961
0
    int bSrcHasNDValue;
962
0
    float fSrcNoDataValue = (float)poSrcBand->GetNoDataValue(&bSrcHasNDValue);
963
0
    double dfMinZ = std::numeric_limits<double>::max();
964
0
    double dfMaxZ = std::numeric_limits<double>::lowest();
965
0
    for (int iRow = nYSize - 1; iRow >= 0; iRow--)
966
0
    {
967
0
        eErr = poSrcBand->RasterIO(GF_Read, 0, iRow, nXSize, 1, pfData, nXSize,
968
0
                                   1, GDT_Float32, 0, 0, nullptr);
969
970
0
        if (eErr != CE_None)
971
0
        {
972
0
            VSIFCloseL(fp);
973
0
            VSIFree(pfData);
974
0
            return nullptr;
975
0
        }
976
977
0
        for (int iCol = 0; iCol < nXSize; iCol++)
978
0
        {
979
0
            if (bSrcHasNDValue && pfData[iCol] == fSrcNoDataValue)
980
0
            {
981
0
                pfData[iCol] = fNODATA_VALUE;
982
0
            }
983
0
            else
984
0
            {
985
0
                if (pfData[iCol] > dfMaxZ)
986
0
                    dfMaxZ = pfData[iCol];
987
988
0
                if (pfData[iCol] < dfMinZ)
989
0
                    dfMinZ = pfData[iCol];
990
0
            }
991
992
0
            CPL_LSBPTR32(pfData + iCol);
993
0
        }
994
995
0
        if (VSIFWriteL((void *)pfData, 4, nXSize, fp) !=
996
0
            static_cast<unsigned>(nXSize))
997
0
        {
998
0
            VSIFCloseL(fp);
999
0
            VSIFree(pfData);
1000
0
            CPLError(CE_Failure, CPLE_FileIO,
1001
0
                     "Unable to write grid row. Disk full?");
1002
0
            return nullptr;
1003
0
        }
1004
1005
0
        if (!pfnProgress(static_cast<double>(nYSize - iRow) / nYSize, nullptr,
1006
0
                         pProgressData))
1007
0
        {
1008
0
            VSIFCloseL(fp);
1009
0
            VSIFree(pfData);
1010
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1011
0
            return nullptr;
1012
0
        }
1013
0
    }
1014
1015
0
    VSIFree(pfData);
1016
1017
    /* write out the min and max values */
1018
0
    eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY,
1019
0
                       dfMinZ, dfMaxZ);
1020
1021
0
    if (eErr != CE_None)
1022
0
    {
1023
0
        VSIFCloseL(fp);
1024
0
        return nullptr;
1025
0
    }
1026
1027
0
    VSIFCloseL(fp);
1028
1029
0
    GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
1030
0
    GDALPamDataset *poDS = OpenPAM(&oOpenInfo);
1031
0
    if (poDS)
1032
0
    {
1033
0
        poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
1034
0
    }
1035
0
    return poDS;
1036
0
}
1037
1038
/************************************************************************/
1039
/*                         GDALRegister_GSBG()                          */
1040
/************************************************************************/
1041
1042
void GDALRegister_GSBG()
1043
1044
24
{
1045
24
    if (GDALGetDriverByName("GSBG") != nullptr)
1046
0
        return;
1047
1048
24
    GDALDriver *poDriver = new GDALDriver();
1049
1050
24
    poDriver->SetDescription("GSBG");
1051
24
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1052
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1053
24
                              "Golden Software Binary Grid (.grd)");
1054
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gsbg.html");
1055
24
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
1056
24
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1057
24
                              "Byte Int16 UInt16 Float32");
1058
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1059
1060
24
    poDriver->pfnIdentify = GSBGDataset::Identify;
1061
24
    poDriver->pfnOpen = GSBGDataset::Open;
1062
24
    poDriver->pfnCreate = GSBGDataset::Create;
1063
24
    poDriver->pfnCreateCopy = GSBGDataset::CreateCopy;
1064
1065
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
1066
24
}