• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.47
/frmts/bsb/bsbdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  BSB Reader
4
 * Purpose:  BSBDataset implementation for BSB format.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "bsb_read.h"
15
#include "cpl_string.h"
16
#include "gdal_frmts.h"
17
#include "gdal_pam.h"
18
#include "ogr_spatialref.h"
19

20
#include <cstdlib>
21
#include <algorithm>
22

23
// Disabled as people may worry about the BSB patent
24
// #define BSB_CREATE
25

26
/************************************************************************/
27
/* ==================================================================== */
28
/*                              BSBDataset                              */
29
/* ==================================================================== */
30
/************************************************************************/
31

32
class BSBRasterBand;
33

34
class BSBDataset final : public GDALPamDataset
35
{
36
    int nGCPCount;
37
    GDAL_GCP *pasGCPList;
38
    OGRSpatialReference m_oGCPSRS{};
39

40
    GDALGeoTransform m_gt{};
41
    int bGeoTransformSet;
42

43
    void ScanForGCPs(bool isNos, const char *pszFilename);
44
    void ScanForGCPsNos(const char *pszFilename);
45
    void ScanForGCPsBSB();
46

47
    void ScanForCutline();
48

49
    static int IdentifyInternal(GDALOpenInfo *, bool &isNosOut);
50

51
  public:
52
    BSBDataset();
53
    ~BSBDataset() override;
54

55
    BSBInfo *psInfo;
56

57
    static GDALDataset *Open(GDALOpenInfo *);
58
    static int Identify(GDALOpenInfo *);
59

60
    int GetGCPCount() override;
61
    const OGRSpatialReference *GetSpatialRef() const override;
62
    const GDAL_GCP *GetGCPs() override;
63

64
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
65
    const OGRSpatialReference *GetGCPSpatialRef() const override;
66
};
67

68
/************************************************************************/
69
/* ==================================================================== */
70
/*                            BSBRasterBand                             */
71
/* ==================================================================== */
72
/************************************************************************/
73

74
class BSBRasterBand final : public GDALPamRasterBand
75
{
76
    GDALColorTable oCT;
77

78
  public:
79
    explicit BSBRasterBand(BSBDataset *);
80

81
    CPLErr IReadBlock(int, int, void *) override;
82
    GDALColorTable *GetColorTable() override;
83
    GDALColorInterp GetColorInterpretation() override;
84
};
85

86
/************************************************************************/
87
/*                           BSBRasterBand()                            */
88
/************************************************************************/
89

90
BSBRasterBand::BSBRasterBand(BSBDataset *poDSIn)
13✔
91

92
{
93
    poDS = poDSIn;
13✔
94
    nBand = 1;
13✔
95

96
    eDataType = GDT_Byte;
13✔
97

98
    nBlockXSize = poDS->GetRasterXSize();
13✔
99
    nBlockYSize = 1;
13✔
100

101
    // Note that the first color table entry is dropped, everything is
102
    // shifted down.
103
    for (int i = 0; i < poDSIn->psInfo->nPCTSize - 1; i++)
1,430✔
104
    {
105
        GDALColorEntry oColor = {poDSIn->psInfo->pabyPCT[i * 3 + 0 + 3],
1,417✔
106
                                 poDSIn->psInfo->pabyPCT[i * 3 + 1 + 3],
1,417✔
107
                                 poDSIn->psInfo->pabyPCT[i * 3 + 2 + 3], 255};
1,417✔
108

109
        oCT.SetColorEntry(i, &oColor);
1,417✔
110
    }
111
}
13✔
112

113
/************************************************************************/
114
/*                             IReadBlock()                             */
115
/************************************************************************/
116

117
CPLErr BSBRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
250✔
118
                                 void *pImage)
119
{
120
    BSBDataset *poGDS = cpl::down_cast<BSBDataset *>(poDS);
250✔
121
    GByte *pabyScanline = (GByte *)pImage;
250✔
122

123
    if (BSBReadScanline(poGDS->psInfo, nBlockYOff, pabyScanline))
250✔
124
    {
125
        for (int i = 0; i < nBlockXSize; i++)
12,648✔
126
        {
127
            /* The indices start at 1, except in case of some charts */
128
            /* where there are missing values, which are filled to 0 */
129
            /* by BSBReadScanline */
130
            if (pabyScanline[i] > 0)
12,400✔
131
                pabyScanline[i] -= 1;
12,400✔
132
        }
133

134
        return CE_None;
248✔
135
    }
136

137
    return CE_Failure;
2✔
138
}
139

140
/************************************************************************/
141
/*                           GetColorTable()                            */
142
/************************************************************************/
143

144
GDALColorTable *BSBRasterBand::GetColorTable()
×
145

146
{
147
    return &oCT;
×
148
}
149

150
/************************************************************************/
151
/*                       GetColorInterpretation()                       */
152
/************************************************************************/
153

154
GDALColorInterp BSBRasterBand::GetColorInterpretation()
×
155

156
{
157
    return GCI_PaletteIndex;
×
158
}
159

160
/************************************************************************/
161
/* ==================================================================== */
162
/*                              BSBDataset                              */
163
/* ==================================================================== */
164
/************************************************************************/
165

166
/************************************************************************/
167
/*                           BSBDataset()                               */
168
/************************************************************************/
169

170
BSBDataset::BSBDataset()
13✔
171
    : nGCPCount(0), pasGCPList(nullptr), bGeoTransformSet(FALSE),
172
      psInfo(nullptr)
13✔
173
{
174
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
13✔
175
    m_oGCPSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
13✔
176
}
13✔
177

178
/************************************************************************/
179
/*                            ~BSBDataset()                             */
180
/************************************************************************/
181

182
BSBDataset::~BSBDataset()
26✔
183

184
{
185
    FlushCache(true);
13✔
186

187
    GDALDeinitGCPs(nGCPCount, pasGCPList);
13✔
188
    CPLFree(pasGCPList);
13✔
189

190
    if (psInfo != nullptr)
13✔
191
        BSBClose(psInfo);
13✔
192
}
26✔
193

194
/************************************************************************/
195
/*                          GetGeoTransform()                           */
196
/************************************************************************/
197

