• 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

85.76
/frmts/ers/ersdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  ERMapper .ers Driver
4
 * Purpose:  Implementation of .ers driver.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_string.h"
15
#include "ershdrnode.h"
16
#include "gdal_frmts.h"
17
#include "gdal_proxy.h"
18
#include "ogr_spatialref.h"
19
#include "rawdataset.h"
20

21
#include <limits>
22

23
/************************************************************************/
24
/* ==================================================================== */
25
/*                              ERSDataset                              */
26
/* ==================================================================== */
27
/************************************************************************/
28

29
class ERSRasterBand;
30

31
class ERSDataset final : public RawDataset
32
{
33
    friend class ERSRasterBand;
34

35
    VSILFILE *fpImage;  // Image data file.
36
    GDALDataset *poDepFile;
37

38
    int bGotTransform;
39
    GDALGeoTransform m_gt{};
40
    OGRSpatialReference m_oSRS{};
41

42
    CPLString osRawFilename;
43

44
    int bHDRDirty;
45
    ERSHdrNode *poHeader;
46

47
    const char *Find(const char *, const char *);
48

49
    int nGCPCount;
50
    GDAL_GCP *pasGCPList;
51
    OGRSpatialReference m_oGCPSRS{};
52

53
    void ReadGCPs();
54

55
    int bHasNoDataValue;
56
    double dfNoDataValue;
57

58
    CPLString osProj, osProjForced;
59
    CPLString osDatum, osDatumForced;
60
    CPLString osUnits, osUnitsForced;
61
    void WriteProjectionInfo(const char *pszProj, const char *pszDatum,
62
                             const char *pszUnits);
63

64
    CPLStringList oERSMetadataList;
65

66
  protected:
67
    int CloseDependentDatasets() override;
68

69
    CPLErr Close() override;
70

71
  public:
72
    ERSDataset();
73
    ~ERSDataset() override;
74

75
    CPLErr FlushCache(bool bAtClosing) override;
76
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
77
    CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
78
    const OGRSpatialReference *GetSpatialRef() const override;
79
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
80
    char **GetFileList(void) override;
81

82
    int GetGCPCount() override;
83
    const OGRSpatialReference *GetGCPSpatialRef() const override;
84
    const GDAL_GCP *GetGCPs() override;
85
    CPLErr SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
86
                   const OGRSpatialReference *poSRS) override;
87

88
    char **GetMetadataDomainList() override;
89
    const char *GetMetadataItem(const char *pszName,
90
                                const char *pszDomain = "") override;
91
    char **GetMetadata(const char *pszDomain = "") override;
92

93
    static GDALDataset *Open(GDALOpenInfo *);
94
    static int Identify(GDALOpenInfo *);
95
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
96
                               int nBandsIn, GDALDataType eType,
97
                               char **papszParamList);
98
};
99

100
/************************************************************************/
101
/*                            ERSDataset()                             */
102
/************************************************************************/
103

104
ERSDataset::ERSDataset()
62✔
105
    : fpImage(nullptr), poDepFile(nullptr), bGotTransform(FALSE),
106
      bHDRDirty(FALSE), poHeader(nullptr), nGCPCount(0), pasGCPList(nullptr),
107
      bHasNoDataValue(FALSE), dfNoDataValue(0.0)
62✔
108
{
109
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
62✔
110
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
62✔
111
}
62✔
112

113
/************************************************************************/
114
/*                            ~ERSDataset()                            */
115
/************************************************************************/
116

117
ERSDataset::~ERSDataset()
124✔
118

119
{
120
    ERSDataset::Close();
62✔
121
}
124✔
122

123
/************************************************************************/
124
/*                              Close()                                 */
125
/************************************************************************/
126

127
CPLErr ERSDataset::Close()
123✔
128
{
129
    CPLErr eErr = CE_None;
123✔
130
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
123✔
131
    {
132
        if (ERSDataset::FlushCache(true) != CE_None)
62✔
133
            eErr = CE_Failure;
×
134

135
        if (fpImage != nullptr)
62✔
136
        {
137
            VSIFCloseL(fpImage);
60✔
138
        }
139

140
        ERSDataset::CloseDependentDatasets();
62✔
141

142
        if (nGCPCount > 0)
62✔
143
        {
144
            GDALDeinitGCPs(nGCPCount, pasGCPList);
3✔
145
            CPLFree(pasGCPList);
3✔
146
        }
147

148
        if (poHeader != nullptr)
62✔
149
            delete poHeader;
62✔
150

151
        if (GDALPamDataset::Close() != CE_None)
62✔
152
            eErr = CE_Failure;
×
153
    }
154
    return eErr;
123✔
155
}
156

157
/************************************************************************/
158
/*                      CloseDependentDatasets()                        */
159
/************************************************************************/
160

161
int ERSDataset::CloseDependentDatasets()
62✔
162
{
163
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
62✔
164

165
    if (poDepFile != nullptr)
62✔
166
    {
167
        bHasDroppedRef = TRUE;
1✔
168

169
        for (int iBand = 0; iBand < nBands; iBand++)
2✔
170
        {
171
            delete papoBands[iBand];
1✔
172
            papoBands[iBand] = nullptr;
1✔
173
        }
174
        nBands = 0;
1✔
175

176
        GDALClose((GDALDatasetH)poDepFile);
1✔
177
        poDepFile = nullptr;
1✔
178
    }
179

180
    return bHasDroppedRef;
62✔
181
}
182

183
/************************************************************************/
184
/*                             FlushCache()                             */
185
/************************************************************************/
186

187
CPLErr ERSDataset::FlushCache(bool bAtClosing)
63✔
188

189
{
190
    CPLErr eErr = CE_None;
63✔
191
    if (bHDRDirty)
63✔
192
    {
193
        VSILFILE *fpERS = VSIFOpenL(GetDescription(), "w");
37✔
194
        if (fpERS == nullptr)
37✔
195
        {
196
            eErr = CE_Failure;
×
197
            CPLError(CE_Failure, CPLE_OpenFailed,
×
198
                     "Unable to rewrite %s header.", GetDescription());
×
199
        }
200
        else
201
        {
202
            if (VSIFPrintfL(fpERS, "DatasetHeader Begin\n") <= 0)
37✔
203
                eErr = CE_Failure;
×
204
            poHeader->WriteSelf(fpERS, 1);
37✔
205
            if (VSIFPrintfL(fpERS, "DatasetHeader End\n") <= 0)
37✔
206
                eErr = CE_Failure;
×
207
            if (VSIFCloseL(fpERS) != 0)
37✔
208
                eErr = CE_Failure;
×
209
        }
210
    }
211

212
    if (RawDataset::FlushCache(bAtClosing) != CE_None)
63✔
213
        eErr = CE_Failure;
×
214
    return eErr;
63✔
215
}
216

217
/************************************************************************/
218
/*                      GetMetadataDomainList()                         */
219
/************************************************************************/
220

221
char **ERSDataset::GetMetadataDomainList()
×
222
{
223
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
×
224
                                   TRUE, "ERS", nullptr);
×
225
}
226

227
/************************************************************************/
228
/*                           GetMetadataItem()                          */
229
/************************************************************************/
230

231
const char *ERSDataset::GetMetadataItem(const char *pszName,
60✔
232
                                        const char *pszDomain)
233
{
234
    if (pszDomain != nullptr && EQUAL(pszDomain, "ERS") && pszName != nullptr)
60✔
235
    {
236
        if (EQUAL(pszName, "PROJ"))
15✔
237
            return osProj.size() ? osProj.c_str() : nullptr;
5✔
238
        if (EQUAL(pszName, "DATUM"))
10✔
239
            return osDatum.size() ? osDatum.c_str() : nullptr;
5✔
240
        if (EQUAL(pszName, "UNITS"))
5✔
241
            return osUnits.size() ? osUnits.c_str() : nullptr;
5✔
242
    }
243
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
45✔
244
}
245

246
/************************************************************************/
247
/*                            GetMetadata()                             */
248
/************************************************************************/
249

250
char **ERSDataset::GetMetadata(const char *pszDomain)
17✔
251

