Coverage Report

Created: 2025-12-03 08:24

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
2
    GSBGDataset() = default;
52
53
    ~GSBGDataset() override;
54
55
    static int Identify(GDALOpenInfo *);
56
    static GDALDataset *Open(GDALOpenInfo *);
57
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
58
                               int nBands, GDALDataType eType,
59
                               char **papszParamList);
60
    static GDALDataset *CreateCopy(const char *pszFilename,
61
                                   GDALDataset *poSrcDS, int bStrict,
62
                                   char **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
0
    : dfMinX(0.0), dfMaxX(0.0), dfMinY(0.0), dfMaxY(0.0), dfMinZ(0.0),
118
0
      dfMaxZ(0.0), pafRowMinZ(nullptr), pafRowMaxZ(nullptr), nMinZRow(-1),
119
0
      nMaxZRow(-1)
120
0
{
121
0
    this->poDS = poDSIn;
122
0
    this->nBand = nBandIn;
123
124
0
    eDataType = GDT_Float32;
125
126
0
    nBlockXSize = poDS->GetRasterXSize();
127
0
    nBlockYSize = 1;
128
0
}
129
130
/************************************************************************/
131
/*                           ~GSBGRasterBand()                          */
132
/************************************************************************/
133
134
GSBGRasterBand::~GSBGRasterBand()
135
136
0
{
137
0
    if (pafRowMinZ != nullptr)
138
0
        CPLFree(pafRowMinZ);
139
0
    if (pafRowMaxZ != nullptr)
140
0
        CPLFree(pafRowMaxZ);
141
0
}
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
0
{
236
0
    if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
237
0
        return CE_Failure;
238
239
0
    GSBGDataset *poGDS = cpl::down_cast<GSBGDataset *>(poDS);
240
0
    if (VSIFSeekL(poGDS->fp,
241
0
                  GSBGDataset::nHEADER_SIZE +
242
0
                      4 * static_cast<vsi_l_offset>(nRasterXSize) *
243
0
                          (nRasterYSize - nBlockYOff - 1),
244
0
                  SEEK_SET) != 0)
245
0
    {
246
0
        CPLError(CE_Failure, CPLE_FileIO,
247
0
                 "Unable to seek to beginning of grid row.\n");
248
0
        return CE_Failure;
249
0
    }
250
251
0
    if (VSIFReadL(pImage, sizeof(float), nBlockXSize, poGDS->fp) !=
252
0
        static_cast<unsigned>(nBlockXSize))
253
0
    {
254
0
        CPLError(CE_Failure, CPLE_FileIO,
255
0
                 "Unable to read block from grid file.\n");
256
0
        return CE_Failure;
257
0
    }
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
0
}
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.\n");
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.\n");
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.\n");
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
0
{
421
0
    if (pbSuccess)
422
0
        *pbSuccess = TRUE;
423
424
0
    return GSBGDataset::fNODATA_VALUE;
425
0
}
426
427
/************************************************************************/
428
/*                             GetMinimum()                             */
429
/************************************************************************/
430
431
double GSBGRasterBand::GetMinimum(int *pbSuccess)
432
0
{
433
0
    if (pbSuccess)
434
0
        *pbSuccess = TRUE;
435
436
0
    return dfMinZ;
437
0
}
438
439
/************************************************************************/
440
/*                             GetMaximum()                             */
441
/************************************************************************/
442
443
double GSBGRasterBand::GetMaximum(int *pbSuccess)
444
0
{
445
0
    if (pbSuccess)
446
0
        *pbSuccess = TRUE;
447
448
0
    return dfMaxZ;
449
0
}
450
451
/************************************************************************/
452
/* ==================================================================== */
453
/*                              GSBGDataset                             */
454
/* ==================================================================== */
455
/************************************************************************/
456
457
GSBGDataset::~GSBGDataset()
458
459
2
{
460
2
    FlushCache(true);
461
2
    if (fp != nullptr)
462
2
        VSIFCloseL(fp);
463
2
}
464
465
/************************************************************************/
466
/*                              Identify()                              */
467
/************************************************************************/
468
469
int GSBGDataset::Identify(GDALOpenInfo *poOpenInfo)
470
471
570k
{
472
    /* Check for signature */
473
570k
    if (poOpenInfo->nHeaderBytes < 4 ||
474
62.2k
        !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "DSBB"))
475
570k
    {
476
570k
        return FALSE;
477
570k
    }
478
479
4
    return TRUE;
480
570k
}
481
482
/************************************************************************/
483
/*                                Open()                                */
484
/************************************************************************/
485
486
GDALDataset *GSBGDataset::Open(GDALOpenInfo *poOpenInfo)
487
488
2
{
489
2
    if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
490
0
    {
491
0
        return nullptr;
492
0
    }
493
494
    /* -------------------------------------------------------------------- */
495
    /*      Create a corresponding GDALDataset.                             */
496
    /* -------------------------------------------------------------------- */
497
2
    auto poDS = std::make_unique<GSBGDataset>();
498
499
2
    poDS->eAccess = poOpenInfo->eAccess;
500
2
    poDS->fp = poOpenInfo->fpL;
501
2
    poOpenInfo->fpL = nullptr;
502
503
    /* -------------------------------------------------------------------- */
504
    /*      Read the header.                                                */
505
    /* -------------------------------------------------------------------- */
506
2
    if (VSIFSeekL(poDS->fp, 4, SEEK_SET) != 0)
507
0
    {
508
0
        CPLError(CE_Failure, CPLE_FileIO,
509
0
                 "Unable to seek to start of grid file header.\n");
510
0
        return nullptr;
511
0
    }
512
513
    /* Parse number of X axis grid rows */
514
2
    GInt16 nTemp;
515
2
    if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
516
0
    {
517
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster X size.\n");
518
0
        return nullptr;
519
0
    }
520
2
    poDS->nRasterXSize = CPL_LSBWORD16(nTemp);
521
522
2
    if (VSIFReadL((void *)&nTemp, 2, 1, poDS->fp) != 1)
523
2
    {
524
2
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster Y size.\n");
525
2
        return nullptr;
526
2
    }
527
0
    poDS->nRasterYSize = CPL_LSBWORD16(nTemp);
528
529
0
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
530
0
    {
531
0
        return nullptr;
532
0
    }
533
534
    /* -------------------------------------------------------------------- */
535
    /*      Create band information objects.                                */
536
    /* -------------------------------------------------------------------- */
537
0
    GSBGRasterBand *poBand = new GSBGRasterBand(poDS.get(), 1);
538
0
    poDS->SetBand(1, poBand);
539
540
0
    double dfTemp;
541
0
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
542
0
    {
543
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.\n");
544
0
        return nullptr;
545
0
    }
546
0
    CPL_LSBPTR64(&dfTemp);
547
0
    poBand->dfMinX = dfTemp;
548
549
0
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
550
0
    {
551
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum X value.\n");
552
0
        return nullptr;
553
0
    }
554
0
    CPL_LSBPTR64(&dfTemp);
555
0
    poBand->dfMaxX = dfTemp;
556
557
0
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
558
0
    {
559
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Y value.\n");
560
0
        return nullptr;
561
0
    }
562
0
    CPL_LSBPTR64(&dfTemp);
563
0
    poBand->dfMinY = dfTemp;
564
565
0
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
566
0
    {
567
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Y value.\n");
568
0
        return nullptr;
569
0
    }
570
0
    CPL_LSBPTR64(&dfTemp);
571
0
    poBand->dfMaxY = dfTemp;
572
573
0
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
574
0
    {
575
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum Z value.\n");
576
0
        return nullptr;
577
0
    }
578
0
    CPL_LSBPTR64(&dfTemp);
579
0
    poBand->dfMinZ = dfTemp;
580
581
0
    if (VSIFReadL((void *)&dfTemp, 8, 1, poDS->fp) != 1)
582
0
    {
583
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read maximum Z value.\n");
584
0
        return nullptr;
585
0
    }
586
0
    CPL_LSBPTR64(&dfTemp);
587
0
    poBand->dfMaxZ = dfTemp;
588
589
    /* -------------------------------------------------------------------- */
590
    /*      Initialize any PAM information.                                 */
591
    /* -------------------------------------------------------------------- */
592
0
    poDS->SetDescription(poOpenInfo->pszFilename);
593
0
    poDS->TryLoadXML();
594
595
    /* -------------------------------------------------------------------- */
596
    /*      Check for external overviews.                                   */
597
    /* -------------------------------------------------------------------- */
598
0
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename,
599
0
                                poOpenInfo->GetSiblingFiles());