198
CPLErr BSBDataset::GetGeoTransform(GDALGeoTransform &gt) const
1✔
199

200
{
201
    gt = m_gt;
1✔
202

203
    if (bGeoTransformSet)
1✔
204
        return CE_None;
1✔
205

206
    return CE_Failure;
×
207
}
208

209
/************************************************************************/
210
/*                          GetSpatialRef()                             */
211
/************************************************************************/
212

213
const OGRSpatialReference *BSBDataset::GetSpatialRef() const
1✔
214

215
{
216
    if (bGeoTransformSet)
1✔
217
        return &m_oGCPSRS;
1✔
218

219
    return nullptr;
×
220
}
221

222
/************************************************************************/
223
/*                     GDALHeuristicDatelineWrap()                      */
224
/************************************************************************/
225

226
static void GDALHeuristicDatelineWrap(int nPointCount, double *padfX)
3✔
227

228
{
229
    if (nPointCount < 2)
3✔
230
        return;
3✔
231

232
    /* -------------------------------------------------------------------- */
233
    /*      Work out what the longitude range will be centering on the      */
234
    /*      prime meridian (-180 to 180) and centering on the dateline      */
235
    /*      (0 to 360).                                                     */
236
    /* -------------------------------------------------------------------- */
237
    /* Following inits are useless but keep GCC happy */
238
    double dfX_PM_Min = 0.0;
3✔
239
    double dfX_PM_Max = 0.0;
3✔
240
    double dfX_Dateline_Min = 0.0;
3✔
241
    double dfX_Dateline_Max = 0.0;
3✔
242

243
    for (int i = 0; i < nPointCount; i++)
110✔
244
    {
245
        double dfX_PM = padfX[i];
107✔
246
        if (dfX_PM > 180)
107✔
247
            dfX_PM -= 360.0;
×
248

249
        double dfX_Dateline = padfX[i];
107✔
250
        if (dfX_Dateline < 0)
107✔
251
            dfX_Dateline += 360.0;
×
252

253
        if (i == 0)
107✔
254
        {
255
            dfX_PM_Min = dfX_PM;
3✔
256
            dfX_PM_Max = dfX_PM;
3✔
257
            dfX_Dateline_Min = dfX_Dateline;
3✔
258
            dfX_Dateline_Max = dfX_Dateline;
3✔
259
        }
260
        else
261
        {
262
            dfX_PM_Min = std::min(dfX_PM_Min, dfX_PM);
104✔
263
            dfX_PM_Max = std::max(dfX_PM_Max, dfX_PM);
104✔
264
            dfX_Dateline_Min = std::min(dfX_Dateline_Min, dfX_Dateline);
104✔
265
            dfX_Dateline_Max = std::max(dfX_Dateline_Max, dfX_Dateline);
104✔
266
        }
267
    }
268

269
    /* -------------------------------------------------------------------- */
270
    /*      Do nothing if the range is always fairly small - no apparent    */
271
    /*      wrapping issues.                                                */
272
    /* -------------------------------------------------------------------- */
273
    if ((dfX_PM_Max - dfX_PM_Min) < 270.0 &&
3✔
274
        (dfX_Dateline_Max - dfX_Dateline_Min) < 270.0)
3✔
275
        return;
3✔
276

277
    /* -------------------------------------------------------------------- */
278
    /*      Do nothing if both approach have a wide range - best not to    */
279
    /*      fiddle if we aren't sure we are improving things.               */
280
    /* -------------------------------------------------------------------- */
281
    if ((dfX_PM_Max - dfX_PM_Min) > 270.0 &&
×
282
        (dfX_Dateline_Max - dfX_Dateline_Min) > 270.0)
×
283
        return;
×
284

285
    /* -------------------------------------------------------------------- */
286
    /*      Pick which way to transform things.                             */
287
    /* -------------------------------------------------------------------- */
288
    bool bUsePMWrap;
289

290
    if ((dfX_PM_Max - dfX_PM_Min) > 270.0 &&
×
291
        (dfX_Dateline_Max - dfX_Dateline_Min) < 270.0)
×
292
        bUsePMWrap = false;
×
293
    else
294
        bUsePMWrap = true;
×
295

296
    /* -------------------------------------------------------------------- */
297
    /*      Apply rewrapping.                                               */
298
    /* -------------------------------------------------------------------- */
299
    for (int i = 0; i < nPointCount; i++)
×
300
    {
301
        if (bUsePMWrap)
×
302
        {
303
            if (padfX[i] > 180)
×
304
                padfX[i] -= 360.0;
×
305
        }
306
        else
307
        {
308
            if (padfX[i] < 0)
×
309
                padfX[i] += 360.0;
×
310
        }
311
    }
312
}
313

314
/************************************************************************/
315
/*                   GDALHeuristicDatelineWrapGCPs()                    */
316
/************************************************************************/
317

318
static void GDALHeuristicDatelineWrapGCPs(int nPointCount, GDAL_GCP *pasGCPList)
3✔
319
{
320
    std::vector<double> oadfX;
6✔
321

322
    oadfX.resize(nPointCount);
3✔
323
    for (int i = 0; i < nPointCount; i++)
110✔
324
        oadfX[i] = pasGCPList[i].dfGCPX;
107✔
325

326
    GDALHeuristicDatelineWrap(nPointCount, &(oadfX[0]));
3✔
327

328
    for (int i = 0; i < nPointCount; i++)
110✔
329
        pasGCPList[i].dfGCPX = oadfX[i];
107✔
330
}
3✔
331

332
/************************************************************************/
333
/*                            ScanForGCPs()                             */
334
/************************************************************************/
335

336
void BSBDataset::ScanForGCPs(bool isNos, const char *pszFilename)
13✔
337

