Coverage Report

Created: 2025-06-09 07:42

/src/gdal/frmts/gsg/gs7bgdataset.cpp
Line
Count
Source (jump to first uncovered line)
1
/****************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Implements the Golden Software Surfer 7 Binary Grid Format.
5
 * Author:   Adam Guernsey, adam@ctech.com
6
 *           (Based almost entirely on gsbgdataset.cpp by Kevin Locke)
7
 *           Create functions added by Russell Jurgensen.
8
 *
9
 ****************************************************************************
10
 * Copyright (c) 2007, Adam Guernsey <adam@ctech.com>
11
 * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
16
#include <cassert>
17
#include <cfloat>
18
#include <climits>
19
#include <cmath>
20
#include <limits>
21
22
#include "gdal_frmts.h"
23
#include "gdal_pam.h"
24
25
/************************************************************************/
26
/* ==================================================================== */
27
/*                GS7BGDataset                */
28
/* ==================================================================== */
29
/************************************************************************/
30
31
class GS7BGRasterBand;
32
33
constexpr double dfDefaultNoDataValue = 1.701410009187828e+38f;
34
35
class GS7BGDataset final : public GDALPamDataset
36
{
37
    friend class GS7BGRasterBand;
38
39
    double dfNoData_Value;
40
    static const size_t nHEADER_SIZE;
41
    size_t nData_Position;
42
43
    static CPLErr WriteHeader(VSILFILE *fp, GInt32 nXSize, GInt32 nYSize,
44
                              double dfMinX, double dfMaxX, double dfMinY,
45
                              double dfMaxY, double dfMinZ, double dfMaxZ);
46
47
    VSILFILE *fp;
48
49
  public:
50
    GS7BGDataset()
51
        : /* NOTE:  This is not mentioned in the spec, but Surfer 8 uses this
52
             value */
53
          /* 0x7effffee (Little Endian: eeffff7e) */
54
0
          dfNoData_Value(dfDefaultNoDataValue), nData_Position(0), fp(nullptr)
55
0
    {
56
0
    }
57
58
    ~GS7BGDataset();
59
60
    static int Identify(GDALOpenInfo *);
61
    static GDALDataset *Open(GDALOpenInfo *);
62
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
63
                               int nBandsIn, GDALDataType eType,
64
                               char **papszParamList);
65
    static GDALDataset *CreateCopy(const char *pszFilename,
66
                                   GDALDataset *poSrcDS, int bStrict,
67
                                   char **papszOptions,
68
                                   GDALProgressFunc pfnProgress,
69
                                   void *pProgressData);
70
71
    CPLErr GetGeoTransform(double *padfGeoTransform) override;
72
    CPLErr SetGeoTransform(double *padfGeoTransform) override;
73
};
74
75
const size_t GS7BGDataset::nHEADER_SIZE = 100;
76
77
constexpr long nHEADER_TAG = 0x42525344;
78
constexpr long nGRID_TAG = 0x44495247;
79
constexpr long nDATA_TAG = 0x41544144;
80
#if 0 /* Unused */
81
const long  nFAULT_TAG = 0x49544c46;
82
#endif
83
84
/************************************************************************/
85
/* ==================================================================== */
86
/*                            GS7BGRasterBand                           */
87
/* ==================================================================== */
88
/************************************************************************/
89
90
class GS7BGRasterBand final : public GDALPamRasterBand
91
{
92
    friend class GS7BGDataset;
93
94
    double dfMinX;
95
    double dfMaxX;
96
    double dfMinY;
97
    double dfMaxY;
98
    double dfMinZ;
99
    double dfMaxZ;
100
101
    double *pafRowMinZ;
102
    double *pafRowMaxZ;
103
    int nMinZRow;
104
    int nMaxZRow;
105
106
    CPLErr ScanForMinMaxZ();
107
108
  public:
109
    GS7BGRasterBand(GS7BGDataset *, int);
110
    ~GS7BGRasterBand();
111
112
    CPLErr IReadBlock(int, int, void *) override;
113
    CPLErr IWriteBlock(int, int, void *) override;
114
    double GetMinimum(int *pbSuccess = nullptr) override;
115
    double GetMaximum(int *pbSuccess = nullptr) override;
116
117
    double GetNoDataValue(int *pbSuccess = nullptr) override;
118
};
119
120
/************************************************************************/
121
/*                           GS7BGRasterBand()                          */
122
/************************************************************************/
123
124
GS7BGRasterBand::GS7BGRasterBand(GS7BGDataset *poDSIn, int nBandIn)
125
0
    : dfMinX(0.0), dfMaxX(0.0), dfMinY(0.0), dfMaxY(0.0), dfMinZ(0.0),
126
0
      dfMaxZ(0.0), pafRowMinZ(nullptr), pafRowMaxZ(nullptr), nMinZRow(-1),
127
0
      nMaxZRow(-1)
