Coverage Report

Created: 2025-08-11 09:23

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