338
{
339
    /* -------------------------------------------------------------------- */
340
    /*      Collect GCPs as appropriate to source.                          */
341
    /* -------------------------------------------------------------------- */
342
    nGCPCount = 0;
13✔
343

344
    if (isNos)
13✔
345
    {
346
        ScanForGCPsNos(pszFilename);
×
347
    }
348
    else
349
    {
350
        ScanForGCPsBSB();
13✔
351
    }
352

353
    /* -------------------------------------------------------------------- */
354
    /*      Apply heuristics to re-wrap GCPs to maintain continuity        */
355
    /*      over the international dateline.                                */
356
    /* -------------------------------------------------------------------- */
357
    if (nGCPCount > 1)
13✔
358
        GDALHeuristicDatelineWrapGCPs(nGCPCount, pasGCPList);
3✔
359

360
    /* -------------------------------------------------------------------- */
361
    /*      Collect coordinate system related parameters from header.       */
362
    /* -------------------------------------------------------------------- */
363
    const char *pszKNP = nullptr;
13✔
364
    const char *pszKNQ = nullptr;
13✔
365

366
    for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
1,765✔
367
    {
368
        if (STARTS_WITH_CI(psInfo->papszHeader[i], "KNP/"))
1,752✔
369
        {
370
            pszKNP = psInfo->papszHeader[i];
13✔
371
            SetMetadataItem("BSB_KNP", pszKNP + 4);
13✔
372
        }
373
        if (STARTS_WITH_CI(psInfo->papszHeader[i], "KNQ/"))
1,752✔
374
        {
375
            pszKNQ = psInfo->papszHeader[i];
3✔
376
            SetMetadataItem("BSB_KNQ", pszKNQ + 4);
3✔
377
        }
378
    }
379

380
    /* -------------------------------------------------------------------- */
381
    /*      Can we derive a reasonable coordinate system definition for     */
382
    /*      this file?  For now we keep it simple, just handling            */
383
    /*      mercator. In the future we should consider others.              */
384
    /* -------------------------------------------------------------------- */
385
    CPLString osUnderlyingSRS;
26✔
386
    if (pszKNP != nullptr)
13✔
387
    {
388
        const char *pszPR = strstr(pszKNP, "PR=");
13✔
389
        const char *pszGD = strstr(pszKNP, "GD=");
13✔
390
        const char *pszGEOGCS = SRS_WKT_WGS84_LAT_LONG;
13✔
391
        CPLString osPP;
26✔
392

393
        // Capture the PP string.
394
        const char *pszValue = strstr(pszKNP, "PP=");
13✔
395
        const char *pszEnd = pszValue ? strstr(pszValue, ",") : nullptr;
13✔
396
        if (pszValue && pszEnd)
13✔
397
            osPP.assign(pszValue + 3, pszEnd - pszValue - 3);
13✔
398

399
        // Look at the datum
400
        if (pszGD == nullptr)
13✔
401
        {
402
            /* no match. We'll default to EPSG:4326 */
403
        }
404
        else if (STARTS_WITH_CI(pszGD, "GD=European 1950"))
13✔
405
        {
406
            pszGEOGCS =
×
407
                "GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID["
408
                "\"International "
409
                "1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],TOWGS84[-87,-"
410
                "98,-121,0,0,0,0],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM["
411
                "\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\","
412
                "0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY["
413
                "\"EPSG\",\"4230\"]]";
414
        }
415

416
        // Look at the projection
417
        if (pszPR == nullptr)
13✔
418
        {
419
            /* no match */
420
        }
421
        else if (STARTS_WITH_CI(pszPR, "PR=MERCATOR") && nGCPCount > 0)
13✔
422
        {
423
            // We somewhat arbitrarily select our first GCPX as our
424
            // central meridian.  This is mostly helpful to ensure
425
            // that regions crossing the dateline will be contiguous
426
            // in mercator.
427
            osUnderlyingSRS.Printf(
1✔
428
                "PROJCS[\"Global "
429
                "Mercator\",%s,PROJECTION[\"Mercator_2SP\"],PARAMETER["
430
                "\"standard_parallel_1\",0],PARAMETER[\"latitude_of_origin\",0]"
431
                ",PARAMETER[\"central_meridian\",%d],PARAMETER[\"false_"
432
                "easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]"
433
                "]",
434
                pszGEOGCS, (int)pasGCPList[0].dfGCPX);
1✔
435
        }
436

437
        else if (STARTS_WITH_CI(pszPR, "PR=TRANSVERSE MERCATOR") &&
13✔
438
                 !osPP.empty())
1✔
439
        {
440

441
            osUnderlyingSRS.Printf(
442
                "PROJCS[\"unnamed\",%s,PROJECTION[\"Transverse_Mercator\"],"
443
                "PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_"
444
                "meridian\",%s],PARAMETER[\"scale_factor\",1],PARAMETER["
445
                "\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT["
446
                "\"Meter\",1]]",
447
                pszGEOGCS, osPP.c_str());
1✔
448
        }
449

450
        else if (STARTS_WITH_CI(pszPR, "PR=UNIVERSAL TRANSVERSE MERCATOR") &&
11✔
451
                 !osPP.empty())
×
452
        {
453
            // This is not *really* UTM unless the central meridian
454
            // matches a zone which it does not in some (most?) maps.
455
            osUnderlyingSRS.Printf(
456
                "PROJCS[\"unnamed\",%s,PROJECTION[\"Transverse_Mercator\"],"
457
                "PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_"
458
                "meridian\",%s],PARAMETER[\"scale_factor\",0.9996],PARAMETER["
459
                "\"false_easting\",500000],PARAMETER[\"false_northing\",0],"
460
                "UNIT[\"Meter\",1]]",
461
                pszGEOGCS, osPP.c_str());
×
462
        }
463

464
        else if (STARTS_WITH_CI(pszPR, "PR=POLYCONIC") && !osPP.empty())
11✔
465
        {
466
            osUnderlyingSRS.Printf(
467
                "PROJCS[\"unnamed\",%s,PROJECTION[\"Polyconic\"],PARAMETER["
468
                "\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%s],"
469
                "PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]"
470
                ",UNIT[\"Meter\",1]]",
471
                pszGEOGCS, osPP.c_str());
×
472
        }
473

474
        else if (STARTS_WITH_CI(pszPR, "PR=LAMBERT CONFORMAL CONIC") &&
23✔
475
                 !osPP.empty() && pszKNQ != nullptr)
11✔
476
        {
477
            CPLString osP2, osP3;
2✔
478

479
            // Capture the KNQ/P2 string.
480
            pszValue = strstr(pszKNQ, "P2=");
1✔
481
            if (pszValue)
1✔
482
                pszEnd = strstr(pszValue, ",");
1✔
483
            if (pszValue && pszEnd)
1✔
484
                osP2.assign(pszValue + 3, pszEnd - pszValue - 3);
1✔
485

486
            // Capture the KNQ/P3 string.
487
            pszValue = strstr(pszKNQ, "P3=");
1✔
488
            if (pszValue)
1✔
489
                pszEnd = strstr(pszValue, ",");
1✔
490
            if (pszValue)
1✔
491
            {
492
                if (pszEnd)
1✔
493
                    osP3.assign(pszValue + 3, pszEnd - pszValue - 3);
1✔
494
                else
495
                    osP3.assign(pszValue + 3);
×
496
            }
497

498
            if (!osP2.empty() && !osP3.empty())
1✔
499
                osUnderlyingSRS.Printf(
500
                    "PROJCS[\"unnamed\",%s,PROJECTION[\"Lambert_Conformal_"
501
                    "Conic_2SP\"],PARAMETER[\"standard_parallel_1\",%s],"
502
                    "PARAMETER[\"standard_parallel_2\",%s],PARAMETER["
503
                    "\"latitude_of_origin\",0.0],PARAMETER[\"central_"
504
                    "meridian\",%s],PARAMETER[\"false_easting\",0.0],PARAMETER["
505
                    "\"false_northing\",0.0],UNIT[\"Meter\",1]]",
506
                    pszGEOGCS, osP2.c_str(), osP3.c_str(), osPP.c_str());
1✔
507
        }
508
    }
509

510
    /* -------------------------------------------------------------------- */
511
    /*      If we got an alternate underlying coordinate system, try        */
512
    /*      converting the GCPs to that coordinate system.                  */
513
    /* -------------------------------------------------------------------- */
514
    if (osUnderlyingSRS.length() > 0)
13✔
515
    {
516
        OGRSpatialReference oGeog_SRS, oProjected_SRS;
6✔
517

518
        oProjected_SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3✔
519
        oProjected_SRS.SetFromUserInput(osUnderlyingSRS);
3✔
520
        oGeog_SRS.CopyGeogCSFrom(&oProjected_SRS);
3✔
521
        oGeog_SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3✔
522

523
        OGRCoordinateTransformation *poCT =
524
            OGRCreateCoordinateTransformation(&oGeog_SRS, &oProjected_SRS);
3✔
525
        if (poCT != nullptr)
3✔
526
        {
527
            for (int i = 0; i < nGCPCount; i++)
110✔
528
            {
529
                poCT->Transform(1, &(pasGCPList[i].dfGCPX),
107✔
530
                                &(pasGCPList[i].dfGCPY),
107✔
531
                                &(pasGCPList[i].dfGCPZ));
107✔
532
            }
533

534
            m_oGCPSRS.importFromWkt(osUnderlyingSRS.c_str());
3✔
535

536
            delete poCT;
3✔
537
        }
538
        else
539
            CPLErrorReset();
×
540
    }
541

542
    /* -------------------------------------------------------------------- */
543
    /*      Attempt to prepare a geotransform from the GCPs.                */
544
    /* -------------------------------------------------------------------- */
545
    if (GDALGCPsToGeoTransform(nGCPCount, pasGCPList, m_gt.data(), FALSE))
13✔
546
    {
547
        bGeoTransformSet = TRUE;
2✔
548
    }
549
}
13✔
550