128
129
0
{
130
0
    poDS = poDSIn;
131
0
    nBand = nBandIn;
132
133
0
    eDataType = GDT_Float64;
134
135
0
    nBlockXSize = poDS->GetRasterXSize();
136
0
    nBlockYSize = 1;
137
0
}
138
139
/************************************************************************/
140
/*                           ~GSBGRasterBand()                          */
141
/************************************************************************/
142
143
GS7BGRasterBand::~GS7BGRasterBand()
144
145
0
{
146
0
    CPLFree(pafRowMinZ);
147
0
    CPLFree(pafRowMaxZ);
148
0
}
149
150
/************************************************************************/
151
/*                          ScanForMinMaxZ()                            */
152
/************************************************************************/
153
154
CPLErr GS7BGRasterBand::ScanForMinMaxZ()
155
156
0
{
157
0
    GS7BGDataset *poGDS = reinterpret_cast<GS7BGDataset *>(poDS);
158
0
    double *pafRowVals =
159
0
        (double *)VSI_MALLOC2_VERBOSE(nRasterXSize, sizeof(double));
160
161
0
    if (pafRowVals == nullptr)
162
0
    {
163
0
        return CE_Failure;
164
0
    }
165
166
0
    double dfNewMinZ = std::numeric_limits<double>::max();
167
0
    double dfNewMaxZ = std::numeric_limits<double>::lowest();
168
0
    int nNewMinZRow = 0;
169
0
    int nNewMaxZRow = 0;
170
171
    /* Since we have to scan, lets calc. statistics too */
172
0
    double dfSum = 0.0;
173
0
    double dfSum2 = 0.0;
174
0
    unsigned long nValuesRead = 0;
175
0
    for (int iRow = 0; iRow < nRasterYSize; iRow++)
176
0
    {
177
0
        CPLErr eErr = IReadBlock(0, iRow, pafRowVals);
178
0
        if (eErr != CE_None)
179
0
        {
180
0
            VSIFree(pafRowVals);
181
0
            return CE_Failure;
182
0
        }
183
184
0
        pafRowMinZ[iRow] = std::numeric_limits<float>::max();
185
0
        pafRowMaxZ[iRow] = std::numeric_limits<float>::lowest();
186
0
        for (int iCol = 0; iCol < nRasterXSize; iCol++)
187
0
        {
188
0
            if (pafRowVals[iCol] == poGDS->dfNoData_Value)
189
0
                continue;
190
191
0
            if (pafRowVals[iCol] < pafRowMinZ[iRow])
192
0
                pafRowMinZ[iRow] = pafRowVals[iCol];
193
194
0
            if (pafRowVals[iCol] > pafRowMinZ[iRow])
195
0
                pafRowMaxZ[iRow] = pafRowVals[iCol];
196
197
0
            dfSum += pafRowVals[iCol];
198
0
            dfSum2 += pafRowVals[iCol] * pafRowVals[iCol];
199
0
            nValuesRead++;
200
0
        }
201
202
0
        if (pafRowMinZ[iRow] < dfNewMinZ)
203
0
        {
204
0
            dfNewMinZ = pafRowMinZ[iRow];
205
0
            nNewMinZRow = iRow;
206
0
        }
207
208
0
        if (pafRowMaxZ[iRow] > dfNewMaxZ)
209
0
        {
210
0
            dfNewMaxZ = pafRowMaxZ[iRow];
211
0
            nNewMaxZRow = iRow;
212
0
        }
213
0
    }
214
215
0
    VSIFree(pafRowVals);
216
217
0
    if (nValuesRead == 0)
218
0
    {
219
0
        dfMinZ = 0.0;
220
0
        dfMaxZ = 0.0;
221
0
        nMinZRow = 0;
222
0
        nMaxZRow = 0;
223
0
        return CE_None;
224
0
    }
225
226
0
    dfMinZ = dfNewMinZ;
227
0
    dfMaxZ = dfNewMaxZ;
228
0
    nMinZRow = nNewMinZRow;
229
0
    nMaxZRow = nNewMaxZRow;
230
231
0
    double dfMean = dfSum / nValuesRead;
232
0
    double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean));
233
0
    SetStatistics(dfMinZ, dfMaxZ, dfMean, dfStdDev);
234
235
0
    return CE_None;
236
0
}
237
238
/************************************************************************/
239
/*                             IReadBlock()                             */
240
/************************************************************************/
241
242
CPLErr GS7BGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
243
244
0
{
245
0
    if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
246
0
        return CE_Failure;
247
248
0
    GS7BGDataset *poGDS = cpl::down_cast<GS7BGDataset *>(poDS);
249
250
0
    if (VSIFSeekL(poGDS->fp,
251
0
                  (poGDS->nData_Position +
252
0
                   sizeof(double) * static_cast<vsi_l_offset>(nRasterXSize) *
253
0
                       (nRasterYSize - nBlockYOff - 1)),
254
0
                  SEEK_SET) != 0)
255
0
    {
256
0
        CPLError(CE_Failure, CPLE_FileIO,
257
0
                 "Unable to seek to beginning of grid row.\n");
258
0
        return CE_Failure;
259
0
    }
260
261
0
    if (VSIFReadL(pImage, sizeof(double), nBlockXSize, poGDS->fp) !=
262
0
        static_cast<unsigned>(nBlockXSize))
263
0
    {
264
0
        CPLError(CE_Failure, CPLE_FileIO,
265
0
                 "Unable to read block from grid file.\n");
266
0
        return CE_Failure;
267
0
    }
268
269
#ifdef CPL_MSB
270
    double *pfImage = (double *)pImage;
271
    for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
272
        CPL_LSBPTR64(pfImage + iPixel);
273
#endif
274
275
0
    return CE_None;
276
0
}
277
278
/************************************************************************/
279
/*                            IWriteBlock()                             */
280
/************************************************************************/
281
282
CPLErr GS7BGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
283
                                    void *pImage)
284
285
0
{
286
0
    if (eAccess == GA_ReadOnly)
287
0
    {
288
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
289
0
                 "Unable to write block, dataset opened read only.\n");
290
0
        return CE_Failure;
291
0
    }
292
293
0
    if (nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0)
294
0
        return CE_Failure;
295
296
0
    GS7BGDataset *poGDS = cpl::down_cast<GS7BGDataset *>(poDS);
297
298
0
    if (pafRowMinZ == nullptr || pafRowMaxZ == nullptr || nMinZRow < 0 ||
299
0
        nMaxZRow < 0)
300
0
    {
301
0
        pafRowMinZ =
302
0
            (double *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(double));
303
0
        if (pafRowMinZ == nullptr)
304
0
        {
305
0
            return CE_Failure;
306
0
        }
307
308
0
        pafRowMaxZ =
309
0
            (double *)VSI_MALLOC2_VERBOSE(nRasterYSize, sizeof(double));
310
0
        if (pafRowMaxZ == nullptr)
311
0
        {
312
0
            VSIFree(pafRowMinZ);
313
0
            pafRowMinZ = nullptr;
314
0
            return CE_Failure;
315
0
        }
316
317
0
        CPLErr eErr = ScanForMinMaxZ();
318
0
        if (eErr != CE_None)
319
0
            return eErr;
320
0
    }
321
322
0
    if (VSIFSeekL(poGDS->fp,
323
0
                  GS7BGDataset::nHEADER_SIZE +
324
0
                      sizeof(double) * nRasterXSize *
325
0
                          (nRasterYSize - nBlockYOff - 1),
326
0
                  SEEK_SET) != 0)