600
601
0
    return poDS.release();
602
0
}
603
604
/************************************************************************/
605
/*                          GetGeoTransform()                           */
606
/************************************************************************/
607
608
CPLErr GSBGDataset::GetGeoTransform(GDALGeoTransform &gt) const
609
0
{
610
0
    const GSBGRasterBand *poGRB =
611
0
        cpl::down_cast<const GSBGRasterBand *>(GetRasterBand(1));
612
613
    /* check if we have a PAM GeoTransform stored */
614
0
    CPLPushErrorHandler(CPLQuietErrorHandler);
615
0
    CPLErr eErr = GDALPamDataset::GetGeoTransform(gt);
616
0
    CPLPopErrorHandler();
617
618
0
    if (eErr == CE_None)
619
0
        return CE_None;
620
621
0
    if (nRasterXSize == 1 || nRasterYSize == 1)
622
0
        return CE_Failure;
623
624
    /* calculate pixel size first */
625
0
    gt[1] = (poGRB->dfMaxX - poGRB->dfMinX) / (nRasterXSize - 1);
626
0
    gt[5] = (poGRB->dfMinY - poGRB->dfMaxY) / (nRasterYSize - 1);
627
628
    /* then calculate image origin */
629
0
    gt[0] = poGRB->dfMinX - gt[1] / 2;
630
0
    gt[3] = poGRB->dfMaxY - gt[5] / 2;
631
632
    /* tilt/rotation does not supported by the GS grids */
633
0
    gt[4] = 0.0;
634
0
    gt[2] = 0.0;
635
636
0
    return CE_None;
637
0
}
638
639
/************************************************************************/
640
/*                          SetGeoTransform()                           */
641
/************************************************************************/
642
643
CPLErr GSBGDataset::SetGeoTransform(const GDALGeoTransform &gt)
644
0
{
645
0
    if (eAccess == GA_ReadOnly)
646
0
    {
647
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
648
0
                 "Unable to set GeoTransform, dataset opened read only.\n");
649
0
        return CE_Failure;
650
0
    }