551
/************************************************************************/
552
/*                           ScanForGCPsNos()                           */
553
/*                                                                      */
554
/*      Nos files have an accompanying .geo file, that contains some    */
555
/*      of the information normally contained in the header section     */
556
/*      with BSB files. we try and open a file with the same name,      */
557
/*      but a .geo extension, and look for lines like...                */
558
/*      PointX=long lat line pixel    (using the same naming system     */
559
/*      as BSB) Point1=-22.0000 64.250000 197 744                       */
560
/************************************************************************/
561

562
void BSBDataset::ScanForGCPsNos(const char *pszFilename)
×
563
{
564
    const std::string extension = CPLGetExtensionSafe(pszFilename);
×
565

566
    // pseudointelligently try and guess whether we want a .geo or a .GEO
567
    std::string geofile;
×
568
    if (extension.size() >= 2 && extension[1] == 'O')
×
569
    {
570
        geofile = CPLResetExtensionSafe(pszFilename, "GEO");
×
571
    }
572
    else
573
    {
574
        geofile = CPLResetExtensionSafe(pszFilename, "geo");
×
575
    }
576

577
    FILE *gfp = VSIFOpen(geofile.c_str(), "r");  // Text files
×
578
    if (gfp == nullptr)
×
579
    {
580
        CPLError(CE_Failure, CPLE_OpenFailed,
×
581
                 "Couldn't find a matching .GEO file: %s", geofile.c_str());
582
        return;
×
583
    }
584

585
    char *thisLine = (char *)CPLMalloc(80);  // FIXME
×
586

587
    // Count the GCPs (reference points) and seek the file pointer 'gfp' to the
588
    // starting point
589
    int fileGCPCount = 0;
×
590
    while (fgets(thisLine, 80, gfp))
×
591
    {
592
        if (STARTS_WITH_CI(thisLine, "Point"))
×
593
            fileGCPCount++;
×
594
    }
595
    VSIRewind(gfp);
×
596

597
    // Memory has not been allocated to fileGCPCount yet
598
    pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), fileGCPCount + 1);
×
599

600
    while (fgets(thisLine, 80, gfp))