327
0
    {
328
0
        CPLError(CE_Failure, CPLE_FileIO,
329
0
                 "Unable to seek to beginning of grid row.\n");
330
0
        return CE_Failure;
331
0
    }
332
333
0
    double *pdfImage = (double *)pImage;
334
0
    pafRowMinZ[nBlockYOff] = std::numeric_limits<double>::max();
335
0
    pafRowMaxZ[nBlockYOff] = std::numeric_limits<double>::lowest();
336
0
    for (int iPixel = 0; iPixel < nBlockXSize; iPixel++)
337
0
    {
338
0
        if (pdfImage[iPixel] != poGDS->dfNoData_Value)
339
0
        {
340
0
            if (pdfImage[iPixel] < pafRowMinZ[nBlockYOff])
341
0
                pafRowMinZ[nBlockYOff] = pdfImage[iPixel];
342
343
0
            if (pdfImage[iPixel] > pafRowMaxZ[nBlockYOff])
344
0
                pafRowMaxZ[nBlockYOff] = pdfImage[iPixel];
345
0
        }
346
347
0
        CPL_LSBPTR64(pdfImage + iPixel);
348
0
    }
349
350
0
    if (VSIFWriteL(pImage, sizeof(double), nBlockXSize, poGDS->fp) !=
351
0
        static_cast<unsigned>(nBlockXSize))
352
0
    {
353
0
        CPLError(CE_Failure, CPLE_FileIO,
354
0
                 "Unable to write block to grid file.\n");
355
0
        return CE_Failure;
356
0
    }
357
358
    /* Update min/max Z values as appropriate */
359
0
    bool bHeaderNeedsUpdate = false;
360
0
    if (nMinZRow == nBlockYOff && pafRowMinZ[nBlockYOff] > dfMinZ)
361
0
    {
362
0
        double dfNewMinZ = std::numeric_limits<double>::max();
363
0
        for (int iRow = 0; iRow < nRasterYSize; iRow++)
364
0
        {
365
0
            if (pafRowMinZ[iRow] < dfNewMinZ)
366
0
            {
367
0
                dfNewMinZ = pafRowMinZ[iRow];
368
0
                nMinZRow = iRow;
369
0
            }
370
0
        }
371
372
0
        if (dfNewMinZ != dfMinZ)
373
0
        {
374
0
            dfMinZ = dfNewMinZ;
375
0
            bHeaderNeedsUpdate = true;
376
0
        }
377
0
    }
378
379
0
    if (nMaxZRow == nBlockYOff && pafRowMaxZ[nBlockYOff] < dfMaxZ)
380
0
    {
381
0
        double dfNewMaxZ = std::numeric_limits<double>::lowest();
382
0
        for (int iRow = 0; iRow < nRasterYSize; iRow++)
383
0
        {
384
0
            if (pafRowMaxZ[iRow] > dfNewMaxZ)
385
0
            {
386
0
                dfNewMaxZ = pafRowMaxZ[iRow];
387
0
                nMaxZRow = iRow;
388
0
            }
389
0
        }
390
391
0
        if (dfNewMaxZ != dfMaxZ)
392
0
        {
393
0
            dfMaxZ = dfNewMaxZ;
394
0
            bHeaderNeedsUpdate = true;
395
0
        }
396
0
    }
397
398
0
    if (pafRowMinZ[nBlockYOff] < dfMinZ || pafRowMaxZ[nBlockYOff] > dfMaxZ)
399
0
    {
400
0
        if (pafRowMinZ[nBlockYOff] < dfMinZ)
401
0
        {
402
0
            dfMinZ = pafRowMinZ[nBlockYOff];
403
0
            nMinZRow = nBlockYOff;
404
0
        }
405
406
0
        if (pafRowMaxZ[nBlockYOff] > dfMaxZ)
407
0
        {
408
0
            dfMaxZ = pafRowMaxZ[nBlockYOff];
409
0
            nMaxZRow = nBlockYOff;
410
0
        }
411
412
0
        bHeaderNeedsUpdate = true;
413
0
    }
414
415
0
    if (bHeaderNeedsUpdate && dfMaxZ > dfMinZ)
416
0
    {
417
0
        CPLErr eErr =
418
0
            poGDS->WriteHeader(poGDS->fp, nRasterXSize, nRasterYSize, dfMinX,
419
0
                               dfMaxX, dfMinY, dfMaxY, dfMinZ, dfMaxZ);
420
0
        return eErr;
421
0
    }
422
423
0
    return CE_None;
424
0
}
425
426
/************************************************************************/
427
/*                           GetNoDataValue()                           */
428
/************************************************************************/
429
430
double GS7BGRasterBand::GetNoDataValue(int *pbSuccess)
431
0
{
432
0
    GS7BGDataset *poGDS = reinterpret_cast<GS7BGDataset *>(poDS);
433
0
    if (pbSuccess)
434
0
        *pbSuccess = TRUE;
435
436
0
    return poGDS->dfNoData_Value;
437
0
}
438
439
/************************************************************************/
440
/*                             GetMinimum()                             */
441
/************************************************************************/
442
443
double GS7BGRasterBand::GetMinimum(int *pbSuccess)
444
0
{
445
0
    if (pbSuccess)
446
0
        *pbSuccess = TRUE;
447
448
0
    return dfMinZ;
449
0
}
450
451
/************************************************************************/
452
/*                             GetMaximum()                             */
453
/************************************************************************/
454
455
double GS7BGRasterBand::GetMaximum(int *pbSuccess)
456
0
{
457
0
    if (pbSuccess)
458
0
        *pbSuccess = TRUE;
459
460
0
    return dfMaxZ;
461
0
}
462
463
/************************************************************************/
464
/* ==================================================================== */
465
/*                            GS7BGDataset                              */
466
/* ==================================================================== */
467
/************************************************************************/
468
469
GS7BGDataset::~GS7BGDataset()
470
471
0
{
472
0
    FlushCache(true);
473
0
    if (fp != nullptr)
474
0
        VSIFCloseL(fp);
475
0
}
476
477
/************************************************************************/
478
/*                              Identify()                              */
479
/************************************************************************/
480
481
int GS7BGDataset::Identify(GDALOpenInfo *poOpenInfo)
482
483
475k
{
484
    /* Check for signature - for GS7BG the signature is the */
485
    /* nHEADER_TAG with reverse byte order.                 */
486
475k
    if (poOpenInfo->nHeaderBytes < 4 ||
487
475k
        !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "DSRB"))