252
{
253
    if (pszDomain != nullptr && EQUAL(pszDomain, "ERS"))
17✔
254
    {
255
        oERSMetadataList.Clear();
1✔
256
        if (!osProj.empty())
1✔
257
            oERSMetadataList.AddString(
258
                CPLSPrintf("%s=%s", "PROJ", osProj.c_str()));
1✔
259
        if (!osDatum.empty())
1✔
260
            oERSMetadataList.AddString(
261
                CPLSPrintf("%s=%s", "DATUM", osDatum.c_str()));
1✔
262
        if (!osUnits.empty())
1✔
263
            oERSMetadataList.AddString(
264
                CPLSPrintf("%s=%s", "UNITS", osUnits.c_str()));
1✔
265
        return oERSMetadataList.List();
1✔
266
    }
267

268
    return GDALPamDataset::GetMetadata(pszDomain);
16✔
269
}
270

271
/************************************************************************/
272
/*                            GetGCPCount()                             */
273
/************************************************************************/
274

275
int ERSDataset::GetGCPCount()
3✔
276

277
{
278
    return nGCPCount;
3✔
279
}
280

281
/************************************************************************/
282
/*                          GetGCPSpatialRef()                          */
283
/************************************************************************/
284

285
const OGRSpatialReference *ERSDataset::GetGCPSpatialRef() const
1✔
286

287
{
288
    return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
1✔
289
}
290

291
/************************************************************************/
292
/*                               GetGCPs()                              */
293
/************************************************************************/
294

295
const GDAL_GCP *ERSDataset::GetGCPs()
1✔
296

297
{
298
    return pasGCPList;
1✔
299
}
300

301
/************************************************************************/
302
/*                              SetGCPs()                               */
303
/************************************************************************/
304

305
CPLErr ERSDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
1✔
306
                           const OGRSpatialReference *poSRS)
307

308
{
309
    /* -------------------------------------------------------------------- */
310
    /*      Clean old gcps.                                                 */
311
    /* -------------------------------------------------------------------- */
312
    m_oGCPSRS.Clear();
1✔
313

314
    if (nGCPCount > 0)
1✔
315
    {
316
        GDALDeinitGCPs(nGCPCount, pasGCPList);
×
317
        CPLFree(pasGCPList);
×
318

319
        pasGCPList = nullptr;
×
320
        nGCPCount = 0;
×
321
    }
322

323
    /* -------------------------------------------------------------------- */
324
    /*      Copy new ones.                                                  */
325
    /* -------------------------------------------------------------------- */
326
    nGCPCount = nGCPCountIn;
1✔
327
    pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
1✔
328
    if (poSRS)
1✔
329
        m_oGCPSRS = *poSRS;
1✔
330

331
    /* -------------------------------------------------------------------- */
332
    /*      Setup the header contents corresponding to these GCPs.          */
333
    /* -------------------------------------------------------------------- */
334
    bHDRDirty = TRUE;
1✔
335

336
    poHeader->Set("RasterInfo.WarpControl.WarpType", "Polynomial");
1✔
337
    if (nGCPCount > 6)
1✔
338
        poHeader->Set("RasterInfo.WarpControl.WarpOrder", "2");
×
339
    else
340
        poHeader->Set("RasterInfo.WarpControl.WarpOrder", "1");
1✔
341
    poHeader->Set("RasterInfo.WarpControl.WarpSampling", "Nearest");
1✔
342

343
    /* -------------------------------------------------------------------- */
344
    /*      Translate the projection.                                       */
345
    /* -------------------------------------------------------------------- */
346
    char szERSProj[32], szERSDatum[32], szERSUnits[32];
347

348
    m_oGCPSRS.exportToERM(szERSProj, szERSDatum, szERSUnits);
1✔
349

350
    /* Write the above computed values, unless they have been overridden by */
351
    /* the creation options PROJ, DATUM or UNITS */
352

353
    poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Datum",
1✔
354
                  CPLString().Printf("\"%s\"", (osDatum.size())
2✔
355
                                                   ? osDatum.c_str()
×
356
                                                   : szERSDatum));
1✔
357
    poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Projection",
1✔
358
                  CPLString().Printf("\"%s\"", (osProj.size()) ? osProj.c_str()
2✔
359
                                                               : szERSProj));
1✔
360
    poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.CoordinateType",
1✔
361
                  CPLString().Printf("EN"));
2✔
362
    poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Units",
1✔
363
                  CPLString().Printf("\"%s\"", (osUnits.size())
2✔
364
                                                   ? osUnits.c_str()
×
365
                                                   : szERSUnits));
1✔
366
    poHeader->Set("RasterInfo.WarpControl.CoordinateSpace.Rotation", "0:0:0.0");
1✔
367

368
    /* -------------------------------------------------------------------- */
369
    /*      Translate the GCPs.                                             */
370
    /* -------------------------------------------------------------------- */
371
    CPLString osControlPoints = "{\n";
1✔
372

373
    for (int iGCP = 0; iGCP < nGCPCount; iGCP++)
5✔
374
    {
375
        CPLString osLine;
8✔
376

377
        CPLString osId = pasGCPList[iGCP].pszId;
8✔
378
        if (osId.empty())
4✔
379
            osId.Printf("%d", iGCP + 1);
4✔
380

381
        osLine.Printf(
382
            "\t\t\t\t\"%s\"\tYes\tYes\t%.6f\t%.6f\t%.15g\t%.15g\t%.15g\n",
383
            osId.c_str(), pasGCPList[iGCP].dfGCPPixel,
4✔
384
            pasGCPList[iGCP].dfGCPLine, pasGCPList[iGCP].dfGCPX,
4✔
385
            pasGCPList[iGCP].dfGCPY, pasGCPList[iGCP].dfGCPZ);
4✔
386
        osControlPoints += osLine;
4✔
387
    }
388
    osControlPoints += "\t\t}";
1✔
389

390
    poHeader->Set("RasterInfo.WarpControl.ControlPoints", osControlPoints);
1✔
391

392
    return CE_None;
2✔
393
}
394

395
/************************************************************************/
396
/*                          GetSpatialRef()                             */
397
/************************************************************************/
398

399
const OGRSpatialReference *ERSDataset::GetSpatialRef() const
3✔
400

401
{
402
    // try xml first
403
    const auto poSRS = GDALPamDataset::GetSpatialRef();
3✔
404
    if (poSRS)
3✔
405
        return poSRS;
×
406

407
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
3✔
408
}
409

410
/************************************************************************/
411
/*                           SetSpatialRef()                            */
412
/************************************************************************/
413

414
CPLErr ERSDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
34✔
415

416
{
417
    if (poSRS == nullptr && m_oSRS.IsEmpty())
34✔
418
        return CE_None;
×
419
    if (poSRS != nullptr && poSRS->IsSame(&m_oSRS))
34✔
420
        return CE_None;
×
421

422
    m_oSRS.Clear();
34✔
423
    if (poSRS)
34✔
424
        m_oSRS = *poSRS;
34✔
425

426
    char szERSProj[32], szERSDatum[32], szERSUnits[32];
427

428
    m_oSRS.exportToERM(szERSProj, szERSDatum, szERSUnits);
34✔
429

430
    /* Write the above computed values, unless they have been overridden by */
431
    /* the creation options PROJ, DATUM or UNITS */
432
    if (!osProjForced.empty())
34✔
433
        osProj = osProjForced;
1✔
434
    else
435
        osProj = szERSProj;
33✔
436
    if (!osDatumForced.empty())
34✔
437
        osDatum = osDatumForced;
1✔
438
    else
439
        osDatum = szERSDatum;
33✔
440
    if (!osUnitsForced.empty())
34✔
441
        osUnits = osUnitsForced;
1✔
442
    else
443
        osUnits = szERSUnits;
33✔
444

445
    WriteProjectionInfo(osProj, osDatum, osUnits);
34✔
446

447
    return CE_None;
34✔
448
}
449

450
/************************************************************************/
451
/*                         WriteProjectionInfo()                        */
452
/************************************************************************/
453

454
void ERSDataset::WriteProjectionInfo(const char *pszProj, const char *pszDatum,
36✔
455
                                     const char *pszUnits)
