Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/raw/byndataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Natural Resources Canada's Geoid BYN file format
4
 * Purpose:  Implementation of BYN format
5
 * Author:   Ivan Lucena, ivan.lucena@outlook.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Ivan Lucena
9
 * Copyright (c) 2018, Even Rouault
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "byndataset.h"
16
#include "rawdataset.h"
17
18
#include "cpl_string.h"
19
#include "gdal_frmts.h"
20
#include "gdal_priv.h"
21
#include "ogr_spatialref.h"
22
#include "ogr_srs_api.h"
23
24
#include <algorithm>
25
#include <cstdlib>
26
#include <limits>
27
28
// Specification at
29
// https://www.nrcan.gc.ca/sites/www.nrcan.gc.ca/files/earthsciences/pdf/gpshgrid_e.pdf
30
31
const static BYNEllipsoids EllipsoidTable[] = {
32
    {"GRS80", 6378137.0, 298.257222101},
33
    {"WGS84", 6378137.0, 298.257223564},
34
    {"ALT1", 6378136.3, 298.256415099},
35
    {"GRS67", 6378160.0, 298.247167427},
36
    {"ELLIP1", 6378136.46, 298.256415099},
37
    {"ALT2", 6378136.3, 298.257},
38
    {"ELLIP2", 6378136.0, 298.257},
39
    {"CLARKE 1866", 6378206.4, 294.9786982}};
40
41
/************************************************************************/
42
/*                            BYNRasterBand()                           */
43
/************************************************************************/
44
45
BYNRasterBand::BYNRasterBand(GDALDataset *poDSIn, int nBandIn,
46
                             VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
47
                             int nPixelOffsetIn, int nLineOffsetIn,
48
                             GDALDataType eDataTypeIn, int bNativeOrderIn)
49
37
    : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
50
37
                    nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
51
37
                    RawRasterBand::OwnFP::NO)
52
37
{
53
37
}
54
55
/************************************************************************/
56
/*                           ~BYNRasterBand()                           */
57
/************************************************************************/
58
59
BYNRasterBand::~BYNRasterBand()
60
37
{
61
37
}
62
63
/************************************************************************/
64
/*                           GetNoDataValue()                           */
65
/************************************************************************/
66
67
double BYNRasterBand::GetNoDataValue(int *pbSuccess)
68
34.7k
{
69
34.7k
    if (pbSuccess)
70
34.7k
        *pbSuccess = TRUE;
71
34.7k
    int bSuccess = FALSE;
72
34.7k
    double dfNoData = GDALPamRasterBand::GetNoDataValue(&bSuccess);
73
34.7k
    if (bSuccess)
74
0
    {
75
0
        return dfNoData;
76
0
    }
77
34.7k
    const double dfFactor =
78
34.7k
        cpl::down_cast<BYNDataset *>(poDS)->hHeader.dfFactor;
79
34.7k
    return eDataType == GDT_Int16 ? 32767.0 : 9999.0 * dfFactor;
80
34.7k
}
81
82
/************************************************************************/
83
/*                              GetScale()                              */
84
/************************************************************************/
85
86
double BYNRasterBand::GetScale(int *pbSuccess)
87
10.3k
{
88
10.3k
    if (pbSuccess != nullptr)
89
4
        *pbSuccess = TRUE;
90
10.3k
    const double dfFactor =
91
10.3k
        cpl::down_cast<BYNDataset *>(poDS)->hHeader.dfFactor;
92
10.3k
    return (dfFactor != 0.0) ? 1.0 / dfFactor : 0.0;
93
10.3k
}
94
95
/************************************************************************/
96
/* ==================================================================== */
97
/*                              BYNDataset                              */
98
/* ==================================================================== */
99
/************************************************************************/
100
101
BYNDataset::BYNDataset()
102
38
    : fpImage(nullptr), hHeader{0, 0, 0, 0, 0, 0,   0,   0, 0.0, 0,   0, 0,
103
38
                                0, 0, 0, 0, 0, 0.0, 0.0, 0, 0,   0.0, 0}