488
475k
    {
489
475k
        return FALSE;
490
475k
    }
491
492
0
    return TRUE;
493
475k
}
494
495
/************************************************************************/
496
/*                                Open()                                */
497
/************************************************************************/
498
499
GDALDataset *GS7BGDataset::Open(GDALOpenInfo *poOpenInfo)
500
501
0
{
502
0
    if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
503
0
    {
504
0
        return nullptr;
505
0
    }
506
507
    /* ------------------------------------------------------------------- */
508
    /*      Create a corresponding GDALDataset.                            */
509
    /* ------------------------------------------------------------------- */
510
0
    GS7BGDataset *poDS = new GS7BGDataset();
511
0
    poDS->eAccess = poOpenInfo->eAccess;
512
0
    poDS->fp = poOpenInfo->fpL;
513
0
    poOpenInfo->fpL = nullptr;
514
515
    /* ------------------------------------------------------------------- */
516
    /*      Read the header. The Header section must be the first section  */
517
    /*      in the file.                                                   */
518
    /* ------------------------------------------------------------------- */
519
0
    if (VSIFSeekL(poDS->fp, 0, SEEK_SET) != 0)
520
0
    {
521
0
        delete poDS;
522
0
        CPLError(CE_Failure, CPLE_FileIO,
523
0
                 "Unable to seek to start of grid file header.\n");
524
0
        return nullptr;
525
0
    }
526
527
0
    GInt32 nTag;
528
0
    if (VSIFReadL((void *)&nTag, sizeof(GInt32), 1, poDS->fp) != 1)
529
0
    {
530
0
        delete poDS;
531
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read Tag.\n");
532
0
        return nullptr;
533
0
    }
534
535
0
    CPL_LSBPTR32(&nTag);
536
537
0
    if (nTag != nHEADER_TAG)
538
0
    {
539
0
        delete poDS;
540
0
        CPLError(CE_Failure, CPLE_FileIO, "Header tag not found.\n");
541
0
        return nullptr;
542
0
    }
543
544
0
    GUInt32 nSize;
545
0
    if (VSIFReadL((void *)&nSize, sizeof(GUInt32), 1, poDS->fp) != 1)
546
0
    {
547
0
        delete poDS;
548
0
        CPLError(CE_Failure, CPLE_FileIO,
549
0
                 "Unable to read file section size.\n");
550
0
        return nullptr;
551
0
    }
552
553
0
    CPL_LSBPTR32(&nSize);
554
555
0
    GInt32 nVersion;
556
0
    if (VSIFReadL((void *)&nVersion, sizeof(GInt32), 1, poDS->fp) != 1)
557
0
    {
558
0
        delete poDS;
559
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read file version.\n");
560
0
        return nullptr;
561
0
    }
562
563
0
    CPL_LSBPTR32(&nVersion);
564
565
0
    if (nVersion != 1 && nVersion != 2)
566
0
    {
567
0
        delete poDS;
568
0
        CPLError(CE_Failure, CPLE_FileIO, "Incorrect file version (%d).",
569
0
                 nVersion);
570
0
        return nullptr;
571
0
    }
572
573
    // advance until the grid tag is found
574
0
    while (nTag != nGRID_TAG)
575
0
    {
576
0
        if (VSIFReadL((void *)&nTag, sizeof(GInt32), 1, poDS->fp) != 1)
577
0
        {
578
0
            delete poDS;
579
0
            CPLError(CE_Failure, CPLE_FileIO, "Unable to read Tag.\n");
580
0
            return nullptr;
581
0
        }
582
583
0
        CPL_LSBPTR32(&nTag);
584
585
0
        if (VSIFReadL((void *)&nSize, sizeof(GUInt32), 1, poDS->fp) != 1)
586
0
        {
587
0
            delete poDS;
588
0
            CPLError(CE_Failure, CPLE_FileIO,
589
0
                     "Unable to read file section size.\n");
590
0
            return nullptr;
591
0
        }
592
593
0
        CPL_LSBPTR32(&nSize);
594
595
0
        if (nTag != nGRID_TAG)
596
0
        {
597
0
            if (VSIFSeekL(poDS->fp, nSize, SEEK_CUR) != 0)
598
0
            {
599
0
                delete poDS;
600
0
                CPLError(CE_Failure, CPLE_FileIO,
601
0
                         "Unable to seek to end of file section.\n");
602
0
                return nullptr;
603
0
            }
604
0
        }
605
0
    }
606
607
    /* --------------------------------------------------------------------*/
608
    /*      Read the grid.                                                 */
609
    /* --------------------------------------------------------------------*/
610
    /* Parse number of Y axis grid rows */
611
0
    GInt32 nRows;
612
0
    if (VSIFReadL((void *)&nRows, sizeof(GInt32), 1, poDS->fp) != 1)
613
0
    {
614
0
        delete poDS;
615
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster Y size.\n");
616
0
        return nullptr;
617
0
    }
618
0
    CPL_LSBPTR32(&nRows);
619
0
    poDS->nRasterYSize = nRows;
620
621
    /* Parse number of X axis grid columns */
622
0
    GInt32 nCols;
623
0
    if (VSIFReadL((void *)&nCols, sizeof(GInt32), 1, poDS->fp) != 1)
624
0
    {
625
0
        delete poDS;
626
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read raster X size.\n");
627
0
        return nullptr;
628
0
    }
629
0
    CPL_LSBPTR32(&nCols);
630
0
    poDS->nRasterXSize = nCols;
631
632
0
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
633
0
    {
634
0
        delete poDS;
635
0
        return nullptr;
636
0
    }
637
638
    /* --------------------------------------------------------------------*/
639
    /*      Create band information objects.                               */
640
    /* --------------------------------------------------------------------*/
641
0
    GS7BGRasterBand *poBand = new GS7BGRasterBand(poDS, 1);
642
0
    poDS->SetBand(1, poBand);
643
644
    // find the min X Value of the grid
645
0
    double dfTemp;
646
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
647
0
    {
648
0
        delete poDS;
649
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.\n");
650
0
        return nullptr;
651
0
    }
652
0
    CPL_LSBPTR64(&dfTemp);
653
0
    poBand->dfMinX = dfTemp;
654
655
    // find the min Y value of the grid
656
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
657
0
    {
658
0
        delete poDS;
659
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read minimum X value.\n");
660
0
        return nullptr;
661
0
    }
662
0
    CPL_LSBPTR64(&dfTemp);
663
0
    poBand->dfMinY = dfTemp;
664
665
    // find the spacing between adjacent nodes in the X direction
666
    // (between columns)
667
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
668
0
    {
669
0
        delete poDS;
670
0
        CPLError(CE_Failure, CPLE_FileIO,
671
0
                 "Unable to read spacing in X value.\n");
672
0
        return nullptr;
673
0
    }
674
0
    CPL_LSBPTR64(&dfTemp);
675
0
    poBand->dfMaxX = poBand->dfMinX + (dfTemp * (nCols - 1));
676
677
    // find the spacing between adjacent nodes in the Y direction
678
    // (between rows)
679
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
680
0
    {
681
0
        delete poDS;
682
0
        CPLError(CE_Failure, CPLE_FileIO,
683
0
                 "Unable to read spacing in Y value.\n");
684
0
        return nullptr;
685
0
    }
686
0
    CPL_LSBPTR64(&dfTemp);
687
0
    poBand->dfMaxY = poBand->dfMinY + (dfTemp * (nRows - 1));
688
689
    // set the z min
690
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
691
0
    {
692
0
        delete poDS;
693
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read Z min value.\n");
694
0
        return nullptr;
695
0
    }
696
0
    CPL_LSBPTR64(&dfTemp);
697
0
    poBand->dfMinZ = dfTemp;
698
699
    // set the z max
700
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
701
0
    {
702
0
        delete poDS;
703
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read Z max value.\n");
704
0
        return nullptr;
705
0
    }
706
0
    CPL_LSBPTR64(&dfTemp);
707
0
    poBand->dfMaxZ = dfTemp;
708
709
    // read and ignore the rotation value
710
    //(This is not used in the current version).
711
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
712
0
    {
713
0
        delete poDS;
714
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read rotation value.\n");
715
0
        return nullptr;
716
0
    }
717
718
    // read and set the cell blank value
719
0
    if (VSIFReadL((void *)&dfTemp, sizeof(double), 1, poDS->fp) != 1)
720
0
    {
721
0
        delete poDS;
722
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to Blank value.\n");
723
0
        return nullptr;
724
0
    }
725
0
    CPL_LSBPTR64(&dfTemp);
726
0
    poDS->dfNoData_Value = dfTemp;
727
728
    /* --------------------------------------------------------------------*/
729
    /*      Set the current offset of the grid data.                       */
730
    /* --------------------------------------------------------------------*/
731
0
    if (VSIFReadL((void *)&nTag, sizeof(GInt32), 1, poDS->fp) != 1)
732
0
    {
733
0
        delete poDS;
734
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to read Tag.\n");
735
0
        return nullptr;
736
0
    }
737
738
0
    CPL_LSBPTR32(&nTag);
739
0
    if (nTag != nDATA_TAG)
740
0
    {
741
0
        delete poDS;
742
0
        CPLError(CE_Failure, CPLE_FileIO, "Data tag not found.\n");
743
0
        return nullptr;
744
0
    }
745
746
0
    if (VSIFReadL((void *)&nSize, sizeof(GInt32), 1, poDS->fp) != 1)
747
0
    {
748
0
        delete poDS;
749
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to data section size.\n");
750
0
        return nullptr;
751
0
    }
752
753
0
    poDS->nData_Position = (size_t)VSIFTellL(poDS->fp);
754
755
    /* --------------------------------------------------------------------*/
756
    /*      Initialize any PAM information.                                */
757
    /* --------------------------------------------------------------------*/
758
0
    poDS->SetDescription(poOpenInfo->pszFilename);
759
0
    poDS->TryLoadXML();
760
761
    /* -------------------------------------------------------------------- */
762
    /*      Check for external overviews.                                   */
763
    /* -------------------------------------------------------------------- */
764
0
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
765
0
                                poOpenInfo->GetSiblingFiles());