456
{
457
    bHDRDirty = TRUE;
36✔
458
    poHeader->Set("CoordinateSpace.Datum",
36✔
459
                  CPLString().Printf("\"%s\"", pszDatum));
72✔
460
    poHeader->Set("CoordinateSpace.Projection",
36✔
461
                  CPLString().Printf("\"%s\"", pszProj));
72✔
462
    poHeader->Set("CoordinateSpace.CoordinateType", CPLString().Printf("EN"));
36✔
463
    poHeader->Set("CoordinateSpace.Units",
36✔
464
                  CPLString().Printf("\"%s\"", pszUnits));
72✔
465
    poHeader->Set("CoordinateSpace.Rotation", "0:0:0.0");
36✔
466

467
    /* -------------------------------------------------------------------- */
468
    /*      It seems that CoordinateSpace needs to come before              */
469
    /*      RasterInfo.  Try moving it up manually.                         */
470
    /* -------------------------------------------------------------------- */
471
    int iRasterInfo = -1;
36✔
472
    int iCoordSpace = -1;
36✔
473

474
    for (int i = 0; i < poHeader->nItemCount; i++)
250✔
475
    {
476
        if (EQUAL(poHeader->papszItemName[i], "RasterInfo"))
250✔
477
            iRasterInfo = i;
34✔
478

479
        if (EQUAL(poHeader->papszItemName[i], "CoordinateSpace"))
250✔
480
        {
481
            iCoordSpace = i;
36✔
482
            break;
36✔
483
        }
484
    }
485

486
    if (iCoordSpace > iRasterInfo && iRasterInfo != -1)
36✔
487
    {
488
        for (int i = iCoordSpace; i > 0 && i != iRasterInfo; i--)
68✔
489
        {
490

491
            ERSHdrNode *poTemp = poHeader->papoItemChild[i];
34✔
492
            poHeader->papoItemChild[i] = poHeader->papoItemChild[i - 1];
34✔
493
            poHeader->papoItemChild[i - 1] = poTemp;
34✔
494

495
            char *pszTemp = poHeader->papszItemName[i];
34✔
496
            poHeader->papszItemName[i] = poHeader->papszItemName[i - 1];
34✔
497
            poHeader->papszItemName[i - 1] = pszTemp;
34✔
498

499
            pszTemp = poHeader->papszItemValue[i];
34✔
500
            poHeader->papszItemValue[i] = poHeader->papszItemValue[i - 1];
34✔
501
            poHeader->papszItemValue[i - 1] = pszTemp;
34✔
502
        }
503
    }
504
}
36✔
505

506
/************************************************************************/
507
/*                          GetGeoTransform()                           */
508
/************************************************************************/
509

510
CPLErr ERSDataset::GetGeoTransform(GDALGeoTransform &gt) const
22✔
511

512
{
513
    if (bGotTransform)
22✔
514
    {
515
        gt = m_gt;
22✔
516
        return CE_None;
22✔
517
    }
518

519
    return GDALPamDataset::GetGeoTransform(gt);
×
520
}
521

522
/************************************************************************/
523
/*                          SetGeoTransform()                           */
524
/************************************************************************/
525

526
CPLErr ERSDataset::SetGeoTransform(const GDALGeoTransform &gt)
32✔
527

528
{
529
    if (m_gt == gt)
32✔
530
        return CE_None;
×
531

532
    if (gt[2] != 0 || gt[4] != 0)
32✔
533
    {
534
        CPLError(CE_Failure, CPLE_AppDefined,
×
535
                 "Rotated and skewed geotransforms not currently supported for "
536
                 "ERS driver.");
537
        return CE_Failure;
×
538
    }
539

540
    bGotTransform = TRUE;
32✔
541
    m_gt = gt;
32✔
542

543
    bHDRDirty = TRUE;
32✔
544

545
    poHeader->Set("RasterInfo.CellInfo.Xdimension",
32✔
546
                  CPLString().Printf("%.15g", fabs(m_gt[1])));
64✔
547
    poHeader->Set("RasterInfo.CellInfo.Ydimension",
32✔
548
                  CPLString().Printf("%.15g", fabs(m_gt[5])));
64✔
549
    poHeader->Set("RasterInfo.RegistrationCoord.Eastings",
32✔
550
                  CPLString().Printf("%.15g", m_gt[0]));
64✔
551
    poHeader->Set("RasterInfo.RegistrationCoord.Northings",
32✔
552
                  CPLString().Printf("%.15g", m_gt[3]));
64✔
553

554
    if (CPLAtof(poHeader->Find("RasterInfo.RegistrationCellX", "0")) != 0.0 ||
64✔
555
        CPLAtof(poHeader->Find("RasterInfo.RegistrationCellY", "0")) != 0.0)
32✔
556
    {
557
        // Reset RegistrationCellX/Y to 0 if the header gets rewritten (#5493)
558
        poHeader->Set("RasterInfo.RegistrationCellX", "0");
×
559
        poHeader->Set("RasterInfo.RegistrationCellY", "0");
×
560
    }
561

562
    return CE_None;
32✔
563
}
564

565
/************************************************************************/
566
/*                             ERSDMS2Dec()                             */
567
/*                                                                      */
568
/*      Convert ERS DMS format to decimal degrees.   Input is like      */
569
/*      "-180:00:00".                                                   */
570
/************************************************************************/
571

572
static double ERSDMS2Dec(const char *pszDMS)
10✔
573

574
{
575
    char **papszTokens = CSLTokenizeStringComplex(pszDMS, ":", FALSE, FALSE);
10✔
576

577
    if (CSLCount(papszTokens) != 3)
10✔
578
    {
579
        CSLDestroy(papszTokens);
×
580
        return CPLAtof(pszDMS);
×
581
    }
582

583
    double dfResult = fabs(CPLAtof(papszTokens[0])) +
10✔
584
                      CPLAtof(papszTokens[1]) / 60.0 +
10✔
585
                      CPLAtof(papszTokens[2]) / 3600.0;
10✔
586

587
    if (CPLAtof(papszTokens[0]) < 0)
10✔
588
        dfResult *= -1;
8✔
589

590
    CSLDestroy(papszTokens);
10✔
591
    return dfResult;
10✔
592
}
593

594
/************************************************************************/
595
/*                            GetFileList()                             */
596
/************************************************************************/
597

598
char **ERSDataset::GetFileList()
10✔
599

600
{
601
    static thread_local int nRecLevel = 0;
602
    if (nRecLevel > 0)
10✔
603
        return nullptr;
×
604

605
    // Main data file, etc.
606
    char **papszFileList = GDALPamDataset::GetFileList();
10✔
607

608
    // Add raw data file if we have one.
609
    if (!osRawFilename.empty())
10✔
610
        papszFileList = CSLAddString(papszFileList, osRawFilename);
10✔
611

612
    // If we have a dependent file, merge its list of files in.
613
    if (poDepFile)
10✔
614
    {
615
        nRecLevel++;
×
616
        char **papszDepFiles = poDepFile->GetFileList();
×
617
        nRecLevel--;
×
618
        papszFileList = CSLInsertStrings(papszFileList, -1, papszDepFiles);
×
619
        CSLDestroy(papszDepFiles);
×
620
    }
621

622
    return papszFileList;
10✔
623
}
624

625
/************************************************************************/
626
/*                              ReadGCPs()                              */
627
/*                                                                      */
628
/*      Read the GCPs from the header.                                  */
629
/************************************************************************/
630

631
void ERSDataset::ReadGCPs()
2✔
632