104
38
{
105
38
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
106
38
}
107
108
/************************************************************************/
109
/*                            ~BYNDataset()                             */
110
/************************************************************************/
111
112
BYNDataset::~BYNDataset()
113
114
38
{
115
38
    BYNDataset::Close();
116
38
}
117
118
/************************************************************************/
119
/*                              Close()                                 */
120
/************************************************************************/
121
122
CPLErr BYNDataset::Close()
123
75
{
124
75
    CPLErr eErr = CE_None;
125
75
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
126
38
    {
127
38
        if (BYNDataset::FlushCache(true) != CE_None)
128
0
            eErr = CE_Failure;
129
130
38
        if (fpImage != nullptr)
131
38
        {
132
38
            if (VSIFCloseL(fpImage) != 0)
133
0
            {
134
0
                eErr = CE_Failure;
135
0
                CPLError(CE_Failure, CPLE_FileIO, "I/O error");
136
0
            }
137
38
        }
138
139
38
        if (GDALPamDataset::Close() != CE_None)
140
0
            eErr = CE_Failure;
141
38
    }
142
75
    return eErr;
143
75
}
144
145
/************************************************************************/
146
/*                              Identify()                              */
147
/************************************************************************/
148
149
int BYNDataset::Identify(GDALOpenInfo *poOpenInfo)
150
151
624k
{
152
624k
    if (poOpenInfo->nHeaderBytes < BYN_HDR_SZ)
153
558k
        return FALSE;
154
155
/* -------------------------------------------------------------------- */
156
/*      Check file extension (.byn/.err)                                */
157
/* -------------------------------------------------------------------- */
158
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
159
    const char *pszFileExtension = poOpenInfo->osExtension.c_str();
160
161
    if (!EQUAL(pszFileExtension, "byn") && !EQUAL(pszFileExtension, "err"))
162
    {
163
        return FALSE;
164
    }
165
#endif
166
167
    /* -------------------------------------------------------------------- */
168
    /*      Check some value's ranges on header                             */
169
    /* -------------------------------------------------------------------- */
170
171
66.1k
    BYNHeader hHeader = {0, 0, 0, 0, 0, 0,   0,   0, 0.0, 0,   0, 0,
172
66.1k
                         0, 0, 0, 0, 0, 0.0, 0.0, 0, 0,   0.0, 0};
173
174
66.1k
    buffer2header(poOpenInfo->pabyHeader, &hHeader);
175
176
66.1k
    if (hHeader.nGlobal < 0 || hHeader.nGlobal > 1 || hHeader.nType < 0 ||
177
3.01k
        hHeader.nType > 9 || (hHeader.nSizeOf != 2 && hHeader.nSizeOf != 4) ||
178
104
        hHeader.nVDatum < 0 || hHeader.nVDatum > 3 || hHeader.nDescrip < 0 ||
179
98
        hHeader.nDescrip > 3 || hHeader.nSubType < 0 || hHeader.nSubType > 9 ||
180
93
        hHeader.nDatum < 0 || hHeader.nDatum > 1 || hHeader.nEllipsoid < 0 ||
181
92
        hHeader.nEllipsoid > 7 || hHeader.nByteOrder < 0 ||
182
90
        hHeader.nByteOrder > 1 || hHeader.nScale < 0 || hHeader.nScale > 1)
183
66.0k
        return FALSE;
184
185
#if 0
186
    // We have disabled those checks as invalid values are often found in some
187
    // datasets, such as http://s3.microsurvey.com/os/fieldgenius/geoids/Lithuania.zip
188
    // We don't use those fields, so we may just ignore them.
189
    if((hHeader.nTideSys   < 0 || hHeader.nTideSys   > 2 ||
190
        hHeader.nPtType    < 0 || hHeader.nPtType    > 1 ))
191
    {
192
        // Some datasets use 0xCC as a marker for invalidity for
193
        // records starting from Geopotential Wo
194
        for( int i = 52; i < 78; i++ )
195
        {
196
            if( poOpenInfo->pabyHeader[i] != 0xCC )
197
                return FALSE;
198
        }
199
    }
200
#endif
201
202
87
    if (hHeader.nScale == 0)
203
87
    {
204
87
        if ((std::abs(static_cast<GIntBig>(hHeader.nSouth) -
205
87
                      (hHeader.nDLat / 2)) > BYN_MAX_LAT) ||
206
76
            (std::abs(static_cast<GIntBig>(hHeader.nNorth) +
207
76
                      (hHeader.nDLat / 2)) > BYN_MAX_LAT) ||
208
76
            (std::abs(static_cast<GIntBig>(hHeader.nWest) -
209
76
                      (hHeader.nDLon / 2)) > BYN_MAX_LON) ||
210
76
            (std::abs(static_cast<GIntBig>(hHeader.nEast) +
211
76
                      (hHeader.nDLon / 2)) > BYN_MAX_LON))
212
11
            return FALSE;
213
87
    }
214
0
    else
215
0
    {
216
0
        if ((std::abs(static_cast<GIntBig>(hHeader.nSouth) -
217
0
                      (hHeader.nDLat / 2)) > BYN_MAX_LAT_SCL) ||
218
0
            (std::abs(static_cast<GIntBig>(hHeader.nNorth) +
219
0
                      (hHeader.nDLat / 2)) > BYN_MAX_LAT_SCL) ||
220
0
            (std::abs(static_cast<GIntBig>(hHeader.nWest) -
221
0
                      (hHeader.nDLon / 2)) > BYN_MAX_LON_SCL) ||
222
0
            (std::abs(static_cast<GIntBig>(hHeader.nEast) +
223
0
                      (hHeader.nDLon / 2)) > BYN_MAX_LON_SCL))
224
0
            return FALSE;
225
0
    }
226
227
76
    return TRUE;
228
87
}
229
230
/************************************************************************/
231
/*                                Open()                                */
232
/************************************************************************/
233
234
GDALDataset *BYNDataset::Open(GDALOpenInfo *poOpenInfo)
235
236
38
{
237
38
    if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr ||
238
38
        poOpenInfo->eAccess == GA_Update)