651
652
0
    GSBGRasterBand *poGRB = cpl::down_cast<GSBGRasterBand *>(GetRasterBand(1));
653
654
    /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
655
    // CPLErr eErr = CE_None;
656
    /*if( gt[2] != 0.0 || gt[4] != 0.0
657
        || gt[1] < 0.0 || gt[5] < 0.0 )
658
        eErr = GDALPamDataset::SetGeoTransform( gt );
659
660
    if( eErr != CE_None )
661
        return eErr;*/
662
663
0
    double dfMinX = gt[0] + gt[1] / 2;
664
0
    double dfMaxX = gt[1] * (nRasterXSize - 0.5) + gt[0];
665
0
    double dfMinY = gt[5] * (nRasterYSize - 0.5) + gt[3];
666
0
    double dfMaxY = gt[3] + gt[5] / 2;
667
668
0
    CPLErr eErr =
669
0
        WriteHeader(fp, poGRB->nRasterXSize, poGRB->nRasterYSize, dfMinX,
670
0
                    dfMaxX, dfMinY, dfMaxY, poGRB->dfMinZ, poGRB->dfMaxZ);
671
672
0
    if (eErr == CE_None)
673
0
    {
674
0
        poGRB->dfMinX = dfMinX;
675
0
        poGRB->dfMaxX = dfMaxX;
676
0
        poGRB->dfMinY = dfMinY;
677
0
        poGRB->dfMaxY = dfMaxY;
678
0
    }