766
767
0
    return poDS;
768
0
}
769
770
/************************************************************************/
771
/*                          GetGeoTransform()                           */
772
/************************************************************************/
773
774
CPLErr GS7BGDataset::GetGeoTransform(double *padfGeoTransform)
775
0
{
776
0
    if (padfGeoTransform == nullptr)
777
0
        return CE_Failure;
778
779
0
    GS7BGRasterBand *poGRB =
780
0
        cpl::down_cast<GS7BGRasterBand *>(GetRasterBand(1));
781
782
0
    if (poGRB == nullptr)
783
0
    {
784
0
        padfGeoTransform[0] = 0;
785
0
        padfGeoTransform[1] = 1;
786
0
        padfGeoTransform[2] = 0;
787
0
        padfGeoTransform[3] = 0;
788
0
        padfGeoTransform[4] = 0;
789
0
        padfGeoTransform[5] = 1;
790
0
        return CE_Failure;
791
0
    }
792
793
    /* check if we have a PAM GeoTransform stored */
794
0
    CPLPushErrorHandler(CPLQuietErrorHandler);
795
0
    CPLErr eErr = GDALPamDataset::GetGeoTransform(padfGeoTransform);
796
0
    CPLPopErrorHandler();
797
798
0
    if (eErr == CE_None)
799
0
        return CE_None;
800
801
0
    if (nRasterXSize == 1 || nRasterYSize == 1)
802
0
        return CE_Failure;
803
804
    /* calculate pixel size first */
805
0
    padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX) / (nRasterXSize - 1);
806
0
    padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY) / (nRasterYSize - 1);
807
808
    /* then calculate image origin */
809
0
    padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2;
810
0
    padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2;
811
812
    /* tilt/rotation does not supported by the GS grids */
813
0
    padfGeoTransform[4] = 0.0;
814
0
    padfGeoTransform[2] = 0.0;
815
816
0
    return CE_None;