239
0
        return nullptr;
240
241
    /* -------------------------------------------------------------------- */
242
    /*      Create a corresponding GDALDataset.                             */
243
    /* -------------------------------------------------------------------- */
244
245
38
    auto poDS = std::make_unique<BYNDataset>();
246
247
38
    poDS->eAccess = poOpenInfo->eAccess;
248
38
    std::swap(poDS->fpImage, poOpenInfo->fpL);
249
250
    /* -------------------------------------------------------------------- */
251
    /*      Read the header.                                                */
252
    /* -------------------------------------------------------------------- */
253
254
38
    buffer2header(poOpenInfo->pabyHeader, &poDS->hHeader);
255
256
    /********************************/
257
    /* Scale boundaries and spacing */
258
    /********************************/
259
260
38
    double dfSouth = poDS->hHeader.nSouth;
261
38
    double dfNorth = poDS->hHeader.nNorth;
262
38
    double dfWest = poDS->hHeader.nWest;
263
38
    double dfEast = poDS->hHeader.nEast;
264
38
    double dfDLat = poDS->hHeader.nDLat;
265
38
    double dfDLon = poDS->hHeader.nDLon;
266
267
38
    if (poDS->hHeader.nScale == 1)
268
0
    {
269
0
        dfSouth *= BYN_SCALE;
270
0
        dfNorth *= BYN_SCALE;
271
0
        dfWest *= BYN_SCALE;
272
0
        dfEast *= BYN_SCALE;
273
0
        dfDLat *= BYN_SCALE;
274
0
        dfDLon *= BYN_SCALE;
275
0
    }
276
277
    /******************************/
278
    /* Calculate rows and columns */
279
    /******************************/
280
281
38
    double dfXSize = -1;
282
38
    double dfYSize = -1;
283
284
38
    poDS->nRasterXSize = -1;
285
38
    poDS->nRasterYSize = -1;
286
287
38
    if (dfDLat != 0.0 && dfDLon != 0.0)
288
37
    {
289
37
        dfXSize = ((dfEast - dfWest + 1.0) / dfDLon) + 1.0;
290
37
        dfYSize = ((dfNorth - dfSouth + 1.0) / dfDLat) + 1.0;
291
37
    }
292
293
38
    if (dfXSize > 0.0 && dfXSize < std::numeric_limits<double>::max() &&
294
37
        dfYSize > 0.0 && dfYSize < std::numeric_limits<double>::max())
295
37
    {
296
37
        poDS->nRasterXSize = static_cast<GInt32>(dfXSize);
297
37
        poDS->nRasterYSize = static_cast<GInt32>(dfYSize);
298
37
    }
299
300
38
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
301
1
    {
302
1
        return nullptr;
303
1
    }
304
305
    /*****************************/
306
    /* Build GeoTransform matrix */
307
    /*****************************/