679
680
0
    return eErr;
681
0
}
682
683
/************************************************************************/
684
/*                             WriteHeader()                            */
685
/************************************************************************/
686
687
CPLErr GSBGDataset::WriteHeader(VSILFILE *fp, int nXSize, int nYSize,
688
                                double dfMinX, double dfMaxX, double dfMinY,
689
                                double dfMaxY, double dfMinZ, double dfMaxZ)
690
691
0
{
692
0
    if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
693
0
    {
694
0
        CPLError(CE_Failure, CPLE_FileIO,
695
0
                 "Unable to seek to start of grid file.\n");
696
0
        return CE_Failure;
697
0
    }
698
699
0
    if (VSIFWriteL((void *)"DSBB", 1, 4, fp) != 4)
700
0
    {
701
0
        CPLError(CE_Failure, CPLE_FileIO,
702
0
                 "Unable to write signature to grid file.\n");
703
0
        return CE_Failure;
704
0
    }
705
706
0
    assert(nXSize >= 0 && nXSize <= std::numeric_limits<int16_t>::max());
707
0
    GInt16 nTemp = CPL_LSBWORD16(static_cast<int16_t>(nXSize));
708
0
    if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
709
0
    {
710
0
        CPLError(CE_Failure, CPLE_FileIO,
711
0
                 "Unable to write raster X size to grid file.\n");
712
0
        return CE_Failure;
713
0
    }
714
715
0
    assert(nYSize >= 0 && nYSize <= std::numeric_limits<int16_t>::max());
716
0
    nTemp = CPL_LSBWORD16(static_cast<int16_t>(nYSize));
717
0
    if (VSIFWriteL((void *)&nTemp, 2, 1, fp) != 1)
718
0
    {
719
0
        CPLError(CE_Failure, CPLE_FileIO,
720
0
                 "Unable to write raster Y size to grid file.\n");
721
0
        return CE_Failure;
722
0
    }
723
724
0
    double dfTemp = dfMinX;
725
0
    CPL_LSBPTR64(&dfTemp);
726
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
727
0
    {
728
0
        CPLError(CE_Failure, CPLE_FileIO,
729
0
                 "Unable to write minimum X value to grid file.\n");
730
0
        return CE_Failure;
731
0
    }
732
733
0
    dfTemp = dfMaxX;
734
0
    CPL_LSBPTR64(&dfTemp);
735
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
736
0
    {
737
0
        CPLError(CE_Failure, CPLE_FileIO,
738
0
                 "Unable to write maximum X value to grid file.\n");
739
0
        return CE_Failure;
740
0
    }
741
742
0
    dfTemp = dfMinY;
743
0
    CPL_LSBPTR64(&dfTemp);
744
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
745
0
    {
746
0
        CPLError(CE_Failure, CPLE_FileIO,
747
0
                 "Unable to write minimum Y value to grid file.\n");
748
0
        return CE_Failure;
749
0
    }
750
751
0
    dfTemp = dfMaxY;
752
0
    CPL_LSBPTR64(&dfTemp);
753
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
754
0
    {
755
0
        CPLError(CE_Failure, CPLE_FileIO,
756
0
                 "Unable to write maximum Y value to grid file.\n");
757
0
        return CE_Failure;
758
0
    }
759
760
0
    dfTemp = dfMinZ;
761
0
    CPL_LSBPTR64(&dfTemp);
762
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
763
0
    {
764
0
        CPLError(CE_Failure, CPLE_FileIO,
765
0
                 "Unable to write minimum Z value to grid file.\n");
766
0
        return CE_Failure;
767
0
    }
768
769
0
    dfTemp = dfMaxZ;
770
0
    CPL_LSBPTR64(&dfTemp);
771
0
    if (VSIFWriteL((void *)&dfTemp, 8, 1, fp) != 1)
772
0
    {
773
0
        CPLError(CE_Failure, CPLE_FileIO,
774
0
                 "Unable to write maximum Z value to grid file.\n");
775
0
        return CE_Failure;
776
0
    }
777
778
0
    return CE_None;
779
0
}
780
781
/************************************************************************/
782
/*                        GSBGCreateCheckDims()                         */
783
/************************************************************************/
784
785
static bool GSBGCreateCheckDims(int nXSize, int nYSize)
786
0
{
787
0
    if (nXSize <= 1 || nYSize <= 1)
788
0
    {
789
0
        CPLError(CE_Failure, CPLE_IllegalArg,
790
0
                 "Unable to create grid, both X and Y size must be "
791
0
                 "larger or equal to 2.");
792
0
        return false;
793
0
    }
794
0
    if (nXSize > std::numeric_limits<short>::max() ||
795
0
        nYSize > std::numeric_limits<short>::max())
796
0
    {
797
0
        CPLError(CE_Failure, CPLE_IllegalArg,
798
0
                 "Unable to create grid, Golden Software Binary Grid format "
799
0
                 "only supports sizes up to %dx%d.  %dx%d not supported.",
800
0
                 std::numeric_limits<short>::max(),
801
0
                 std::numeric_limits<short>::max(), nXSize, nYSize);
802
0
        return false;
803
0
    }
804
0
    return true;
805
0
}
806
807
/************************************************************************/
808
/*                               Create()                               */
809
/************************************************************************/
810
811
GDALDataset *GSBGDataset::Create(const char *pszFilename, int nXSize,
812
                                 int nYSize, int /* nBands */,
813
                                 GDALDataType eType,
814
                                 CPL_UNUSED char **papszParamList)