817
0
}
818
819
/************************************************************************/
820
/*                          SetGeoTransform()                           */
821
/************************************************************************/
822
823
CPLErr GS7BGDataset::SetGeoTransform(double *padfGeoTransform)
824
0
{
825
0
    if (eAccess == GA_ReadOnly)
826
0
    {
827
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
828
0
                 "Unable to set GeoTransform, dataset opened read only.\n");
829
0
        return CE_Failure;
830
0
    }
831
832
0
    GS7BGRasterBand *poGRB =
833
0
        cpl::down_cast<GS7BGRasterBand *>(GetRasterBand(1));
834
835
0
    if (padfGeoTransform == nullptr)
836
0
        return CE_Failure;
837
838
    /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */
839
    /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0
840
    || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 )
841
    eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform );
842
843
    if( eErr != CE_None )
844
    return eErr;*/
845
846
0
    double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
847
0
    double dfMaxX =
848
0
        padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0];
849
0
    double dfMinY =
850
0
        padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
851
0
    double dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2;
852
853
0
    CPLErr eErr =
854
0
        WriteHeader(fp, poGRB->nRasterXSize, poGRB->nRasterYSize, dfMinX,
855
0
                    dfMaxX, dfMinY, dfMaxY, poGRB->dfMinZ, poGRB->dfMaxZ);
856
857
0
    if (eErr == CE_None)
858
0
    {
859
0
        poGRB->dfMinX = dfMinX;
860
0
        poGRB->dfMaxX = dfMaxX;
861
0
        poGRB->dfMinY = dfMinY;
862
0
        poGRB->dfMaxY = dfMaxY;
863
0
    }
864
865
0
    return eErr;
866
0
}
867
868
/************************************************************************/
869
/*                             WriteHeader()                            */
870
/************************************************************************/
871
872
CPLErr GS7BGDataset::WriteHeader(VSILFILE *fp, GInt32 nXSize, GInt32 nYSize,
873
                                 double dfMinX, double dfMaxX, double dfMinY,
874
                                 double dfMaxY, double dfMinZ, double dfMaxZ)
875
876
0
{
877
0
    if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
878
0
    {
879
0
        CPLError(CE_Failure, CPLE_FileIO,
880
0
                 "Unable to seek to start of grid file.\n");
881
0
        return CE_Failure;
882
0
    }
883
884
0
    GInt32 nTemp = CPL_LSBWORD32(nHEADER_TAG);
885
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
886
0
    {
887
0
        CPLError(CE_Failure, CPLE_FileIO,
888
0
                 "Unable to write header tag to grid file.\n");
889
0
        return CE_Failure;
890
0
    }
891
892
0
    nTemp = CPL_LSBWORD32(sizeof(GInt32));  // Size of version section.
893
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
894
0
    {
895
0
        CPLError(CE_Failure, CPLE_FileIO,
896
0
                 "Unable to write size to grid file.\n");
897
0
        return CE_Failure;
898
0
    }
899
900
0
    nTemp = CPL_LSBWORD32(1);  // Version
901
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
902
0
    {
903
0
        CPLError(CE_Failure, CPLE_FileIO,
904
0
                 "Unable to write size to grid file.\n");
905
0
        return CE_Failure;
906
0
    }
907
908
0
    nTemp = CPL_LSBWORD32(nGRID_TAG);  // Mark start of grid
909
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
910
0
    {
911
0
        CPLError(CE_Failure, CPLE_FileIO,
912
0
                 "Unable to write size to grid file.\n");
913
0
        return CE_Failure;
914
0
    }
915
916
0
    nTemp = CPL_LSBWORD32(72);  // Grid info size (the remainder of the header)
917
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
918
0
    {
919
0
        CPLError(CE_Failure, CPLE_FileIO,
920
0
                 "Unable to write size to grid file.\n");
921
0
        return CE_Failure;
922
0
    }
923
924
0
    nTemp = CPL_LSBWORD32(nYSize);
925
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
926
0
    {
927
0
        CPLError(CE_Failure, CPLE_FileIO,
928
0
                 "Unable to write Y size to grid file.\n");
929
0
        return CE_Failure;
930
0
    }
931
932
0
    nTemp = CPL_LSBWORD32(nXSize);
933
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
934
0
    {
935
0
        CPLError(CE_Failure, CPLE_FileIO,
936
0
                 "Unable to write X size to grid file.\n");
937
0
        return CE_Failure;
938
0
    }
939
940
0
    double dfTemp = dfMinX;
941
0
    CPL_LSBPTR64(&dfTemp);
942
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
943
0
    {
944
0
        CPLError(CE_Failure, CPLE_FileIO,
945
0
                 "Unable to write minimum X value to grid file.\n");
946
0
        return CE_Failure;
947
0
    }
948
949
0
    dfTemp = dfMinY;
950
0
    CPL_LSBPTR64(&dfTemp);
951
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
952
0
    {
953
0
        CPLError(CE_Failure, CPLE_FileIO,
954
0
                 "Unable to write minimum Y value to grid file.\n");
955
0
        return CE_Failure;
956
0
    }
957
958
    // Write node spacing in x direction
959
0
    dfTemp = (dfMaxX - dfMinX) / (nXSize - 1);
960
0
    CPL_LSBPTR64(&dfTemp);
961
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
962
0
    {
963
0
        CPLError(CE_Failure, CPLE_FileIO,
964
0
                 "Unable to write spacing in X value.\n");
965
0
        return CE_Failure;
966
0
    }
967
968
    // Write node spacing in y direction
969
0
    dfTemp = (dfMaxY - dfMinY) / (nYSize - 1);
970
0
    CPL_LSBPTR64(&dfTemp);
971
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
972
0
    {
973
0
        CPLError(CE_Failure, CPLE_FileIO,
974
0
                 "Unable to write spacing in Y value.\n");
975
0
        return CE_Failure;
976
0
    }
977
978
0
    dfTemp = dfMinZ;
979
0
    CPL_LSBPTR64(&dfTemp);
980
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
981
0
    {
982
0
        CPLError(CE_Failure, CPLE_FileIO,
983
0
                 "Unable to write minimum Z value to grid file.\n");
984
0
        return CE_Failure;
985
0
    }
986
987
0
    dfTemp = dfMaxZ;
988
0
    CPL_LSBPTR64(&dfTemp);
989
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
990
0
    {
991
0
        CPLError(CE_Failure, CPLE_FileIO,
992
0
                 "Unable to write maximum Z value to grid file.\n");
993
0
        return CE_Failure;
994
0
    }
995
996
0
    dfTemp = 0;  // Rotation value is zero
997
0
    CPL_LSBPTR64(&dfTemp);
998
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
999
0
    {
1000
0
        CPLError(CE_Failure, CPLE_FileIO,
1001
0
                 "Unable to write rotation value to grid file.\n");
1002
0
        return CE_Failure;
1003
0
    }
1004
1005
0
    dfTemp = dfDefaultNoDataValue;
1006
0
    CPL_LSBPTR64(&dfTemp);
1007
0
    if (VSIFWriteL((void *)&dfTemp, sizeof(double), 1, fp) != 1)
1008
0
    {
1009
0
        CPLError(CE_Failure, CPLE_FileIO,
1010
0
                 "Unable to write cell blank value to grid file.\n");
1011
0
        return CE_Failure;
1012
0
    }
1013
1014
    // Only supports 1 band so go ahead and write band info here
1015
0
    nTemp = CPL_LSBWORD32(nDATA_TAG);  // Mark start of data
1016
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
1017
0
    {
1018
0
        CPLError(CE_Failure, CPLE_FileIO, "Unable to data tag to grid file.\n");
1019
0
        return CE_Failure;
1020
0
    }
1021
1022
0
    int nSize = nXSize * nYSize * (int)sizeof(double);
1023
0
    nTemp = CPL_LSBWORD32(nSize);  // Mark size of data
1024
0
    if (VSIFWriteL((void *)&nTemp, sizeof(GInt32), 1, fp) != 1)
1025
0
    {
1026
0
        CPLError(CE_Failure, CPLE_FileIO,
1027
0
                 "Unable to write data size to grid file.\n");
1028
0
        return CE_Failure;
1029
0
    }
1030
1031
0
    return CE_None;
1032
0
}
1033
1034
/************************************************************************/
1035
/*                               Create()                               */
1036
/************************************************************************/
1037
1038
GDALDataset *GS7BGDataset::Create(const char *pszFilename, int nXSize,
1039
                                  int nYSize, int nBandsIn, GDALDataType eType,
1040
                                  CPL_UNUSED char **papszParamList)