308
309
37
    poDS->m_gt[0] = (dfWest - (dfDLon / 2.0)) / 3600.0;
310
37
    poDS->m_gt[1] = dfDLon / 3600.0;
311
37
    poDS->m_gt[2] = 0.0;
312
37
    poDS->m_gt[3] = (dfNorth + (dfDLat / 2.0)) / 3600.0;
313
37
    poDS->m_gt[4] = 0.0;
314
37
    poDS->m_gt[5] = -1 * dfDLat / 3600.0;
315
316
    /*********************/
317
    /* Set data type     */
318
    /*********************/
319
320
37
    GDALDataType eDT = GDT_Unknown;
321
322
37
    if (poDS->hHeader.nSizeOf == 2)
323
20
        eDT = GDT_Int16;
324
17
    else if (poDS->hHeader.nSizeOf == 4)
325
17
        eDT = GDT_Int32;
326
0
    else
327
0
    {
328
0
        return nullptr;
329
0
    }
330
331
    /* -------------------------------------------------------------------- */
332
    /*      Create band information object.                                 */
333
    /* -------------------------------------------------------------------- */
334
335
37
    const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
336
337
37
    int bIsLSB = poDS->hHeader.nByteOrder == 1 ? 1 : 0;
338
339
37
    auto poBand = std::make_unique<BYNRasterBand>(
340
37
        poDS.get(), 1, poDS->fpImage, BYN_HDR_SZ, nDTSize,
341
37
        poDS->nRasterXSize * nDTSize, eDT, CPL_IS_LSB == bIsLSB);
342
37
    if (!poBand->IsValid())
343
0
        return nullptr;
344
37
    poDS->SetBand(1, std::move(poBand));
345
346
    /* -------------------------------------------------------------------- */
347
    /*      Initialize any PAM information.                                 */
348
    /* -------------------------------------------------------------------- */
349
350
37
    poDS->SetDescription(poOpenInfo->pszFilename);
351
37
    poDS->TryLoadXML();
352
353
    /* -------------------------------------------------------------------- */
354
    /*      Check for overviews.                                            */
355
    /* -------------------------------------------------------------------- */
356
357
37
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
358
359
37
    return poDS.release();