633
{
634
    const char *pszCP =
635
        poHeader->Find("RasterInfo.WarpControl.ControlPoints", nullptr);
2✔
636

637
    if (pszCP == nullptr)
2✔
638
        return;
×
639

640
    /* -------------------------------------------------------------------- */
641
    /*      Parse the control points.  They will look something like:       */
642
    /*                                                                      */
643
    /*   "1035" Yes No 2344.650885 3546.419458 483270.73 3620906.21 3.105   */
644
    /* -------------------------------------------------------------------- */
645
    char **papszTokens = CSLTokenizeStringComplex(pszCP, "{ \t}", TRUE, FALSE);
2✔
646
    int nItemCount = CSLCount(papszTokens);
2✔
647

648
    /* -------------------------------------------------------------------- */
649
    /*      Work out if we have elevation values or not.                    */
650
    /* -------------------------------------------------------------------- */
651
    int nItemsPerLine;
652

653
    if (nItemCount == 7)
2✔
654
        nItemsPerLine = 7;
×
655
    else if (nItemCount == 8)
2✔
656
        nItemsPerLine = 8;
×
657
    else if (nItemCount < 14)
2✔
658
    {
659
        CPLDebug("ERS", "Invalid item count for ControlPoints");
×
660
        CSLDestroy(papszTokens);
×
661
        return;
×
662
    }
663
    else if (EQUAL(papszTokens[8], "Yes") || EQUAL(papszTokens[8], "No"))
2✔
664
        nItemsPerLine = 7;
×
665
    else if (EQUAL(papszTokens[9], "Yes") || EQUAL(papszTokens[9], "No"))
2✔
666
        nItemsPerLine = 8;
2✔
667
    else
668
    {
669
        CPLDebug("ERS", "Invalid format for ControlPoints");
×
670
        CSLDestroy(papszTokens);
×
671
        return;
×
672
    }
673

674
    /* -------------------------------------------------------------------- */
675
    /*      Setup GCPs.                                                     */
676
    /* -------------------------------------------------------------------- */
677
    CPLAssert(nGCPCount == 0);
2✔
678

679
    nGCPCount = nItemCount / nItemsPerLine;
2✔
680
    pasGCPList = (GDAL_GCP *)CPLCalloc(nGCPCount, sizeof(GDAL_GCP));
2✔
681
    GDALInitGCPs(nGCPCount, pasGCPList);
2✔
682

683
    for (int iGCP = 0; iGCP < nGCPCount; iGCP++)
10✔
684
    {
685
        GDAL_GCP *psGCP = pasGCPList + iGCP;
8✔
686

687
        CPLFree(psGCP->pszId);
8✔
688
        psGCP->pszId = CPLStrdup(papszTokens[iGCP * nItemsPerLine + 0]);
8✔
689
        psGCP->dfGCPPixel = CPLAtof(papszTokens[iGCP * nItemsPerLine + 3]);
8✔
690
        psGCP->dfGCPLine = CPLAtof(papszTokens[iGCP * nItemsPerLine + 4]);
8✔
691
        psGCP->dfGCPX = CPLAtof(papszTokens[iGCP * nItemsPerLine + 5]);
8✔
692
        psGCP->dfGCPY = CPLAtof(papszTokens[iGCP * nItemsPerLine + 6]);
8✔
693
        if (nItemsPerLine == 8)
8✔
694
            psGCP->dfGCPZ = CPLAtof(papszTokens[iGCP * nItemsPerLine + 7]);
8✔
695
    }
696

697
    CSLDestroy(papszTokens);
2✔
698

699
    /* -------------------------------------------------------------------- */
700
    /*      Parse the GCP projection.                                       */
701
    /* -------------------------------------------------------------------- */
702
    osProj =
703
        poHeader->Find("RasterInfo.WarpControl.CoordinateSpace.Projection", "");
2✔
704
    osDatum =
705
        poHeader->Find("RasterInfo.WarpControl.CoordinateSpace.Datum", "");
2✔
706
    osUnits =
707
        poHeader->Find("RasterInfo.WarpControl.CoordinateSpace.Units", "");
2✔
708

709
    m_oGCPSRS.importFromERM(!osProj.empty() ? osProj.c_str() : "RAW",
2✔
710
                            !osDatum.empty() ? osDatum.c_str() : "WGS84",
2✔
711
                            !osUnits.empty() ? osUnits.c_str() : "METERS");
2✔
712
}
713

714
/************************************************************************/
715
/* ==================================================================== */
716
/*                             ERSRasterBand                            */
717
/* ==================================================================== */
718
/************************************************************************/
719

720
class ERSRasterBand final : public RawRasterBand
721
{
722
  public:
723
    ERSRasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
724
                  vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset,
725
                  GDALDataType eDataType, int bNativeOrder);
726

727
    double GetNoDataValue(int *pbSuccess = nullptr) override;
728
    CPLErr SetNoDataValue(double) override;
729
};
730

731
/************************************************************************/
732
/*                           ERSRasterBand()                            */
733
/************************************************************************/
734

735
ERSRasterBand::ERSRasterBand(GDALDataset *poDSIn, int nBandIn,
102✔
736
                             VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
737
                             int nPixelOffsetIn, int nLineOffsetIn,
738
                             GDALDataType eDataTypeIn, int bNativeOrderIn)
102✔
739
    : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
740
                    nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
741
                    RawRasterBand::OwnFP::NO)
102✔
742
{
743
}
102✔
744

745
/************************************************************************/
746
/*                           GetNoDataValue()                           */
747
/************************************************************************/
748

749
double ERSRasterBand::GetNoDataValue(int *pbSuccess)
15✔
750
{
751
    ERSDataset *poGDS = cpl::down_cast<ERSDataset *>(poDS);
15✔
752
    if (poGDS->bHasNoDataValue)
15✔
753
    {
754
        if (pbSuccess)
1✔
755
            *pbSuccess = TRUE;
1✔
756
        return poGDS->dfNoDataValue;
1✔
757
    }
758

759
    return RawRasterBand::GetNoDataValue(pbSuccess);
14✔
760
}
761

762
/************************************************************************/
763
/*                           SetNoDataValue()                           */
764
/************************************************************************/
765

766
CPLErr ERSRasterBand::SetNoDataValue(double dfNoDataValue)
1✔
767
{
768
    ERSDataset *poGDS = cpl::down_cast<ERSDataset *>(poDS);
1✔
769
    if (!poGDS->bHasNoDataValue || poGDS->dfNoDataValue != dfNoDataValue)
1✔
770
    {
771
        poGDS->bHasNoDataValue = TRUE;
1✔
772
        poGDS->dfNoDataValue = dfNoDataValue;
1✔
773

774
        poGDS->bHDRDirty = TRUE;
1✔
775
        poGDS->poHeader->Set("RasterInfo.NullCellValue",
1✔
776
                             CPLString().Printf("%.16g", dfNoDataValue));
2✔
777
    }
778
    return CE_None;
1✔
779
}
780

781
/************************************************************************/
782
/*                              Identify()                              */
783
/************************************************************************/
784

785
int ERSDataset::Identify(GDALOpenInfo *poOpenInfo)
59,338✔
786

787
{
788
    /* -------------------------------------------------------------------- */
789
    /*      We assume the user selects the .ers file.                       */
790
    /* -------------------------------------------------------------------- */
791
    CPLString osHeader((const char *)poOpenInfo->pabyHeader,
59,338✔
792
                       poOpenInfo->nHeaderBytes);
118,675✔
793

794
    if (osHeader.ifind("Algorithm Begin") != std::string::npos)
59,337✔
795
    {
796
        CPLError(CE_Failure, CPLE_OpenFailed,
×
797
                 "%s appears to be an algorithm ERS file, which is not "
798
                 "currently supported.",
799
                 poOpenInfo->pszFilename);
800
        return FALSE;
×
801
    }
802

803
    if (osHeader.ifind("DatasetHeader ") != std::string::npos)
59,337✔
804
        return TRUE;
89✔
805

806
    return FALSE;
59,248✔
807
}
808

809
/************************************************************************/
810
/*                         ERSProxyRasterBand                           */
811
/************************************************************************/
812

813
namespace
814
{
815

816
static int &GetRecLevel()
63✔
817
{
818
    static thread_local int nRecLevel = 0;
819
    return nRecLevel;
63✔
820
}
821

822
class ERSProxyRasterBand final : public GDALProxyRasterBand
823
{
824
  public:
825
    explicit ERSProxyRasterBand(GDALRasterBand *poUnderlyingBand)
1✔
826
        : m_poUnderlyingBand(poUnderlyingBand)
1✔
827
    {
828
        poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
1✔
829
        eDataType = poUnderlyingBand->GetRasterDataType();
1✔
830
    }
1✔
831

832
    int GetOverviewCount() override;
833

834
  protected:
835
    GDALRasterBand *RefUnderlyingRasterBand(bool /*bForceOpen*/) const override
1✔
836
    {
837
        return m_poUnderlyingBand;
1✔
838
    }
839

840
  private:
841
    GDALRasterBand *m_poUnderlyingBand;
842
};
843

844
int ERSProxyRasterBand::GetOverviewCount()
×
845
{
846
    int &nRecLevel = GetRecLevel();
×
847
    nRecLevel++;
×
848
    int nRet = GDALProxyRasterBand::GetOverviewCount();
×
849
    nRecLevel--;
×
850
    return nRet;
×
851
}
852

853
}  // namespace
854

855
/************************************************************************/
856
/*                                Open()                                */
857
/************************************************************************/
858

859
GDALDataset *ERSDataset::Open(GDALOpenInfo *poOpenInfo)
63✔
860

861
{
862
    if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
63✔
863
        return nullptr;
×
864

865
    int &nRecLevel = GetRecLevel();
63✔
866
    // cppcheck-suppress knownConditionTrueFalse
867
    if (nRecLevel)
63✔
868
    {
869
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
870
                 "Attempt at recursively opening ERS dataset");
871
        return nullptr;
1✔
872
    }