815
0
{
816
0
    if (!GSBGCreateCheckDims(nXSize, nYSize))
817
0
    {
818
0
        return nullptr;
819
0
    }
820
821
0
    if (eType != GDT_UInt8 && eType != GDT_Float32 && eType != GDT_UInt16 &&
822
0
        eType != GDT_Int16)
823
0
    {
824
0
        CPLError(CE_Failure, CPLE_AppDefined,
825
0
                 "Golden Software Binary Grid only supports Byte, Int16, "
826
0
                 "Uint16, and Float32 datatypes.  Unable to create with "
827
0
                 "type %s.\n",
828
0
                 GDALGetDataTypeName(eType));
829
830
0
        return nullptr;
831
0
    }
832
833
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
834
835
0
    if (fp == nullptr)
836
0
    {
837
0
        CPLError(CE_Failure, CPLE_OpenFailed,
838
0
                 "Attempt to create file '%s' failed.\n", pszFilename);
839
0
        return nullptr;
840
0
    }
841
842
0
    CPLErr eErr =
843
0
        WriteHeader(fp, nXSize, nYSize, 0.0, nXSize, 0.0, nYSize, 0.0, 0.0);
844
0
    if (eErr != CE_None)
845
0
    {
846
0
        VSIFCloseL(fp);
847
0
        return nullptr;
848
0
    }
849
850
0
    float fVal = fNODATA_VALUE;
851
0
    CPL_LSBPTR32(&fVal);
852
0
    for (int iRow = 0; iRow < nYSize; iRow++)
853
0
    {
854
0
        for (int iCol = 0; iCol < nXSize; iCol++)
855
0
        {
856
0
            if (VSIFWriteL((void *)&fVal, 4, 1, fp) != 1)
857
0
            {
858
0
                VSIFCloseL(fp);
859
0
                CPLError(CE_Failure, CPLE_FileIO,
860
0
                         "Unable to write grid cell.  Disk full?\n");
861
0
                return nullptr;
862
0
            }
863
0
        }
864
0
    }
865
866
0
    VSIFCloseL(fp);
867
868
0
    return (GDALDataset *)GDALOpen(pszFilename, GA_Update);
869
0
}
870
871
/************************************************************************/
872
/*                             CreateCopy()                             */
873
/************************************************************************/
874
875
GDALDataset *GSBGDataset::CreateCopy(const char *pszFilename,
876
                                     GDALDataset *poSrcDS, int bStrict,
877
                                     CPL_UNUSED char **papszOptions,
878
                                     GDALProgressFunc pfnProgress,
879
                                     void *pProgressData)