360
37
}
361
362
/************************************************************************/
363
/*                          GetGeoTransform()                           */
364
/************************************************************************/
365
366
CPLErr BYNDataset::GetGeoTransform(GDALGeoTransform &gt) const
367
37
{
368
37
    gt = m_gt;
369
37
    return CE_None;
370
37
}
371
372
/************************************************************************/
373
/*                          GetSpatialRef()                             */
374
/************************************************************************/
375
376
const OGRSpatialReference *BYNDataset::GetSpatialRef() const
377
378
35
{
379
35
    if (!m_oSRS.IsEmpty())
380
0
        return &m_oSRS;
381
382
    /* Try to use a prefefined EPSG compound CS */
383
384
35
    if (hHeader.nDatum == 1 && hHeader.nVDatum == 2)
385
0
    {
386
0
        m_oSRS.importFromEPSG(BYN_DATUM_1_VDATUM_2);
387
0
        return &m_oSRS;
388
0
    }
389
390
    /* Build the GEOGCS based on Datum ( or Ellipsoid )*/
391
392
35
    bool bNoGeogCS = false;
393
394
35
    if (hHeader.nDatum == 0)
395
12
        m_oSRS.importFromEPSG(BYN_DATUM_0);
396
23
    else if (hHeader.nDatum == 1)
397
23
        m_oSRS.importFromEPSG(BYN_DATUM_1);
398
0
    else
399
0
    {
400
        /* Build GEOGCS based on Ellipsoid (Table 3) */
401
402
0
        if (hHeader.nEllipsoid > -1 &&
403
0
            hHeader.nEllipsoid <
404
0
                static_cast<GInt16>(CPL_ARRAYSIZE(EllipsoidTable)))
405
0
            m_oSRS.SetGeogCS(
406
0
                CPLSPrintf("BYN Ellipsoid(%d)", hHeader.nEllipsoid),
407
0
                "Unspecified", EllipsoidTable[hHeader.nEllipsoid].pszName,
408
0
                EllipsoidTable[hHeader.nEllipsoid].dfSemiMajor,
409
0
                EllipsoidTable[hHeader.nEllipsoid].dfInvFlattening);
410
0
        else
411
0
            bNoGeogCS = true;
412
0
    }
413
414
    /* Build the VERT_CS based on VDatum */
415
416
35
    OGRSpatialReference oSRSComp;
417
35
    OGRSpatialReference oSRSVert;
418
419
35
    int nVertCS = 0;
420
421
35
    if (hHeader.nVDatum == 1)
422
24
        nVertCS = BYN_VDATUM_1;
423
11
    else if (hHeader.nVDatum == 2)
424
4
        nVertCS = BYN_VDATUM_2;
425
7
    else if (hHeader.nVDatum == 3)
426
0
        nVertCS = BYN_VDATUM_3;
427
7
    else
428
7
    {
429
        /* Return GEOGCS ( .err files ) */
430
431
7
        if (bNoGeogCS)
432
0
            return nullptr;
433
434
7
        return &m_oSRS;
435
7
    }
436
437
28
    oSRSVert.importFromEPSG(nVertCS);
438
439
    /* Create CPMPD_CS with GEOGCS and VERT_CS */
440
441
28
    if (oSRSComp.SetCompoundCS(CPLSPrintf("BYN Datum(%d) & VDatum(%d)",
442
28
                                          hHeader.nDatum, hHeader.nDatum),
443
28
                               &m_oSRS, &oSRSVert) == CE_None)
444
28
    {
445
        /* Return COMPD_CS with GEOGCS and VERT_CS */
446
447
28
        m_oSRS = std::move(oSRSComp);
448
28
        m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
449
28
        return &m_oSRS;
450
28
    }
451
452
0
    return nullptr;
453
28
}
454
455
/*----------------------------------------------------------------------*/
456
/*                           buffer2header()                            */
457
/*----------------------------------------------------------------------*/
458
459
void BYNDataset::buffer2header(const GByte *pabyBuf, BYNHeader *pohHeader)
460
461
66.1k
{
462
66.1k
    memcpy(&pohHeader->nSouth, pabyBuf, 4);
463
66.1k
    memcpy(&pohHeader->nNorth, pabyBuf + 4, 4);
464
66.1k
    memcpy(&pohHeader->nWest, pabyBuf + 8, 4);
465
66.1k
    memcpy(&pohHeader->nEast, pabyBuf + 12, 4);
466
66.1k
    memcpy(&pohHeader->nDLat, pabyBuf + 16, 2);
467
66.1k
    memcpy(&pohHeader->nDLon, pabyBuf + 18, 2);
468
66.1k
    memcpy(&pohHeader->nGlobal, pabyBuf + 20, 2);
469
66.1k
    memcpy(&pohHeader->nType, pabyBuf + 22, 2);
470
66.1k
    memcpy(&pohHeader->dfFactor, pabyBuf + 24, 8);
471
66.1k
    memcpy(&pohHeader->nSizeOf, pabyBuf + 32, 2);
472
66.1k
    memcpy(&pohHeader->nVDatum, pabyBuf + 34, 2);
473
66.1k
    memcpy(&pohHeader->nDescrip, pabyBuf + 40, 2);
474
66.1k
    memcpy(&pohHeader->nSubType, pabyBuf + 42, 2);
475
66.1k
    memcpy(&pohHeader->nDatum, pabyBuf + 44, 2);
476
66.1k
    memcpy(&pohHeader->nEllipsoid, pabyBuf + 46, 2);
477
66.1k
    memcpy(&pohHeader->nByteOrder, pabyBuf + 48, 2);
478
66.1k
    memcpy(&pohHeader->nScale, pabyBuf + 50, 2);
479
66.1k
    memcpy(&pohHeader->dfWo, pabyBuf + 52, 8);
480
66.1k
    memcpy(&pohHeader->dfGM, pabyBuf + 60, 8);
481
66.1k
    memcpy(&pohHeader->nTideSys, pabyBuf + 68, 2);
482
66.1k
    memcpy(&pohHeader->nRealiz, pabyBuf + 70, 2);
483
66.1k
    memcpy(&pohHeader->dEpoch, pabyBuf + 72, 4);
484
66.1k
    memcpy(&pohHeader->nPtType, pabyBuf + 76, 2);
485
486
#if defined(CPL_MSB)
487
    CPL_LSBPTR32(&pohHeader->nSouth);
488
    CPL_LSBPTR32(&pohHeader->nNorth);
489
    CPL_LSBPTR32(&pohHeader->nWest);
490
    CPL_LSBPTR32(&pohHeader->nEast);
491
    CPL_LSBPTR16(&pohHeader->nDLat);
492
    CPL_LSBPTR16(&pohHeader->nDLon);
493
    CPL_LSBPTR16(&pohHeader->nGlobal);
494
    CPL_LSBPTR16(&pohHeader->nType);
495
    CPL_LSBPTR64(&pohHeader->dfFactor);
496
    CPL_LSBPTR16(&pohHeader->nSizeOf);
497
    CPL_LSBPTR16(&pohHeader->nVDatum);
498
    CPL_LSBPTR16(&pohHeader->nDescrip);
499
    CPL_LSBPTR16(&pohHeader->nSubType);
500
    CPL_LSBPTR16(&pohHeader->nDatum);
501
    CPL_LSBPTR16(&pohHeader->nEllipsoid);
502
    CPL_LSBPTR16(&pohHeader->nByteOrder);
503
    CPL_LSBPTR16(&pohHeader->nScale);
504
    CPL_LSBPTR64(&pohHeader->dfWo);
505
    CPL_LSBPTR64(&pohHeader->dfGM);
506
    CPL_LSBPTR16(&pohHeader->nTideSys);
507
    CPL_LSBPTR16(&pohHeader->nRealiz);
508
    CPL_LSBPTR32(&pohHeader->dEpoch);
509
    CPL_LSBPTR16(&pohHeader->nPtType);
510
#endif
511
512
#if DEBUG
513
    CPLDebug("BYN", "South         = %d", pohHeader->nSouth);
514
    CPLDebug("BYN", "North         = %d", pohHeader->nNorth);
515
    CPLDebug("BYN", "West          = %d", pohHeader->nWest);
516
    CPLDebug("BYN", "East          = %d", pohHeader->nEast);
517
    CPLDebug("BYN", "DLat          = %d", pohHeader->nDLat);
518
    CPLDebug("BYN", "DLon          = %d", pohHeader->nDLon);
519
    CPLDebug("BYN", "DGlobal       = %d", pohHeader->nGlobal);
520
    CPLDebug("BYN", "DType         = %d", pohHeader->nType);
521
    CPLDebug("BYN", "Factor        = %f", pohHeader->dfFactor);
522
    CPLDebug("BYN", "SizeOf        = %d", pohHeader->nSizeOf);
523
    CPLDebug("BYN", "VDatum        = %d", pohHeader->nVDatum);
524
    CPLDebug("BYN", "Data          = %d", pohHeader->nDescrip);
525
    CPLDebug("BYN", "SubType       = %d", pohHeader->nSubType);
526
    CPLDebug("BYN", "Datum         = %d", pohHeader->nDatum);
527
    CPLDebug("BYN", "Ellipsoid     = %d", pohHeader->nEllipsoid);
528
    CPLDebug("BYN", "ByteOrder     = %d", pohHeader->nByteOrder);
529
    CPLDebug("BYN", "Scale         = %d", pohHeader->nScale);
530
    CPLDebug("BYN", "Wo            = %f", pohHeader->dfWo);
531
    CPLDebug("BYN", "GM            = %f", pohHeader->dfGM);
532
    CPLDebug("BYN", "TideSystem    = %d", pohHeader->nTideSys);
533
    CPLDebug("BYN", "RefRealzation = %d", pohHeader->nRealiz);
534
    CPLDebug("BYN", "Epoch         = %f", pohHeader->dEpoch);
535
    CPLDebug("BYN", "PtType        = %d", pohHeader->nPtType);
536
#endif
537
66.1k
}
538
539
/************************************************************************/
540
/*                          GDALRegister_BYN()                          */
541
/************************************************************************/
542
543
void GDALRegister_BYN()
544
545
22
{
546
22
    if (GDALGetDriverByName("BYN") != nullptr)
547
0
        return;
548
549
22
    GDALDriver *poDriver = new GDALDriver();
550
551
22
    poDriver->SetDescription("BYN");
552
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
553
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
554
22
                              "Natural Resources Canada's Geoid");
555
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "byn err");
556
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
557
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/byn.html");
558
559
22
    poDriver->pfnOpen = BYNDataset::Open;
560
22
    poDriver->pfnIdentify = BYNDataset::Identify;
561
562
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
563
22
}