1041
1042
0
{
1043
0
    if (nXSize <= 0 || nYSize <= 0)
1044
0
    {
1045
0
        CPLError(CE_Failure, CPLE_IllegalArg,
1046
0
                 "Unable to create grid, both X and Y size must be "
1047
0
                 "non-negative.\n");
1048
1049
0
        return nullptr;
1050
0
    }
1051
1052
0
    if (eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 &&
1053
0
        eType != GDT_Int16 && eType != GDT_Float64)
1054
0
    {
1055
0
        CPLError(
1056
0
            CE_Failure, CPLE_AppDefined,
1057
0
            "GS7BG Grid only supports Byte, Int16, "
1058
0
            "Uint16, Float32, and Float64 datatypes.  Unable to create with "
1059
0
            "type %s.\n",
1060
0
            GDALGetDataTypeName(eType));
1061
1062
0
        return nullptr;
1063
0
    }
1064
1065
0
    if (nBandsIn > 1)
1066
0
    {
1067
0
        CPLError(CE_Failure, CPLE_NotSupported,
1068
0
                 "Unable to create copy, "
1069
0
                 "format only supports one raster band.\n");
1070
0
        return nullptr;
1071
0
    }
1072
1073
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
1074
1075
0
    if (fp == nullptr)
1076
0
    {
1077
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1078
0
                 "Attempt to create file '%s' failed.\n", pszFilename);
1079
0
        return nullptr;
1080
0
    }
1081
1082
0
    CPLErr eErr =
1083
0
        WriteHeader(fp, nXSize, nYSize, 0.0, nXSize, 0.0, nYSize, 0.0, 0.0);
1084
0
    if (eErr != CE_None)
1085
0
    {
1086
0
        VSIFCloseL(fp);
1087
0
        return nullptr;
1088
0
    }
1089
1090
0
    double dfVal = dfDefaultNoDataValue;
1091
0
    CPL_LSBPTR64(&dfVal);
1092
0
    for (int iRow = 0; iRow < nYSize; iRow++)
1093
0
    {
1094
0
        for (int iCol = 0; iCol < nXSize; iCol++)
1095
0
        {
1096
0
            if (VSIFWriteL((void *)&dfVal, sizeof(double), 1, fp) != 1)
1097
0
            {
1098
0
                VSIFCloseL(fp);
1099
0
                CPLError(CE_Failure, CPLE_FileIO,
1100
0
                         "Unable to write grid cell.  Disk full?\n");
1101
0
                return nullptr;
1102
0
            }
1103
0
        }
1104
0
    }
1105
1106
0
    VSIFCloseL(fp);
1107
1108
0
    return (GDALDataset *)GDALOpen(pszFilename, GA_Update);
1109
0
}
1110
1111
/************************************************************************/
1112
/*                             CreateCopy()                             */
1113
/************************************************************************/
1114
1115
GDALDataset *GS7BGDataset::CreateCopy(const char *pszFilename,
1116
                                      GDALDataset *poSrcDS, int bStrict,
1117
                                      CPL_UNUSED char **papszOptions,
1118
                                      GDALProgressFunc pfnProgress,
1119
                                      void *pProgressData)
1120
0
{
1121
0
    if (pfnProgress == nullptr)
1122
0
        pfnProgress = GDALDummyProgress;
1123
1124
0
    int nBands = poSrcDS->GetRasterCount();
1125
0
    if (nBands == 0)
1126
0
    {
1127
0
        CPLError(CE_Failure, CPLE_NotSupported,
1128
0
                 "Driver does not support source dataset with zero band.\n");
1129
0
        return nullptr;
1130
0
    }
1131
0
    else if (nBands > 1)
1132
0
    {
1133
0
        if (bStrict)
1134
0
        {
1135
0
            CPLError(CE_Failure, CPLE_NotSupported,
1136
0
                     "Unable to create copy, "
1137
0
                     "format only supports one raster band.\n");
1138
0
            return nullptr;
1139
0
        }
1140
0
        else
1141
0
            CPLError(CE_Warning, CPLE_NotSupported,
1142
0
                     "Format only supports one "
1143
0
                     "raster band, first band will be copied.\n");
1144
0
    }
1145
1146
0
    GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
1147
1148
0
    if (!pfnProgress(0.0, nullptr, pProgressData))
1149
0
    {
1150
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated\n");
1151
0
        return nullptr;
1152
0
    }
1153
1154
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
1155
1156
0
    if (fp == nullptr)
1157
0
    {
1158
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1159
0
                 "Attempt to create file '%s' failed.\n", pszFilename);
1160
0
        return nullptr;
1161
0
    }