873

874
    /* -------------------------------------------------------------------- */
875
    /*      Ingest the file as a tree of header nodes.                      */
876
    /* -------------------------------------------------------------------- */
877
    ERSHdrNode *poHeader = new ERSHdrNode();
62✔
878

879
    if (!poHeader->ParseHeader(poOpenInfo->fpL))
62✔
880
    {
881
        delete poHeader;
×
882
        VSIFCloseL(poOpenInfo->fpL);
×
883
        poOpenInfo->fpL = nullptr;
×
884
        return nullptr;
×
885
    }
886

887
    VSIFCloseL(poOpenInfo->fpL);
62✔
888
    poOpenInfo->fpL = nullptr;
62✔
889

890
    /* -------------------------------------------------------------------- */
891
    /*      Do we have the minimum required information from this header?   */
892
    /* -------------------------------------------------------------------- */
893
    if (poHeader->Find("RasterInfo.NrOfLines") == nullptr ||
62✔
894
        poHeader->Find("RasterInfo.NrOfCellsPerLine") == nullptr ||
124✔
895
        poHeader->Find("RasterInfo.NrOfBands") == nullptr)
62✔
896
    {
897
        if (poHeader->FindNode("Algorithm") != nullptr)
×
898
        {
899
            CPLError(CE_Failure, CPLE_OpenFailed,
×
900
                     "%s appears to be an algorithm ERS file, which is not "
901
                     "currently supported.",
902
                     poOpenInfo->pszFilename);
903
        }
904
        delete poHeader;
×
905
        return nullptr;
×
906
    }
907

908
    /* -------------------------------------------------------------------- */
909
    /*      Create a corresponding GDALDataset.                             */
910
    /* -------------------------------------------------------------------- */
911
    auto poDS = std::make_unique<ERSDataset>();
124✔
912
    poDS->poHeader = poHeader;
62✔
913
    poDS->eAccess = poOpenInfo->eAccess;
62✔
914

915
    /* -------------------------------------------------------------------- */
916
    /*      Capture some information from the file that is of interest.     */
917
    /* -------------------------------------------------------------------- */
918
    int nBands = atoi(poHeader->Find("RasterInfo.NrOfBands"));
62✔
919
    poDS->nRasterXSize = atoi(poHeader->Find("RasterInfo.NrOfCellsPerLine"));
62✔
920
    poDS->nRasterYSize = atoi(poHeader->Find("RasterInfo.NrOfLines"));
62✔
921

922
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
124✔
923
        !GDALCheckBandCount(nBands, FALSE))
62✔
924
    {
925
        return nullptr;
×
926
    }
927

928
    /* -------------------------------------------------------------------- */
929
    /*     Get the HeaderOffset if it exists in the header                  */
930
    /* -------------------------------------------------------------------- */
931
    GIntBig nHeaderOffset = 0;
62✔
932
    const char *pszHeaderOffset = poHeader->Find("HeaderOffset");
62✔
933
    if (pszHeaderOffset != nullptr)
62✔
934
    {
935
        nHeaderOffset = CPLAtoGIntBig(pszHeaderOffset);
2✔
936
        if (nHeaderOffset < 0)
2✔
937
        {
938
            CPLError(CE_Failure, CPLE_AppDefined,
×
939
                     "Illegal value for HeaderOffset: %s", pszHeaderOffset);
940
            return nullptr;
×
941
        }
942
    }
943

944
    /* -------------------------------------------------------------------- */
945
    /*      Establish the data type.                                        */
946
    /* -------------------------------------------------------------------- */
947
    CPLString osCellType =
948
        poHeader->Find("RasterInfo.CellType", "Unsigned8BitInteger");
124✔
949
    GDALDataType eType;
950
    if (EQUAL(osCellType, "Unsigned8BitInteger"))
62✔
951
        eType = GDT_Byte;
29✔
952
    else if (EQUAL(osCellType, "Signed8BitInteger"))
33✔
953
        eType = GDT_Int8;
6✔
954
    else if (EQUAL(osCellType, "Unsigned16BitInteger"))
27✔
955
        eType = GDT_UInt16;
3✔
956
    else if (EQUAL(osCellType, "Signed16BitInteger"))
24✔
957
        eType = GDT_Int16;
6✔
958
    else if (EQUAL(osCellType, "Unsigned32BitInteger"))
18✔
959
        eType = GDT_UInt32;
3✔
960
    else if (EQUAL(osCellType, "Signed32BitInteger"))
15✔
961
        eType = GDT_Int32;
3✔
962
    else if (EQUAL(osCellType, "IEEE4ByteReal"))
12✔
963
        eType = GDT_Float32;
9✔
964
    else if (EQUAL(osCellType, "IEEE8ByteReal"))
3✔
965
        eType = GDT_Float64;
3✔
966
    else
967
    {
968
        CPLDebug("ERS", "Unknown CellType '%s'", osCellType.c_str());
×
969
        eType = GDT_Byte;
×
970
    }
971

972
    /* -------------------------------------------------------------------- */
973
    /*      Pick up the word order.                                         */
974
    /* -------------------------------------------------------------------- */
975
    const int bNative =
976
#ifdef CPL_LSB
977
        EQUAL(poHeader->Find("ByteOrder", "LSBFirst"), "LSBFirst")
62✔
978
#else
979
        EQUAL(poHeader->Find("ByteOrder", "MSBFirst"), "MSBFirst")
980
#endif
981
        ;
982
    /* -------------------------------------------------------------------- */
983
    /*      Figure out the name of the target file.                         */
984
    /* -------------------------------------------------------------------- */
985
    CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
124✔
986
    CPLString osDataFile = poHeader->Find("DataFile", "");
124✔
987

988
    if (osDataFile.length() == 0)  // just strip off extension.
62✔
989
    {
990
        osDataFile = CPLGetFilename(poOpenInfo->pszFilename);
61✔
991
        osDataFile = osDataFile.substr(0, osDataFile.find_last_of('.'));
61✔
992
    }
993

994
    CPLString osDataFilePath = CPLFormFilenameSafe(osPath, osDataFile, nullptr);
124✔
995

996
    /* -------------------------------------------------------------------- */
997
    /*      DataSetType = Translated files are links to things like ecw     */
998
    /*      files.                                                          */
999
    /* -------------------------------------------------------------------- */
1000
    if (EQUAL(poHeader->Find("DataSetType", ""), "Translated"))
62✔
1001
    {
1002
        nRecLevel++;
2✔
1003
        poDS->poDepFile = GDALDataset::FromHandle(
2✔
1004
            GDALOpen(osDataFilePath, poOpenInfo->eAccess));
1005
        nRecLevel--;
2✔
1006

1007
        if (poDS->poDepFile != nullptr &&
2✔
1008
            poDS->poDepFile->GetRasterXSize() == poDS->GetRasterXSize() &&
1✔
1009
            poDS->poDepFile->GetRasterYSize() == poDS->GetRasterYSize() &&
4✔
1010
            poDS->poDepFile->GetRasterCount() >= nBands)
1✔
1011
        {
1012
            for (int iBand = 0; iBand < nBands; iBand++)
2✔
1013
            {
1014
                // Assume pixel interleaved.
1015
                poDS->SetBand(iBand + 1,
2✔
1016
                              new ERSProxyRasterBand(
1017
                                  poDS->poDepFile->GetRasterBand(iBand + 1)));
1✔
1018
            }
1019
        }
1020
        else
1021
        {
1022
            delete poDS->poDepFile;
1✔
1023
            poDS->poDepFile = nullptr;
1✔
1024
        }
1025
    }
1026

1027
    /* ==================================================================== */
1028
    /*      While ERStorage indicates a raw file.                           */
1029
    /* ==================================================================== */
1030
    else if (EQUAL(poHeader->Find("DataSetType", ""), "ERStorage"))