×
601
    {
602
        if (STARTS_WITH_CI(thisLine, "Point"))
×
603
        {
604
            // got a point line, turn it into a gcp
605
            char **Tokens =
606
                CSLTokenizeStringComplex(thisLine, "= ", FALSE, FALSE);
×
607
            if (CSLCount(Tokens) >= 5)
×
608
            {
609
                GDALInitGCPs(1, pasGCPList + nGCPCount);
×
610
                pasGCPList[nGCPCount].dfGCPX = CPLAtof(Tokens[1]);
×
611
                pasGCPList[nGCPCount].dfGCPY = CPLAtof(Tokens[2]);
×
612
                pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(Tokens[4]);
×
613
                pasGCPList[nGCPCount].dfGCPLine = CPLAtof(Tokens[3]);
×
614

615
                CPLFree(pasGCPList[nGCPCount].pszId);
×
616
                char szName[50];
617
                snprintf(szName, sizeof(szName), "GCP_%d", nGCPCount + 1);
×
618
                pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
×
619

620
                nGCPCount++;
×
621
            }
622
            CSLDestroy(Tokens);
×
623
        }
624
    }
625

626
    CPLFree(thisLine);
×
627
    VSIFClose(gfp);
×
628
}
629

630
/************************************************************************/
631
/*                            ScanForGCPsBSB()                          */
632
/************************************************************************/
633

634
void BSBDataset::ScanForGCPsBSB()
13✔
635
{
636
    /* -------------------------------------------------------------------- */
637
    /*      Collect standalone GCPs.  They look like:                       */
638
    /*                                                                      */
639
    /*      REF/1,115,2727,32.346666666667,-60.881666666667                 */
640
    /*      REF/n,pixel,line,lat,long                                       */
641
    /* -------------------------------------------------------------------- */
642
    int fileGCPCount = 0;
13✔
643

644
    // Count the GCPs (reference points) in psInfo->papszHeader
645
    for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
1,765✔
646
        if (STARTS_WITH_CI(psInfo->papszHeader[i], "REF/"))
1,752✔
647
            fileGCPCount++;
107✔
648

649
    // Memory has not been allocated to fileGCPCount yet
650
    pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), fileGCPCount + 1);
13✔
651

652
    for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
1,765✔
653
    {
654

655
        if (!STARTS_WITH_CI(psInfo->papszHeader[i], "REF/"))
1,752✔
656
            continue;
1,645✔
657

658
        char **papszTokens = CSLTokenizeStringComplex(
214✔
659
            psInfo->papszHeader[i] + 4, ",", FALSE, FALSE);
107✔
660

661
        if (CSLCount(papszTokens) > 4)
107✔
662
        {
663
            GDALInitGCPs(1, pasGCPList + nGCPCount);
107✔
664

665
            pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[4]);
107✔
666
            pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[3]);
107✔
667
            pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[1]);
107✔
668
            pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[2]);
107✔
669

670
            CPLFree(pasGCPList[nGCPCount].pszId);
107✔
671
            if (CSLCount(papszTokens) > 5)
107✔
672
            {
673
                pasGCPList[nGCPCount].pszId = CPLStrdup(papszTokens[5]);
×
674
            }
675
            else
676
            {
677
                char szName[50];
678
                snprintf(szName, sizeof(szName), "GCP_%d", nGCPCount + 1);
107✔
679
                pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
107✔
680
            }
681

682
            nGCPCount++;
107✔
683
        }
684
        CSLDestroy(papszTokens);
107✔
685
    }
686
}
13✔
687

688
/************************************************************************/
689
/*                            ScanForCutline()                          */
690
/************************************************************************/
691

692
void BSBDataset::ScanForCutline()
13✔
693
{
694
    /* PLY: Border Polygon Record - coordinates of the panel within the
695
     * raster image, given in chart datum lat/long. Any shape polygon.
696
     * They look like:
697
     *      PLY/1,32.346666666667,-60.881666666667
698
     *      PLY/n,lat,long
699
     *
700
     * If found then we return it via a BSB_CUTLINE metadata item as a WKT
701
     * POLYGON.
702
     */
703

704
    std::string wkt;
26✔
705
    for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
1,765✔
706
    {
707
        if (!STARTS_WITH_CI(psInfo->papszHeader[i], "PLY/"))
1,752✔
708
            continue;
1,731✔
709

710
        const CPLStringList aosTokens(
711
            CSLTokenizeString2(psInfo->papszHeader[i] + 4, ",", 0));
42✔
712

713
        if (aosTokens.size() >= 3)
21✔
714
        {
715
            if (wkt.empty())
21✔
716
                wkt = "POLYGON ((";
2✔
717
            else
718
                wkt += ',';
19✔
719
            wkt += aosTokens[2];
21✔
720
            wkt += ' ';
21✔
721
            wkt += aosTokens[1];
21✔
722
        }
723
    }
724

725
    if (!wkt.empty())
13✔
726
    {
727
        wkt += "))";
2✔
728
        SetMetadataItem("BSB_CUTLINE", wkt.c_str());
2✔
729
    }
730
}
13✔
731

732
/************************************************************************/
733
/*                          IdentifyInternal()                          */
734
/************************************************************************/
735

736
int BSBDataset::IdentifyInternal(GDALOpenInfo *poOpenInfo, bool &isNosOut)
61,018✔
737