880
0
{
881
0
    if (pfnProgress == nullptr)
882
0
        pfnProgress = GDALDummyProgress;
883
884
0
    int nBands = poSrcDS->GetRasterCount();
885
0
    if (nBands == 0)
886
0
    {
887
0
        CPLError(
888
0
            CE_Failure, CPLE_NotSupported,
889
0
            "GSBG driver does not support source dataset with zero band.\n");
890
0
        return nullptr;
891
0
    }
892
0
    else if (nBands > 1)
893
0
    {
894
0
        if (bStrict)
895
0
        {
896
0
            CPLError(CE_Failure, CPLE_NotSupported,
897
0
                     "Unable to create copy, Golden Software Binary Grid "
898
0
                     "format only supports one raster band.\n");
899
0
            return nullptr;
900
0
        }
901
0
        else
902
0
            CPLError(CE_Warning, CPLE_NotSupported,
903
0
                     "Golden Software Binary Grid format only supports one "
904
0
                     "raster band, first band will be copied.\n");
905
0
    }
906
907
0
    GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
908
0
    if (!GSBGCreateCheckDims(poSrcBand->GetXSize(), poSrcBand->GetYSize()))
909
0
    {
910
0
        return nullptr;
911
0
    }
912
913
0
    if (!pfnProgress(0.0, nullptr, pProgressData))
914
0
    {
915
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated\n");
916
0
        return nullptr;
917
0
    }
918
919
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
920
921
0
    if (fp == nullptr)
922
0
    {
923
0
        CPLError(CE_Failure, CPLE_OpenFailed,
924
0
                 "Attempt to create file '%s' failed.\n", pszFilename);
925
0
        return nullptr;
926
0
    }
927
928
0
    const int nXSize = poSrcBand->GetXSize();
929
0
    const int nYSize = poSrcBand->GetYSize();
930
0
    GDALGeoTransform gt;
931
0
    poSrcDS->GetGeoTransform(gt);
932
933
0
    double dfMinX = gt[0] + gt[1] / 2;
934
0
    double dfMaxX = gt[1] * (nXSize - 0.5) + gt[0];
935
0
    double dfMinY = gt[5] * (nYSize - 0.5) + gt[3];
936
0
    double dfMaxY = gt[3] + gt[5] / 2;
937
0
    CPLErr eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY,
938
0
                              dfMaxY, 0.0, 0.0);
939
940
0
    if (eErr != CE_None)
941
0
    {
942
0
        VSIFCloseL(fp);
943
0
        return nullptr;
944
0
    }
945
946
    /* -------------------------------------------------------------------- */
947
    /*      Copy band data.                                                 */
948
    /* -------------------------------------------------------------------- */
949
0
    float *pfData = (float *)VSI_MALLOC2_VERBOSE(nXSize, sizeof(float));
950
0
    if (pfData == nullptr)
951
0
    {
952
0
        VSIFCloseL(fp);
953
0
        return nullptr;
954
0
    }
955
956
0
    int bSrcHasNDValue;
957
0
    float fSrcNoDataValue = (float)poSrcBand->GetNoDataValue(&bSrcHasNDValue);
958
0
    double dfMinZ = std::numeric_limits<double>::max();
959
0
    double dfMaxZ = std::numeric_limits<double>::lowest();
960
0
    for (int iRow = nYSize - 1; iRow >= 0; iRow--)
961
0
    {
962
0
        eErr = poSrcBand->RasterIO(GF_Read, 0, iRow, nXSize, 1, pfData, nXSize,
963
0
                                   1, GDT_Float32, 0, 0, nullptr);
964
965
0
        if (eErr != CE_None)
966
0
        {
967
0
            VSIFCloseL(fp);
968
0
            VSIFree(pfData);
969
0
            return nullptr;
970
0
        }
971
972
0
        for (int iCol = 0; iCol < nXSize; iCol++)
973
0
        {
974
0
            if (bSrcHasNDValue && pfData[iCol] == fSrcNoDataValue)
975
0
            {
976
0
                pfData[iCol] = fNODATA_VALUE;
977
0
            }
978
0
            else
979
0
            {
980
0
                if (pfData[iCol] > dfMaxZ)
981
0
                    dfMaxZ = pfData[iCol];
982
983
0
                if (pfData[iCol] < dfMinZ)
984
0
                    dfMinZ = pfData[iCol];
985
0
            }
986
987
0
            CPL_LSBPTR32(pfData + iCol);
988
0
        }
989
990
0
        if (VSIFWriteL((void *)pfData, 4, nXSize, fp) !=
991
0
            static_cast<unsigned>(nXSize))
992
0
        {
993
0
            VSIFCloseL(fp);
994
0
            VSIFree(pfData);
995
0
            CPLError(CE_Failure, CPLE_FileIO,
996
0
                     "Unable to write grid row. Disk full?\n");
997
0
            return nullptr;
998
0
        }
999
1000
0
        if (!pfnProgress(static_cast<double>(nYSize - iRow) / nYSize, nullptr,
1001
0
                         pProgressData))
1002
0
        {
1003
0
            VSIFCloseL(fp);
1004
0
            VSIFree(pfData);
1005
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1006
0
            return nullptr;
1007
0
        }
1008
0
    }
1009
1010
0
    VSIFree(pfData);
1011
1012
    /* write out the min and max values */
1013
0
    eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY,
1014
0
                       dfMinZ, dfMaxZ);
1015
1016
0
    if (eErr != CE_None)
1017
0
    {
1018
0
        VSIFCloseL(fp);
1019
0
        return nullptr;
1020
0
    }
1021
1022
0
    VSIFCloseL(fp);
1023
1024
0
    GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen(pszFilename, GA_Update);
1025
0
    if (poDS)
1026
0
    {
1027
0
        poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
1028
0
    }
1029
0
    return poDS;
1030
0
}
1031
1032
/************************************************************************/
1033
/*                          GDALRegister_GSBG()                         */
1034
/************************************************************************/
1035
1036
void GDALRegister_GSBG()
1037
1038
22
{
1039
22
    if (GDALGetDriverByName("GSBG") != nullptr)
1040
0
        return;
1041
1042
22
    GDALDriver *poDriver = new GDALDriver();
1043
1044
22
    poDriver->SetDescription("GSBG");
1045
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1046
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1047
22
                              "Golden Software Binary Grid (.grd)");
1048
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gsbg.html");
1049
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
1050
22
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1051
22
                              "Byte Int16 UInt16 Float32");
1052
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1053
1054
22
    poDriver->pfnIdentify = GSBGDataset::Identify;
1055
22
    poDriver->pfnOpen = GSBGDataset::Open;
1056
22
    poDriver->pfnCreate = GSBGDataset::Create;
1057
22
    poDriver->pfnCreateCopy = GSBGDataset::CreateCopy;
1058
1059
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
1060
22
}