60✔
1031
    {
1032
        // Open data file.
1033
        if (poOpenInfo->eAccess == GA_Update)
60✔
1034
            poDS->fpImage = VSIFOpenL(osDataFilePath, "r+");
38✔
1035
        else
1036
            poDS->fpImage = VSIFOpenL(osDataFilePath, "r");
22✔
1037

1038
        poDS->osRawFilename = std::move(osDataFilePath);
60✔
1039

1040
        if (poDS->fpImage != nullptr && nBands > 0)
60✔
1041
        {
1042
            int iWordSize = GDALGetDataTypeSizeBytes(eType);
60✔
1043

1044
            const auto knIntMax = std::numeric_limits<int>::max();
60✔
1045
            if (nBands > knIntMax / iWordSize ||
120✔
1046
                poDS->nRasterXSize > knIntMax / (nBands * iWordSize))
60✔
1047
            {
1048
                CPLError(CE_Failure, CPLE_AppDefined,
×
1049
                         "int overflow: too large nBands and/or nRasterXSize");
1050
                return nullptr;
×
1051
            }
1052

1053
            if (!RAWDatasetCheckMemoryUsage(
60✔
1054
                    poDS->nRasterXSize, poDS->nRasterYSize, nBands, iWordSize,
60✔
1055
                    iWordSize, iWordSize * nBands * poDS->nRasterXSize,
60✔
1056
                    nHeaderOffset,
1057
                    static_cast<vsi_l_offset>(iWordSize) * poDS->nRasterXSize,
60✔
1058
                    poDS->fpImage))
60✔
1059
            {
1060
                return nullptr;
×
1061
            }
1062
            if (nHeaderOffset > std::numeric_limits<GIntBig>::max() -
60✔
1063
                                    static_cast<GIntBig>(nBands - 1) *
60✔
1064
                                        iWordSize * poDS->nRasterXSize)
60✔
1065
            {
1066
                CPLError(CE_Failure, CPLE_AppDefined,
×
1067
                         "int overflow: too large nHeaderOffset");
1068
                return nullptr;
×
1069
            }
1070

1071
            for (int iBand = 0; iBand < nBands; iBand++)
162✔
1072
            {
1073
                // Assume pixel interleaved.
1074
                auto poBand = std::make_unique<ERSRasterBand>(
1075
                    poDS.get(), iBand + 1, poDS->fpImage,
102✔
1076
                    nHeaderOffset + static_cast<vsi_l_offset>(iWordSize) *
×
1077
                                        iBand * poDS->nRasterXSize,
102✔
1078
                    iWordSize, iWordSize * nBands * poDS->nRasterXSize, eType,
102✔
1079
                    bNative);
102✔
1080
                if (!poBand->IsValid())
102✔
1081
                    return nullptr;
×
1082
                poDS->SetBand(iBand + 1, std::move(poBand));
102✔
1083
            }
1084
        }
1085
    }
1086

1087
    /* -------------------------------------------------------------------- */
1088
    /*      Otherwise we have an error!                                     */
1089
    /* -------------------------------------------------------------------- */
1090
    if (poDS->nBands == 0)
62✔
1091
    {
1092
        return nullptr;
1✔
1093
    }
1094

1095
    /* -------------------------------------------------------------------- */
1096
    /*      Look for band descriptions.                                     */
1097
    /* -------------------------------------------------------------------- */
1098
    ERSHdrNode *poRI = poHeader->FindNode("RasterInfo");
61✔
1099

1100
    for (int iChild = 0, iBand = 0;
357✔
1101
         poRI != nullptr && iChild < poRI->nItemCount && iBand < poDS->nBands;
357✔
1102
         iChild++)
1103
    {
1104
        if (poRI->papoItemChild[iChild] != nullptr &&
296✔
1105
            EQUAL(poRI->papszItemName[iChild], "BandId"))
33✔
1106
        {
1107
            const char *pszValue =
1108
                poRI->papoItemChild[iChild]->Find("Value", nullptr);
12✔
1109

1110
            iBand++;
12✔
1111
            if (pszValue)
12✔
1112
            {
1113
                CPLPushErrorHandler(CPLQuietErrorHandler);
12✔
1114
                poDS->GetRasterBand(iBand)->SetDescription(pszValue);
12✔
1115
                CPLPopErrorHandler();
12✔
1116
            }
1117

1118
            pszValue = poRI->papoItemChild[iChild]->Find("Units", nullptr);
12✔
1119
            if (pszValue)
12✔
1120
            {
1121
                CPLPushErrorHandler(CPLQuietErrorHandler);
4✔
1122
                poDS->GetRasterBand(iBand)->SetUnitType(pszValue);
4✔
1123
                CPLPopErrorHandler();
4✔
1124
            }
1125
        }
1126
    }
1127

1128
    /* -------------------------------------------------------------------- */
1129
    /*      Look for projection.                                            */
1130
    /* -------------------------------------------------------------------- */
1131
    poDS->osProj = poHeader->Find("CoordinateSpace.Projection", "");
61✔
1132
    poDS->osDatum = poHeader->Find("CoordinateSpace.Datum", "");
61✔
1133
    poDS->osUnits = poHeader->Find("CoordinateSpace.Units", "");
61✔
1134

1135
    poDS->m_oSRS.importFromERM(
219✔
1136
        !poDS->osProj.empty() ? poDS->osProj.c_str() : "RAW",
61✔
1137
        !poDS->osDatum.empty() ? poDS->osDatum.c_str() : "WGS84",
61✔
1138
        !poDS->osUnits.empty() ? poDS->osUnits.c_str() : "METERS");
61✔
1139

1140
    /* -------------------------------------------------------------------- */
1141
    /*      Look for the geotransform.                                      */
1142
    /* -------------------------------------------------------------------- */
1143
    if (poHeader->Find("RasterInfo.RegistrationCoord.Eastings", nullptr))
61✔
1144
    {
1145
        poDS->bGotTransform = TRUE;
6✔
1146
        poDS->m_gt[0] = CPLAtof(
6✔
1147
            poHeader->Find("RasterInfo.RegistrationCoord.Eastings", ""));
1148
        poDS->m_gt[1] =
6✔
1149
            CPLAtof(poHeader->Find("RasterInfo.CellInfo.Xdimension", "1.0"));
6✔
1150
        poDS->m_gt[2] = 0.0;
6✔
1151
        poDS->m_gt[3] = CPLAtof(
6✔
1152
            poHeader->Find("RasterInfo.RegistrationCoord.Northings", ""));
1153
        poDS->m_gt[4] = 0.0;
6✔
1154
        poDS->m_gt[5] =
6✔
1155
            -CPLAtof(poHeader->Find("RasterInfo.CellInfo.Ydimension", "1.0"));
6✔
1156
    }
1157
    else if (poHeader->Find("RasterInfo.RegistrationCoord.Latitude", nullptr) &&
60✔
1158
             poHeader->Find("RasterInfo.CellInfo.Xdimension", nullptr))
5✔
1159
    {
1160
        poDS->bGotTransform = TRUE;
5✔
1161
        poDS->m_gt[0] = ERSDMS2Dec(
5✔
1162
            poHeader->Find("RasterInfo.RegistrationCoord.Longitude", ""));
1163
        poDS->m_gt[1] =
5✔
1164
            CPLAtof(poHeader->Find("RasterInfo.CellInfo.Xdimension", ""));
5✔
1165
        poDS->m_gt[2] = 0.0;
5✔
1166
        poDS->m_gt[3] = ERSDMS2Dec(
5✔
1167
            poHeader->Find("RasterInfo.RegistrationCoord.Latitude", ""));
1168
        poDS->m_gt[4] = 0.0;
5✔
1169
        poDS->m_gt[5] =
5✔
1170
            -CPLAtof(poHeader->Find("RasterInfo.CellInfo.Ydimension", ""));
5✔
1171
    }
1172

1173
    /* -------------------------------------------------------------------- */
1174
    /*      Adjust if we have a registration cell.                          */
1175
    /* -------------------------------------------------------------------- */
1176

1177
    /* http://geospatial.intergraph.com/Libraries/Tech_Docs/ERDAS_ER_Mapper_Customization_Guide.sflb.ashx
1178
     */
1179
    /* Page 27 : */
1180
    /* RegistrationCellX and RegistrationCellY : The image X and Y
1181
       coordinates of the cell which corresponds to the Registration
1182
       Coordinate. Note that the RegistrationCellX and
1183
       RegistrationCellY can be fractional values. If
1184
       RegistrationCellX and RegistrationCellY are not specified,
1185
       they are assumed to be (0,0), which is the top left corner of the
1186
       image.
1187
       */
1188
    double dfCellX =
1189
        CPLAtof(poHeader->Find("RasterInfo.RegistrationCellX", "0"));
61✔
1190
    double dfCellY =
1191
        CPLAtof(poHeader->Find("RasterInfo.RegistrationCellY", "0"));
61✔
1192

1193
    if (poDS->bGotTransform)
61✔
1194
    {
1195
        poDS->m_gt[0] -= dfCellX * poDS->m_gt[1] + dfCellY * poDS->m_gt[2];
11✔
1196
        poDS->m_gt[3] -= dfCellX * poDS->m_gt[4] + dfCellY * poDS->m_gt[5];
11✔
1197
    }
1198

1199
    /* -------------------------------------------------------------------- */
1200
    /*      Check for null values.                                          */
1201
    /* -------------------------------------------------------------------- */
1202
    if (poHeader->Find("RasterInfo.NullCellValue", nullptr))
61✔
1203
    {
1204
        poDS->bHasNoDataValue = TRUE;
8✔
1205
        poDS->dfNoDataValue =
16✔
1206
            CPLAtofM(poHeader->Find("RasterInfo.NullCellValue"));
8✔
1207

1208
        if (poDS->poDepFile != nullptr)
8✔
1209
        {
1210
            CPLPushErrorHandler(CPLQuietErrorHandler);
×
1211

1212
            for (int iBand = 1; iBand <= poDS->nBands; iBand++)
×
1213
                poDS->GetRasterBand(iBand)->SetNoDataValue(poDS->dfNoDataValue);
×
1214

1215
            CPLPopErrorHandler();
×
1216
        }
1217
    }
1218

1219
    /* -------------------------------------------------------------------- */
1220
    /*      Do we have an "All" region?                                     */
1221
    /* -------------------------------------------------------------------- */
1222
    ERSHdrNode *poAll = nullptr;
61✔
1223

1224
    for (int iChild = 0; poRI != nullptr && iChild < poRI->nItemCount; iChild++)
366✔
1225
    {
1226
        if (poRI->papoItemChild[iChild] != nullptr &&
305✔
1227
            EQUAL(poRI->papszItemName[iChild], "RegionInfo"))
39✔
1228
        {
1229
            if (EQUAL(poRI->papoItemChild[iChild]->Find("RegionName", ""),
3✔
1230
                      "All"))
1231
                poAll = poRI->papoItemChild[iChild];
3✔
1232
        }
1233
    }
1234

1235
    /* -------------------------------------------------------------------- */
1236
    /*      Do we have statistics?                                          */
1237
    /* -------------------------------------------------------------------- */
1238
    if (poAll && poAll->FindNode("Stats"))
61✔
1239
    {
1240
        CPLPushErrorHandler(CPLQuietErrorHandler);
3✔
1241

1242
        for (int iBand = 1; iBand <= poDS->nBands; iBand++)
6✔
1243
        {
1244
            const char *pszValue =
1245
                poAll->FindElem("Stats.MinimumValue", iBand - 1);
3✔
1246

1247
            if (pszValue)
3✔
1248
                poDS->GetRasterBand(iBand)->SetMetadataItem(
3✔
1249
                    "STATISTICS_MINIMUM", pszValue);
3✔
1250

1251
            pszValue = poAll->FindElem("Stats.MaximumValue", iBand - 1);
3✔
1252

1253
            if (pszValue)
3✔
1254
                poDS->GetRasterBand(iBand)->SetMetadataItem(
3✔
1255
                    "STATISTICS_MAXIMUM", pszValue);
3✔
1256

1257
            pszValue = poAll->FindElem("Stats.MeanValue", iBand - 1);
3✔
1258

1259
            if (pszValue)
3✔
1260
                poDS->GetRasterBand(iBand)->SetMetadataItem("STATISTICS_MEAN",
3✔
1261
                                                            pszValue);
3✔
1262

1263
            pszValue = poAll->FindElem("Stats.MedianValue", iBand - 1);
3✔
1264

1265
            if (pszValue)
3✔
1266
                poDS->GetRasterBand(iBand)->SetMetadataItem("STATISTICS_MEDIAN",
3✔
1267
                                                            pszValue);
3✔
1268
        }
1269

1270
        CPLPopErrorHandler();
3✔
1271
    }
1272

1273
    /* -------------------------------------------------------------------- */
1274
    /*      Do we have GCPs.                                                */
1275
    /* -------------------------------------------------------------------- */
1276
    if (poHeader->FindNode("RasterInfo.WarpControl"))
61✔
1277
        poDS->ReadGCPs();
2✔
1278

1279
    /* -------------------------------------------------------------------- */
1280
    /*      Initialize any PAM information.                                 */
1281
    /* -------------------------------------------------------------------- */
1282
    poDS->SetDescription(poOpenInfo->pszFilename);
61✔
1283
    poDS->TryLoadXML();
61✔
1284

1285
    // if no SR in xml, try aux
1286
    const OGRSpatialReference *poSRS = poDS->GDALPamDataset::GetSpatialRef();
61✔
1287
    if (poSRS == nullptr)
61✔
1288
    {
1289
        // try aux
1290
        auto poAuxDS = std::unique_ptr<GDALDataset>(GDALFindAssociatedAuxFile(
1291
            poOpenInfo->pszFilename, GA_ReadOnly, poDS.get()));
122✔
1292
        if (poAuxDS)
61✔
1293
        {
1294
            poSRS = poAuxDS->GetSpatialRef();
×
1295
            if (poSRS)
×
1296
            {
1297
                poDS->m_oSRS = *poSRS;
×
1298
            }
1299
        }
1300
    }
1301
    /* -------------------------------------------------------------------- */
1302
    /*      Check for overviews.                                            */
1303
    /* -------------------------------------------------------------------- */
1304
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
61✔
1305

1306
    return poDS.release();
61✔
1307
}
1308

1309
/************************************************************************/
1310
/*                               Create()                               */
1311
/************************************************************************/
1312

1313
GDALDataset *ERSDataset::Create(const char *pszFilename, int nXSize, int nYSize,
67✔
1314
                                int nBandsIn, GDALDataType eType,
1315
                                char **papszOptions)
1316

1317
{
1318
    /* -------------------------------------------------------------------- */
1319
    /*      Verify settings.                                                */
1320
    /* -------------------------------------------------------------------- */
1321
    if (nBandsIn <= 0)
67✔
1322
    {
1323
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
1324
                 "ERS driver does not support %d bands.\n", nBandsIn);
1325
        return nullptr;
1✔
1326
    }
1327

1328
    if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_Int16 &&
66✔
1329
        eType != GDT_UInt16 && eType != GDT_Int32 && eType != GDT_UInt32 &&
29✔
1330
        eType != GDT_Float32 && eType != GDT_Float64)
19✔
1331
    {
1332
        CPLError(
16✔
1333
            CE_Failure, CPLE_AppDefined,
1334
            "The ERS driver does not supporting creating files of types %s.",
1335
            GDALGetDataTypeName(eType));
1336
        return nullptr;
16✔
1337
    }
1338

1339
    /* -------------------------------------------------------------------- */
1340
    /*      Work out the name we want to use for the .ers and binary        */
1341
    /*      data files.                                                     */
1342
    /* -------------------------------------------------------------------- */
1343
    CPLString osBinFile, osErsFile;
100✔
1344

1345
    if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ers"))
50✔
1346
    {
1347
        osErsFile = pszFilename;
7✔
1348
        osBinFile = osErsFile.substr(0, osErsFile.length() - 4);
7✔
1349
    }
1350
    else
1351
    {
1352
        osBinFile = pszFilename;
43✔
1353
        osErsFile = osBinFile + ".ers";
43✔
1354
    }
1355

1356
    /* -------------------------------------------------------------------- */
1357
    /*      Work out some values we will write.                             */
1358
    /* -------------------------------------------------------------------- */
1359
    const char *pszCellType = "Unsigned8BitInteger";
50✔
1360
    CPL_IGNORE_RET_VAL(pszCellType);  // Make CSA happy
50✔
1361

1362
    if (eType == GDT_Byte)
50✔
1363
        pszCellType = "Unsigned8BitInteger";
28✔
1364
    else if (eType == GDT_Int8)
22✔
1365
        pszCellType = "Signed8BitInteger";
3✔
1366
    else if (eType == GDT_Int16)
19✔
1367
        pszCellType = "Signed16BitInteger";
3✔
1368
    else if (eType == GDT_UInt16)
16✔
1369
        pszCellType = "Unsigned16BitInteger";
3✔
1370
    else if (eType == GDT_Int32)
13✔
1371
        pszCellType = "Signed32BitInteger";
3✔
1372
    else if (eType == GDT_UInt32)