738
{
739
    /* -------------------------------------------------------------------- */
740
    /*      Check for BSB/ keyword.                                         */
741
    /* -------------------------------------------------------------------- */
742
    isNosOut = false;
61,018✔
743

744
    if (poOpenInfo->nHeaderBytes < 1000)
61,018✔
745
        return FALSE;
56,787✔
746

747
    int i = 0;
4,231✔
748
    for (; i < poOpenInfo->nHeaderBytes - 4; i++)
5,648,910✔
749
    {
750
        if (poOpenInfo->pabyHeader[i + 0] == 'B' &&
5,644,700✔
751
            poOpenInfo->pabyHeader[i + 1] == 'S' &&
15,824✔
752
            poOpenInfo->pabyHeader[i + 2] == 'B' &&
104✔
753
            poOpenInfo->pabyHeader[i + 3] == '/')
28✔
754
            break;
26✔
755
        if (poOpenInfo->pabyHeader[i + 0] == 'N' &&
5,644,680✔
756
            poOpenInfo->pabyHeader[i + 1] == 'O' &&
16,888✔
757
            poOpenInfo->pabyHeader[i + 2] == 'S' &&
597✔
758
            poOpenInfo->pabyHeader[i + 3] == '/')
12✔
759
        {
760
            isNosOut = true;
×
761
            break;
×
762
        }
763
        if (poOpenInfo->pabyHeader[i + 0] == 'W' &&
5,644,680✔
764
            poOpenInfo->pabyHeader[i + 1] == 'X' &&
5,131✔
765
            poOpenInfo->pabyHeader[i + 2] == '\\' &&
133✔
766
            poOpenInfo->pabyHeader[i + 3] == '8')
×
767
            break;
×
768
    }
769

770
    if (i == poOpenInfo->nHeaderBytes - 4)
4,231✔
771
        return FALSE;
4,205✔
772

773
    /* Additional test to avoid false positive. See #2881 */
774
    const char *pszHeader =
26✔
775
        reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
776
    const char *pszShiftedHeader = pszHeader + i;
26✔
777
    const char *pszRA = strstr(pszShiftedHeader, "RA=");
26✔
778
    if (pszRA == nullptr) /* This may be a NO1 file */
26✔
779
        pszRA = strstr(pszShiftedHeader, "[JF");
×
780
    if (pszRA == nullptr)
26✔
781
        return FALSE;
×
782
    if (pszRA - pszShiftedHeader > 100 && !strstr(pszHeader, "VER/") &&
26✔
783
        !strstr(pszHeader, "KNP/") && !strstr(pszHeader, "KNQ/") &&
×
784
        !strstr(pszHeader, "RGB/"))
×
785
        return FALSE;
×
786

787
    return TRUE;
26✔
788
}
789

790
/************************************************************************/
791
/*                              Identify()                              */
792
/************************************************************************/
793

794
int BSBDataset::Identify(GDALOpenInfo *poOpenInfo)
61,005✔
795

796
{
797
    bool isNos;
798
    return IdentifyInternal(poOpenInfo, isNos);
61,005✔
799
}
800

801
/************************************************************************/
802
/*                                Open()                                */
803
/************************************************************************/
804

805
GDALDataset *BSBDataset::Open(GDALOpenInfo *poOpenInfo)
13✔
806

807
{
808
    bool isNos = false;
13✔
809
    if (!IdentifyInternal(poOpenInfo, isNos))
13✔
810
        return nullptr;
×
811

812
    if (poOpenInfo->eAccess == GA_Update)
13✔
813
    {
814
        ReportUpdateNotSupportedByDriver("BSB");
×
815
        return nullptr;
×
816
    }
817

818
    /* -------------------------------------------------------------------- */
819
    /*      Create a corresponding GDALDataset.                             */
820
    /* -------------------------------------------------------------------- */
821
    BSBDataset *poDS = new BSBDataset();
13✔
822

823
    /* -------------------------------------------------------------------- */
824
    /*      Open the file.                                                  */
825
    /* -------------------------------------------------------------------- */
826
    poDS->psInfo = BSBOpen(poOpenInfo->pszFilename);
13✔
827
    if (poDS->psInfo == nullptr)
13✔
828
    {
829
        delete poDS;
×
830
        return nullptr;
×
831
    }
832

833
    poDS->nRasterXSize = poDS->psInfo->nXSize;
13✔
834
    poDS->nRasterYSize = poDS->psInfo->nYSize;
13✔
835

836
    /* -------------------------------------------------------------------- */
837
    /*      Create band information objects.                                */
838
    /* -------------------------------------------------------------------- */
839
    poDS->SetBand(1, new BSBRasterBand(poDS));
13✔
840

841
    poDS->ScanForGCPs(isNos, poOpenInfo->pszFilename);
13✔
842

843
    /* -------------------------------------------------------------------- */
844
    /*      Set CUTLINE metadata if a bounding polygon is available         */
845
    /* -------------------------------------------------------------------- */
846
    poDS->ScanForCutline();
13✔
847

848
    /* -------------------------------------------------------------------- */
849
    /*      Initialize any PAM information.                                 */
850
    /* -------------------------------------------------------------------- */
851
    poDS->SetDescription(poOpenInfo->pszFilename);
13✔
852
    poDS->TryLoadXML();
13✔
853

854
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
13✔
855

856
    return poDS;
13✔
857
}
858

859
/************************************************************************/
860
/*                            GetGCPCount()                             */
861
/************************************************************************/
862

863
int BSBDataset::GetGCPCount()
2✔
864

865
{
866
    return nGCPCount;
2✔
867
}
868

869
/************************************************************************/
870
/*                          GetGCPSpatialRef()                          */
871
/************************************************************************/
872

873
const OGRSpatialReference *BSBDataset::GetGCPSpatialRef() const
1✔
874

875
{
876
    return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
1✔
877
}
878

879
/************************************************************************/
880
/*                               GetGCP()                               */
881
/************************************************************************/
882

883
const GDAL_GCP *BSBDataset::GetGCPs()
1✔
884

885
{
886
    return pasGCPList;
1✔
887
}
888

889
#ifdef BSB_CREATE
890

891
/************************************************************************/
892
/*                             BSBIsSRSOK()                             */
893
/************************************************************************/
894

895
static int BSBIsSRSOK(const char *pszWKT)
896
{
897
    bool bOK = false;
898
    OGRSpatialReference oSRS, oSRS_WGS84, oSRS_NAD83;
899

900
    if (pszWKT != NULL && pszWKT[0] != '\0')
901
    {
902
        oSRS.importFromWkt(pszWKT);
903

904
        oSRS_WGS84.SetWellKnownGeogCS("WGS84");
905
        oSRS_NAD83.SetWellKnownGeogCS("NAD83");
906
        if ((oSRS.IsSameGeogCS(&oSRS_WGS84) ||
907
             oSRS.IsSameGeogCS(&oSRS_NAD83)) &&
908
            oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0)
909
        {
910
            bOK = true;
911
        }
912
    }
913

914
    if (!bOK)
915
    {
916
        CPLError(CE_Warning, CPLE_NotSupported,
917
                 "BSB only supports WGS84 or NAD83 geographic projections.\n");
918
    }
919

920
    return bOK;
921
}
922

923
/************************************************************************/
924
/*                           BSBCreateCopy()                            */
925
/************************************************************************/
926