1162
1163
0
    GInt32 nXSize = poSrcBand->GetXSize();
1164
0
    GInt32 nYSize = poSrcBand->GetYSize();
1165
0
    double adfGeoTransform[6];
1166
1167
0
    poSrcDS->GetGeoTransform(adfGeoTransform);
1168
1169
0
    double dfMinX = adfGeoTransform[0] + adfGeoTransform[1] / 2;
1170
0
    double dfMaxX = adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0];
1171
0
    double dfMinY = adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3];
1172
0
    double dfMaxY = adfGeoTransform[3] + adfGeoTransform[5] / 2;
1173
0
    CPLErr eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY,
1174
0
                              dfMaxY, 0.0, 0.0);
1175
1176
0
    if (eErr != CE_None)
1177
0
    {
1178
0
        VSIFCloseL(fp);
1179
0
        return nullptr;
1180
0
    }
1181
1182
    /* -------------------------------------------------------------------- */
1183
    /*      Copy band data.                                                 */
1184
    /* -------------------------------------------------------------------- */
1185
0
    double *pfData = (double *)VSI_MALLOC2_VERBOSE(nXSize, sizeof(double));
1186
0
    if (pfData == nullptr)
1187
0
    {
1188
0
        VSIFCloseL(fp);
1189
0
        return nullptr;
1190
0
    }
1191
1192
0
    int bSrcHasNDValue;
1193
0
    double dfSrcNoDataValue = poSrcBand->GetNoDataValue(&bSrcHasNDValue);
1194
0
    double dfMinZ = std::numeric_limits<double>::max();
1195
0
    double dfMaxZ = std::numeric_limits<double>::lowest();
1196
0
    for (GInt32 iRow = nYSize - 1; iRow >= 0; iRow--)
1197
0
    {
1198
0
        eErr = poSrcBand->RasterIO(GF_Read, 0, iRow, nXSize, 1, pfData, nXSize,
1199
0
                                   1, GDT_Float64, 0, 0, nullptr);
1200
1201
0
        if (eErr != CE_None)
1202
0
        {
1203
0
            VSIFCloseL(fp);
1204
0
            VSIFree(pfData);
1205
0
            return nullptr;
1206
0
        }
1207
1208
0
        for (int iCol = 0; iCol < nXSize; iCol++)
1209
0
        {
1210
0
            if (bSrcHasNDValue && pfData[iCol] == dfSrcNoDataValue)
1211
0
            {
1212
0
                pfData[iCol] = dfDefaultNoDataValue;
1213
0
            }
1214
0
            else
1215
0
            {
1216
0
                if (pfData[iCol] > dfMaxZ)
1217
0
                    dfMaxZ = pfData[iCol];
1218
1219
0
                if (pfData[iCol] < dfMinZ)
1220
0
                    dfMinZ = pfData[iCol];
1221
0
            }
1222
1223
0
            CPL_LSBPTR64(pfData + iCol);
1224
0
        }
1225
1226
0
        if (VSIFWriteL((void *)pfData, sizeof(double), nXSize, fp) !=
1227
0
            static_cast<unsigned>(nXSize))
1228
0
        {
1229
0
            VSIFCloseL(fp);
1230
0
            VSIFree(pfData);
1231
0
            CPLError(CE_Failure, CPLE_FileIO,
1232
0
                     "Unable to write grid row. Disk full?\n");
1233
0
            return nullptr;
1234
0
        }
1235
1236
0
        if (!pfnProgress(static_cast<double>(nYSize - iRow) / nYSize, nullptr,
1237
0
                         pProgressData))
1238
0
        {
1239
0
            VSIFCloseL(fp);
1240
0
            VSIFree(pfData);
1241
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1242
0
            return nullptr;
1243
0
        }
1244
0
    }
1245
1246
0
    VSIFree(pfData);
1247
1248
    /* write out the min and max values */
1249
0
    eErr = WriteHeader(fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY,
1250
0
                       dfMinZ, dfMaxZ);
1251
1252
0
    if (eErr != CE_None)
1253
0
    {
1254
0
        VSIFCloseL(fp);
1255
0
        return nullptr;
1256
0
    }
1257
1258
0
    VSIFCloseL(fp);
1259
1260
0
    GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen(pszFilename, GA_Update);
1261
0
    if (poDS)
1262
0
    {
1263
0
        poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
1264
0
    }
1265
1266
0
    return poDS;
1267
0
}
1268
1269
/************************************************************************/
1270
/*                          GDALRegister_GS7BG()                        */
1271
/************************************************************************/
1272
void GDALRegister_GS7BG()
1273
1274
2
{
1275
2
    if (GDALGetDriverByName("GS7BG") != nullptr)
1276
0
        return;
1277
1278
2
    GDALDriver *poDriver = new GDALDriver();
1279
1280
2
    poDriver->SetDescription("GS7BG");
1281
2
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1282
2
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1283
2
                              "Golden Software 7 Binary Grid (.grd)");
1284
2
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gs7bg.html");
1285
2
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
1286
2
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1287
2
                              "Byte Int16 UInt16 Float32 Float64");
1288
2
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1289
1290
2
    poDriver->pfnIdentify = GS7BGDataset::Identify;
1291
2
    poDriver->pfnOpen = GS7BGDataset::Open;
1292
2
    poDriver->pfnCreate = GS7BGDataset::Create;
1293
2
    poDriver->pfnCreateCopy = GS7BGDataset::CreateCopy;
1294
1295
2
    GetGDALDriverManager()->RegisterDriver(poDriver);
1296
2
}