10✔
1373
        pszCellType = "Unsigned32BitInteger";
3✔
1374
    else if (eType == GDT_Float32)
7✔
1375
        pszCellType = "IEEE4ByteReal";
4✔
1376
    else if (eType == GDT_Float64)
3✔
1377
        pszCellType = "IEEE8ByteReal";
3✔
1378
    else
1379
    {
1380
        CPLAssert(false);
×
1381
    }
1382

1383
    /* -------------------------------------------------------------------- */
1384
    /*      Handling for signed eight bit data.                             */
1385
    /* -------------------------------------------------------------------- */
1386
    const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
50✔
1387
    if (pszPixelType && EQUAL(pszPixelType, "SIGNEDBYTE") && eType == GDT_Byte)
50✔
1388
        pszCellType = "Signed8BitInteger";
×
1389

1390
    /* -------------------------------------------------------------------- */
1391
    /*      Write binary file.                                              */
1392
    /* -------------------------------------------------------------------- */
1393
    VSILFILE *fpBin = VSIFOpenL(osBinFile, "w");
50✔
1394

1395
    if (fpBin == nullptr)
50✔
1396
    {
1397
        CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
3✔
1398
                 osBinFile.c_str(), VSIStrerror(errno));
3✔
1399
        return nullptr;
3✔
1400
    }
1401

1402
    const GUIntBig nSize = static_cast<GUIntBig>(nXSize) * nYSize * nBandsIn *
47✔
1403
                           GDALGetDataTypeSizeBytes(eType);
47✔
1404
    GByte byZero = 0;
47✔
1405
    if (VSIFSeekL(fpBin, nSize - 1, SEEK_SET) != 0 ||
94✔
1406
        VSIFWriteL(&byZero, 1, 1, fpBin) != 1)
47✔
1407
    {
1408
        CPLError(CE_Failure, CPLE_FileIO, "Failed to write %s:\n%s",
10✔
1409
                 osBinFile.c_str(), VSIStrerror(errno));
10✔
1410
        VSIFCloseL(fpBin);
10✔
1411
        return nullptr;
10✔
1412
    }
1413
    VSIFCloseL(fpBin);
37✔
1414

1415
    /* -------------------------------------------------------------------- */
1416
    /*      Try writing header file.                                        */
1417
    /* -------------------------------------------------------------------- */
1418
    VSILFILE *fpERS = VSIFOpenL(osErsFile, "w");
37✔
1419

1420
    if (fpERS == nullptr)
37✔
1421
    {
1422
        CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
×
1423
                 osErsFile.c_str(), VSIStrerror(errno));
×
1424
        return nullptr;
×
1425
    }
1426

1427
    VSIFPrintfL(fpERS, "DatasetHeader Begin\n");
37✔
1428
    VSIFPrintfL(fpERS, "\tVersion\t\t = \"6.0\"\n");
37✔
1429
    VSIFPrintfL(fpERS, "\tName\t\t= \"%s\"\n", CPLGetFilename(osErsFile));
37✔
1430

1431
    // Last updated requires timezone info which we don't necessarily get
1432
    // get from VSICTime() so perhaps it is better to omit this.
1433
    //    VSIFPrintfL( fpERS, "\tLastUpdated\t= %s",
1434
    //                 VSICTime( VSITime( NULL ) ) );
1435

1436
    VSIFPrintfL(fpERS, "\tDataSetType\t= ERStorage\n");
37✔
1437
    VSIFPrintfL(fpERS, "\tDataType\t= Raster\n");
37✔
1438
    VSIFPrintfL(fpERS, "\tByteOrder\t= LSBFirst\n");
37✔
1439
    VSIFPrintfL(fpERS, "\tRasterInfo Begin\n");
37✔
1440
    VSIFPrintfL(fpERS, "\t\tCellType\t= %s\n", pszCellType);
37✔
1441
    VSIFPrintfL(fpERS, "\t\tNrOfLines\t= %d\n", nYSize);
37✔
1442
    VSIFPrintfL(fpERS, "\t\tNrOfCellsPerLine\t= %d\n", nXSize);
37✔
1443
    VSIFPrintfL(fpERS, "\t\tNrOfBands\t= %d\n", nBandsIn);
37✔
1444
    VSIFPrintfL(fpERS, "\tRasterInfo End\n");
37✔
1445
    if (VSIFPrintfL(fpERS, "DatasetHeader End\n") < 17)
37✔
1446
    {
1447
        CPLError(CE_Failure, CPLE_FileIO, "Failed to write %s:\n%s",
×
1448
                 osErsFile.c_str(), VSIStrerror(errno));
×
1449
        return nullptr;
×
1450
    }
1451

1452
    VSIFCloseL(fpERS);
37✔
1453

1454
    /* -------------------------------------------------------------------- */
1455
    /*      Reopen.                                                         */
1456
    /* -------------------------------------------------------------------- */
1457
    GDALOpenInfo oOpenInfo(osErsFile, GA_Update);
74✔
1458
    ERSDataset *poDS = cpl::down_cast<ERSDataset *>(Open(&oOpenInfo));
37✔
1459
    if (poDS == nullptr)
37✔
1460
        return nullptr;
×
1461

1462
    /* -------------------------------------------------------------------- */
1463
    /*      Fetch DATUM, PROJ and UNITS creation option                     */
1464
    /* -------------------------------------------------------------------- */
1465
    const char *pszDatum = CSLFetchNameValue(papszOptions, "DATUM");
37✔
1466
    if (pszDatum)
37✔
1467
    {
1468
        poDS->osDatumForced = pszDatum;
2✔
1469
        poDS->osDatum = pszDatum;
2✔
1470
    }
1471
    const char *pszProj = CSLFetchNameValue(papszOptions, "PROJ");
37✔
1472
    if (pszProj)
37✔
1473
    {
1474
        poDS->osProjForced = pszProj;
2✔
1475
        poDS->osProj = pszProj;
2✔
1476
    }
1477
    const char *pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
37✔
1478
    if (pszUnits)
37✔
1479
    {
1480
        poDS->osUnitsForced = pszUnits;
2✔
1481
        poDS->osUnits = pszUnits;
2✔
1482
    }
1483

1484
    if (pszDatum || pszProj || pszUnits)
37✔
1485
    {
1486
        poDS->WriteProjectionInfo(pszProj ? pszProj : "RAW",
2✔
1487
                                  pszDatum ? pszDatum : "RAW",
1488
                                  pszUnits ? pszUnits : "METERS");
1489
    }
1490

1491
    return poDS;
37✔
1492
}
1493

1494
/************************************************************************/
1495
/*                         GDALRegister_ERS()                           */
1496
/************************************************************************/
1497

1498
void GDALRegister_ERS()
1,911✔
1499

1500
{
1501
    if (GDALGetDriverByName("ERS") != nullptr)
1,911✔
1502
        return;
282✔
1503

1504
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1505

1506
    poDriver->SetDescription("ERS");
1,629✔
1507
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1508
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ERMapper .ers Labelled");
1,629✔
1509
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ers.html");
1,629✔
1510
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ers");
1,629✔
1511
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1,629✔
1512
                              "Byte Int8 Int16 UInt16 Int32 UInt32 "
1513
                              "Float32 Float64");
1,629✔
1514

1515
    poDriver->SetMetadataItem(
1,629✔
1516
        GDAL_DMD_CREATIONOPTIONLIST,
1517
        "<CreationOptionList>"
1518
        "   <Option name='PIXELTYPE' type='string' description='(deprecated, "
1519
        "use Int8 datatype) By setting this to SIGNEDBYTE, a new Byte file can "
1520
        "be forced to be written as signed byte'/>"
1521
        "   <Option name='PROJ' type='string' description='ERS Projection "
1522
        "Name'/>"
1523
        "   <Option name='DATUM' type='string' description='ERS Datum Name' />"
1524
        "   <Option name='UNITS' type='string-select' description='ERS "
1525
        "Projection Units'>"
1526
        "       <Value>METERS</Value>"
1527
        "       <Value>FEET</Value>"
1528
        "   </Option>"
1529
        "</CreationOptionList>");
1,629✔
1530

1531
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1532

1533
    poDriver->pfnOpen = ERSDataset::Open;
1,629✔
1534
    poDriver->pfnIdentify = ERSDataset::Identify;
1,629✔
1535
    poDriver->pfnCreate = ERSDataset::Create;
1,629✔
1536

1537
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1538
}
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