927
static GDALDataset *BSBCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
928
                                  int bStrict, char ** /*papszOptions*/,
929
                                  GDALProgressFunc /*pfnProgress*/,
930
                                  void * /*pProgressData*/)
931

932
{
933
    /* -------------------------------------------------------------------- */
934
    /*      Some some rudimentary checks                                    */
935
    /* -------------------------------------------------------------------- */
936
    const int nBands = poSrcDS->GetRasterCount();
937
    if (nBands != 1)
938
    {
939
        CPLError(CE_Failure, CPLE_NotSupported,
940
                 "BSB driver only supports one band images.\n");
941

942
        return NULL;
943
    }
944

945
    if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte && bStrict)
946
    {
947
        CPLError(CE_Failure, CPLE_NotSupported,
948
                 "BSB driver doesn't support data type %s. "
949
                 "Only eight bit bands supported.\n",
950
                 GDALGetDataTypeName(
951
                     poSrcDS->GetRasterBand(1)->GetRasterDataType()));
952

953
        return NULL;
954
    }
955

956
    const int nXSize = poSrcDS->GetRasterXSize();
957
    const int nYSize = poSrcDS->GetRasterYSize();
958

959
    /* -------------------------------------------------------------------- */
960
    /*      Open the output file.                                           */
961
    /* -------------------------------------------------------------------- */
962
    BSBInfo *psBSB = BSBCreate(pszFilename, 0, 200, nXSize, nYSize);
963
    if (psBSB == NULL)
964
        return NULL;
965

966
    /* -------------------------------------------------------------------- */
967
    /*      Prepare initial color table.colortable.                         */
968
    /* -------------------------------------------------------------------- */
969
    GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
970
    unsigned char abyPCT[771];
971
    int nPCTSize;
972
    int anRemap[256];
973

974
    abyPCT[0] = 0;
975
    abyPCT[1] = 0;
976
    abyPCT[2] = 0;
977

978
    if (poBand->GetColorTable() == NULL)
979
    {
980
        /* map greyscale down to 63 grey levels. */
981
        for (int iColor = 0; iColor < 256; iColor++)
982
        {
983
            int nOutValue = (int)(iColor / 4.1) + 1;
984

985
            anRemap[iColor] = nOutValue;
986
            abyPCT[nOutValue * 3 + 0] = (unsigned char)iColor;
987
            abyPCT[nOutValue * 3 + 1] = (unsigned char)iColor;
988
            abyPCT[nOutValue * 3 + 2] = (unsigned char)iColor;
989
        }
990
        nPCTSize = 64;
991
    }
992
    else
993
    {
994
        GDALColorTable *poCT = poBand->GetColorTable();
995
        int nColorTableSize = poCT->GetColorEntryCount();
996
        if (nColorTableSize > 255)
997
            nColorTableSize = 255;
998

999
        for (int iColor = 0; iColor < nColorTableSize; iColor++)
1000
        {
1001
            GDALColorEntry sEntry;
1002

1003
            poCT->GetColorEntryAsRGB(iColor, &sEntry);
1004

1005
            anRemap[iColor] = iColor + 1;
1006
            abyPCT[(iColor + 1) * 3 + 0] = (unsigned char)sEntry.c1;
1007
            abyPCT[(iColor + 1) * 3 + 1] = (unsigned char)sEntry.c2;
1008
            abyPCT[(iColor + 1) * 3 + 2] = (unsigned char)sEntry.c3;
1009
        }
1010

1011
        nPCTSize = nColorTableSize + 1;
1012

1013
        // Add entries for pixel values which apparently will not occur.
1014
        for (int iColor = nPCTSize; iColor < 256; iColor++)
1015
            anRemap[iColor] = 1;
1016
    }
1017

1018
    /* -------------------------------------------------------------------- */
1019
    /*      Boil out all duplicate entries.                                 */
1020
    /* -------------------------------------------------------------------- */
1021
    for (int i = 1; i < nPCTSize - 1; i++)
1022
    {
1023
        for (int j = i + 1; j < nPCTSize; j++)
1024
        {
1025
            if (abyPCT[i * 3 + 0] == abyPCT[j * 3 + 0] &&
1026
                abyPCT[i * 3 + 1] == abyPCT[j * 3 + 1] &&
1027
                abyPCT[i * 3 + 2] == abyPCT[j * 3 + 2])
1028
            {
1029
                nPCTSize--;
1030
                abyPCT[j * 3 + 0] = abyPCT[nPCTSize * 3 + 0];
1031
                abyPCT[j * 3 + 1] = abyPCT[nPCTSize * 3 + 1];
1032
                abyPCT[j * 3 + 2] = abyPCT[nPCTSize * 3 + 2];
1033

1034
                for (int k = 0; k < 256; k++)
1035
                {
1036
                    // merge matching entries.
1037
                    if (anRemap[k] == j)
1038
                        anRemap[k] = i;
1039

1040
                    // shift the last PCT entry into the new hole.
1041
                    if (anRemap[k] == nPCTSize)
1042
                        anRemap[k] = j;
1043
                }
1044
            }
1045
        }
1046
    }
1047

1048
    /* -------------------------------------------------------------------- */
1049
    /*      Boil out all duplicate entries.                                 */
1050
    /* -------------------------------------------------------------------- */
1051
    if (nPCTSize > 128)
1052
    {
1053
        CPLError(CE_Warning, CPLE_AppDefined,
1054
                 "Having to merge color table entries to reduce %d real\n"
1055
                 "color table entries down to 127 values.",
1056
                 nPCTSize);
1057
    }
1058

1059
    while (nPCTSize > 128)
1060
    {
1061
        int nBestRange = 768;
1062
        int iBestMatch1 = -1;
1063
        int iBestMatch2 = -1;
1064

1065
        // Find the closest pair of color table entries.
1066

1067
        for (int i = 1; i < nPCTSize - 1; i++)
1068
        {
1069
            for (int j = i + 1; j < nPCTSize; j++)
1070
            {
1071
                int nRange = std::abs(abyPCT[i * 3 + 0] - abyPCT[j * 3 + 0]) +
1072
                             std::abs(abyPCT[i * 3 + 1] - abyPCT[j * 3 + 1]) +
1073
                             std::abs(abyPCT[i * 3 + 2] - abyPCT[j * 3 + 2]);
1074

1075
                if (nRange < nBestRange)
1076
                {
1077
                    iBestMatch1 = i;
1078
                    iBestMatch2 = j;
1079
                    nBestRange = nRange;
1080
                }
1081
            }
1082
        }
1083

1084
        // Merge the second entry into the first.
1085
        nPCTSize--;
1086
        abyPCT[iBestMatch2 * 3 + 0] = abyPCT[nPCTSize * 3 + 0];
1087
        abyPCT[iBestMatch2 * 3 + 1] = abyPCT[nPCTSize * 3 + 1];
1088
        abyPCT[iBestMatch2 * 3 + 2] = abyPCT[nPCTSize * 3 + 2];
1089

1090
        for (int i = 0; i < 256; i++)
1091
        {
1092
            // merge matching entries.
1093
            if (anRemap[i] == iBestMatch2)
1094
                anRemap[i] = iBestMatch1;
1095

1096
            // shift the last PCT entry into the new hole.
1097
            if (anRemap[i] == nPCTSize)
1098
                anRemap[i] = iBestMatch2;
1099
        }
1100
    }
1101

1102
    /* -------------------------------------------------------------------- */
1103
    /*      Write the PCT.                                                  */
1104
    /* -------------------------------------------------------------------- */
1105
    if (!BSBWritePCT(psBSB, nPCTSize, abyPCT))
1106
    {
1107
        BSBClose(psBSB);
1108
        return NULL;
1109
    }
1110

1111
    /* -------------------------------------------------------------------- */
1112
    /*      Write the GCPs.                                                 */
1113
    /* -------------------------------------------------------------------- */
1114
    GDALGeoTransform gt;
1115
    int nGCPCount = poSrcDS->GetGCPCount();
1116
    if (nGCPCount)
1117
    {
1118
        const char *pszGCPProjection = poSrcDS->GetGCPProjection();
1119
        if (BSBIsSRSOK(pszGCPProjection))
1120
        {
1121
            const GDAL_GCP *pasGCPList = poSrcDS->GetGCPs();
1122
            for (int i = 0; i < nGCPCount; i++)
1123
            {
1124
                VSIFPrintfL(psBSB->fp, "REF/%d,%f,%f,%f,%f\n", i + 1,
1125
                            pasGCPList[i].dfGCPPixel, pasGCPList[i].dfGCPLine,
1126
                            pasGCPList[i].dfGCPY, pasGCPList[i].dfGCPX);
1127
            }
1128
        }
1129
    }
1130
    else if (poSrcDS->GetGeoTransform(gt) == CE_None)
1131
    {
1132
        const char *pszProjection = poSrcDS->GetProjectionRef();
1133
        if (BSBIsSRSOK(pszProjection))
1134
        {
1135
            VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 1, 0, 0,
1136
                        gt[3] + 0 * gt[4] + 0 * gt[5],
1137
                        gt[0] + 0 * gt[1] + 0 * gt[2]);
1138
            VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 2, nXSize, 0,
1139
                        gt[3] + nXSize * gt[4] + 0 * gt[5],
1140
                        gt[0] + nXSize * gt[1] + 0 * gt[2]);
1141
            VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 3, nXSize, nYSize,
1142
                        gt[3] + nXSize * gt[4] + nYSize * gt[5],
1143
                        gt[0] + nXSize * gt[1] + nYSize * gt[2]);
1144
            VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 4, 0, nYSize,
1145
                        gt[3] + 0 * gt[4] + nYSize * gt[5],
1146
                        gt[0] + 0 * gt[1] + nYSize * gt[2]);
1147
        }
1148
    }
1149

1150
    /* -------------------------------------------------------------------- */
1151
    /*      Loop over image, copying image data.                            */
1152
    /* -------------------------------------------------------------------- */
1153
    CPLErr eErr = CE_None;
1154

1155
    GByte *pabyScanline = (GByte *)CPLMalloc(nXSize);
1156

1157
    for (int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++)
1158
    {
1159
        eErr =
1160
            poBand->RasterIO(GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize,
1161
                             1, GDT_Byte, nBands, nBands * nXSize, NULL);
1162
        if (eErr == CE_None)
1163
        {
1164
            for (int i = 0; i < nXSize; i++)
1165
                pabyScanline[i] = (GByte)anRemap[pabyScanline[i]];
1166

1167
            if (!BSBWriteScanline(psBSB, pabyScanline))
1168
                eErr = CE_Failure;
1169
        }
1170
    }
1171

1172
    CPLFree(pabyScanline);
1173

1174
    /* -------------------------------------------------------------------- */
1175
    /*      cleanup                                                         */
1176
    /* -------------------------------------------------------------------- */
1177
    BSBClose(psBSB);
1178

1179
    if (eErr != CE_None)
1180
    {
1181
        VSIUnlink(pszFilename);
1182
        return NULL;
1183
    }
1184

1185
    return (GDALDataset *)GDALOpen(pszFilename, GA_ReadOnly);
1186
}
1187
#endif
1188

1189
/************************************************************************/
1190
/*                        GDALRegister_BSB()                            */
1191
/************************************************************************/
1192

1193
void GDALRegister_BSB()
1,911✔
1194

1195
{
1196
    if (GDALGetDriverByName("BSB") != nullptr)
1,911✔
1197
        return;
282✔
1198

1199
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1200

1201
    poDriver->SetDescription("BSB");
1,629✔
1202
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1203
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Maptech BSB Nautical Charts");
1,629✔
1204
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bsb.html");
1,629✔
1205
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1206
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "kap");
1,629✔
1207
#ifdef BSB_CREATE
1208
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
1209
#endif
1210
    poDriver->pfnOpen = BSBDataset::Open;
1,629✔
1211
    poDriver->pfnIdentify = BSBDataset::Identify;
1,629✔
1212
#ifdef BSB_CREATE
1213
    poDriver->pfnCreateCopy = BSBCreateCopy;
1214
#endif
1215

1216
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc