Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/raw/landataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  eCognition
4
 * Purpose:  Implementation of Erdas .LAN / .GIS format.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2004, Frank Warmerdam
9
 * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_string.h"
15
#include "gdal_frmts.h"
16
#include "gdal_priv.h"
17
#include "ogr_spatialref.h"
18
#include "rawdataset.h"
19
20
#include <cmath>
21
22
#include <algorithm>
23
24
/**
25
26
Erdas Header format: "HEAD74"
27
28
Offset   Size    Type      Description
29
------   ----    ----      -----------
30
0          6     char      magic cookie / version (i.e. HEAD74).
31
6          2    Int16      Pixel type, 0=8bit, 1=4bit, 2=16bit
32
8          2    Int16      Number of Bands.
33
10         6     char      Unknown.
34
16         4    Int32      Width
35
20         4    Int32      Height
36
24         4    Int32      X Start (offset in original file?)
37
28         4    Int32      Y Start (offset in original file?)
38
32        56     char      Unknown.
39
88         2    Int16      0=LAT, 1=UTM, 2=StatePlane, 3- are projections?
40
90         2    Int16      Classes in coverage.
41
92        14     char      Unknown.
42
106        2    Int16      Area Unit (0=none, 1=Acre, 2=Hectare, 3=Other)
43
108        4  Float32      Pixel area.
44
112        4  Float32      Upper Left corner X (center of pixel?)
45
116        4  Float32      Upper Left corner Y (center of pixel?)
46
120        4  Float32      Width of a pixel.
47
124        4  Float32      Height of a pixel.
48
49
Erdas Header format: "HEADER"
50
51
Offset   Size    Type      Description
52
------   ----    ----      -----------
53
0          6     char      magic cookie / version (i.e. HEAD74).
54
6          2    Int16      Pixel type, 0=8bit, 1=4bit, 2=16bit
55
8          2    Int16      Number of Bands.
56
10         6     char      Unknown.
57
16         4  Float32      Width
58
20         4  Float32      Height
59
24         4    Int32      X Start (offset in original file?)
60
28         4    Int32      Y Start (offset in original file?)
61
32        56     char      Unknown.
62
88         2    Int16      0=LAT, 1=UTM, 2=StatePlane, 3- are projections?
63
90         2    Int16      Classes in coverage.
64
92        14     char      Unknown.
65
106        2    Int16      Area Unit (0=none, 1=Acre, 2=Hectare, 3=Other)
66
108        4  Float32      Pixel area.
67
112        4  Float32      Upper Left corner X (center of pixel?)
68
116        4  Float32      Upper Left corner Y (center of pixel?)
69
120        4  Float32      Width of a pixel.
70
124        4  Float32      Height of a pixel.
71
72
All binary fields are in the same byte order but it may be big endian or
73
little endian depending on what platform the file was written on.  Usually
74
this can be checked against the number of bands though this test won't work
75
if there are more than 255 bands.
76
77
There is also some information on .STA and .TRL files at:
78
79
  http://www.pcigeomatics.com/cgi-bin/pcihlp/ERDASWR%7CTRAILER+FORMAT
80
81
**/
82
83
constexpr int ERD_HEADER_SIZE = 128;
84
85
/************************************************************************/
86
/* ==================================================================== */
87
/*                         LAN4BitRasterBand                            */
88
/* ==================================================================== */
89
/************************************************************************/
90
91
class LANDataset;
92
93
class LAN4BitRasterBand final : public GDALPamRasterBand
94
{
95
    GDALColorTable *poCT;
96
    GDALColorInterp eInterp;
97
98
    CPL_DISALLOW_COPY_ASSIGN(LAN4BitRasterBand)
99
100
  public:
101
    LAN4BitRasterBand(LANDataset *, int);
102
    ~LAN4BitRasterBand() override;
103
104
    GDALColorTable *GetColorTable() override;
105
    GDALColorInterp GetColorInterpretation() override;
106
    CPLErr SetColorTable(GDALColorTable *) override;
107
    CPLErr SetColorInterpretation(GDALColorInterp) override;
108
109
    CPLErr IReadBlock(int, int, void *) override;
110
};
111
112
/************************************************************************/
113
/* ==================================================================== */
114
/*                              LANDataset                              */
115
/* ==================================================================== */
116
/************************************************************************/
117
118
class LANDataset final : public RawDataset
119
{
120
    CPL_DISALLOW_COPY_ASSIGN(LANDataset)
121
122
  public:
123
    VSILFILE *fpImage;  // Image data file.
124
125
    char pachHeader[ERD_HEADER_SIZE];
126
127
    OGRSpatialReference *m_poSRS = nullptr;
128
129
    GDALGeoTransform m_gt{};
130
131
    CPLString osSTAFilename{};
132
    void CheckForStatistics(void);
133
134
    char **GetFileList() override;
135
136
    CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
137
138
  public:
139
    LANDataset();
140
    ~LANDataset() override;
141
142
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
143
144
    const OGRSpatialReference *GetSpatialRef() const override;
145
146
    static GDALDataset *Open(GDALOpenInfo *);
147
};
148
149
/************************************************************************/
150
/* ==================================================================== */
151
/*                         LAN4BitRasterBand                            */
152
/* ==================================================================== */
153
/************************************************************************/
154
155
/************************************************************************/
156
/*                         LAN4BitRasterBand()                          */
157
/************************************************************************/
158
159
LAN4BitRasterBand::LAN4BitRasterBand(LANDataset *poDSIn, int nBandIn)
160
1.10k
    : poCT(nullptr), eInterp(GCI_Undefined)
161
1.10k
{
162
1.10k
    poDS = poDSIn;
163
1.10k
    nBand = nBandIn;
164
1.10k
    eDataType = GDT_UInt8;
165
166
1.10k
    nBlockXSize = poDSIn->GetRasterXSize();
167
1.10k
    nBlockYSize = 1;
168
1.10k
}
169
170
/************************************************************************/
171
/*                         ~LAN4BitRasterBand()                         */
172
/************************************************************************/
173
174
LAN4BitRasterBand::~LAN4BitRasterBand()
175
176
1.10k
{
177
1.10k
    if (poCT)
178
0
        delete poCT;
179
1.10k
}
180
181
/************************************************************************/
182
/*                             IReadBlock()                             */
183
/************************************************************************/
184
185
CPLErr LAN4BitRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
186
                                     void *pImage)
187
188
8.33k
{
189
8.33k
    LANDataset *poLAN_DS = cpl::down_cast<LANDataset *>(poDS);
190
8.33k
    CPLAssert(nBlockXOff == 0);
191
192
    /* -------------------------------------------------------------------- */
193
    /*      Seek to profile.                                                */
194
    /* -------------------------------------------------------------------- */
195
8.33k
    const vsi_l_offset nOffset =
196
8.33k
        ERD_HEADER_SIZE +
197
8.33k
        (static_cast<vsi_l_offset>(nBlockYOff) * nRasterXSize *
198
8.33k
         poLAN_DS->GetRasterCount()) /
199
8.33k
            2 +
200
8.33k
        (static_cast<vsi_l_offset>(nBand - 1) * nRasterXSize) / 2;
201
202
8.33k
    if (VSIFSeekL(poLAN_DS->fpImage, nOffset, SEEK_SET) != 0)
203
0
    {
204
0
        CPLError(CE_Failure, CPLE_FileIO, "LAN Seek failed:%s",
205
0
                 VSIStrerror(errno));
206
0
        return CE_Failure;
207
0
    }
208
209
    /* -------------------------------------------------------------------- */
210
    /*      Read the profile.                                               */
211
    /* -------------------------------------------------------------------- */
212
8.33k
    if (VSIFReadL(pImage, 1, nRasterXSize / 2, poLAN_DS->fpImage) !=
213
8.33k
        static_cast<size_t>(nRasterXSize) / 2)
214
143
    {
215
143
        CPLError(CE_Failure, CPLE_FileIO, "LAN Read failed:%s",
216
143
                 VSIStrerror(errno));
217
143
        return CE_Failure;
218
143
    }
219
220
    /* -------------------------------------------------------------------- */
221
    /*      Convert 4bit to 8bit.                                           */
222
    /* -------------------------------------------------------------------- */
223
122k
    for (int i = nRasterXSize - 1; i >= 0; i--)
224
114k
    {
225
114k
        if ((i & 0x01) != 0)
226
57.3k
            reinterpret_cast<GByte *>(pImage)[i] =
227
57.3k
                reinterpret_cast<GByte *>(pImage)[i / 2] & 0x0f;
228
57.4k
        else
229
57.4k
            reinterpret_cast<GByte *>(pImage)[i] =
230
57.4k
                (reinterpret_cast<GByte *>(pImage)[i / 2] & 0xf0) / 16;
231
114k
    }
232
233
8.18k
    return CE_None;
234
8.33k
}
235
236
/************************************************************************/
237
/*                           SetColorTable()                            */
238
/************************************************************************/
239
240
CPLErr LAN4BitRasterBand::SetColorTable(GDALColorTable *poNewCT)
241
242
0
{
243
0
    if (poCT)
244
0
        delete poCT;
245
0
    if (poNewCT == nullptr)
246
0
        poCT = nullptr;
247
0
    else
248
0
        poCT = poNewCT->Clone();
249
250
0
    return CE_None;
251
0
}
252
253
/************************************************************************/
254
/*                           GetColorTable()                            */
255
/************************************************************************/
256
257
GDALColorTable *LAN4BitRasterBand::GetColorTable()
258
259
0
{
260
0
    if (poCT != nullptr)
261
0
        return poCT;
262
263
0
    return GDALPamRasterBand::GetColorTable();
264
0
}
265
266
/************************************************************************/
267
/*                       SetColorInterpretation()                       */
268
/************************************************************************/
269
270
CPLErr LAN4BitRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp)
271
272
0
{
273
0
    eInterp = eNewInterp;
274
275
0
    return CE_None;
276
0
}
277
278
/************************************************************************/
279
/*                       GetColorInterpretation()                       */
280
/************************************************************************/
281
282
GDALColorInterp LAN4BitRasterBand::GetColorInterpretation()
283
284
20
{
285
20
    return eInterp;
286
20
}
287
288
/************************************************************************/
289
/* ==================================================================== */
290
/*                              LANDataset                              */
291
/* ==================================================================== */
292
/************************************************************************/
293
294
/************************************************************************/
295
/*                             LANDataset()                             */
296
/************************************************************************/
297
298
128
LANDataset::LANDataset() : fpImage(nullptr)
299
128
{
300
128
    memset(pachHeader, 0, sizeof(pachHeader));
301
128
}
302
303
/************************************************************************/
304
/*                            ~LANDataset()                             */
305
/************************************************************************/
306
307
LANDataset::~LANDataset()
308
309
128
{
310
128
    LANDataset::Close();
311
128
}
312
313
/************************************************************************/
314
/*                               Close()                                */
315
/************************************************************************/
316
317
CPLErr LANDataset::Close(GDALProgressFunc, void *)
318
205
{
319
205
    CPLErr eErr = CE_None;
320
205
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
321
128
    {
322
128
        if (LANDataset::FlushCache(true) != CE_None)
323
0
            eErr = CE_Failure;
324
325
128
        if (fpImage)
326
128
        {
327
128
            if (VSIFCloseL(fpImage) != 0)
328
0
            {
329
0
                CPLError(CE_Failure, CPLE_FileIO, "I/O error");
330
0
                eErr = CE_Failure;
331
0
            }
332
128
        }
333
334
128
        if (m_poSRS)
335
77
            m_poSRS->Release();
336
337
128
        if (GDALPamDataset::Close() != CE_None)
338
0
            eErr = CE_Failure;
339
128
    }
340
205
    return eErr;
341
205
}
342
343
/************************************************************************/
344
/*                                Open()                                */
345
/************************************************************************/
346
347
GDALDataset *LANDataset::Open(GDALOpenInfo *poOpenInfo)
348
349
465k
{
350
    /* -------------------------------------------------------------------- */
351
    /*      We assume the user is pointing to the header (.pcb) file.       */
352
    /*      Does this appear to be a pcb file?                              */
353
    /* -------------------------------------------------------------------- */
354
465k
    if (poOpenInfo->nHeaderBytes < ERD_HEADER_SIZE ||
355
83.7k
        poOpenInfo->fpL == nullptr)
356
381k
        return nullptr;
357
358
83.7k
    if (!STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
359
83.6k
                        "HEADER") &&
360
83.6k
        !STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
361
83.7k
                        "HEAD74"))
362
83.6k
        return nullptr;
363
364
128
    if (memcmp(poOpenInfo->pabyHeader + 16, "S LAT   ", 8) == 0)
365
0
    {
366
        // NTV1 format
367
0
        return nullptr;
368
0
    }
369
370
    /* -------------------------------------------------------------------- */
371
    /*      Create a corresponding GDALDataset.                             */
372
    /* -------------------------------------------------------------------- */
373
128
    auto poDS = std::make_unique<LANDataset>();
374
375
128
    poDS->eAccess = poOpenInfo->eAccess;
376
128
    std::swap(poDS->fpImage, poOpenInfo->fpL);
377
378
    /* -------------------------------------------------------------------- */
379
    /*      Do we need to byte swap the headers to local machine order?     */
380
    /* -------------------------------------------------------------------- */
381
128
    const RawRasterBand::ByteOrder eByteOrder =
382
128
        poOpenInfo->pabyHeader[8] == 0
383
128
            ? RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN
384
128
            : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
385
386
128
    memcpy(poDS->pachHeader, poOpenInfo->pabyHeader, ERD_HEADER_SIZE);
387
388
128
    if (eByteOrder != RawRasterBand::NATIVE_BYTE_ORDER)
389
103
    {
390
103
        CPL_SWAP16PTR(poDS->pachHeader + 6);
391
103
        CPL_SWAP16PTR(poDS->pachHeader + 8);
392
393
103
        CPL_SWAP32PTR(poDS->pachHeader + 16);
394
103
        CPL_SWAP32PTR(poDS->pachHeader + 20);
395
103
        CPL_SWAP32PTR(poDS->pachHeader + 24);
396
103
        CPL_SWAP32PTR(poDS->pachHeader + 28);
397
398
103
        CPL_SWAP16PTR(poDS->pachHeader + 88);
399
103
        CPL_SWAP16PTR(poDS->pachHeader + 90);
400
401
103
        CPL_SWAP16PTR(poDS->pachHeader + 106);
402
103
        CPL_SWAP32PTR(poDS->pachHeader + 108);
403
103
        CPL_SWAP32PTR(poDS->pachHeader + 112);
404
103
        CPL_SWAP32PTR(poDS->pachHeader + 116);
405
103
        CPL_SWAP32PTR(poDS->pachHeader + 120);
406
103
        CPL_SWAP32PTR(poDS->pachHeader + 124);
407
103
    }
408
409
    /* -------------------------------------------------------------------- */
410
    /*      Capture some information from the file that is of interest.     */
411
    /* -------------------------------------------------------------------- */
412
128
    if (STARTS_WITH_CI(poDS->pachHeader, "HEADER"))
413
128
    {
414
128
        float fTmp = 0.0;
415
128
        memcpy(&fTmp, poDS->pachHeader + 16, 4);
416
128
        poDS->nRasterXSize = static_cast<int>(fTmp);
417
128
        memcpy(&fTmp, poDS->pachHeader + 20, 4);
418
128
        poDS->nRasterYSize = static_cast<int>(fTmp);
419
128
    }
420
0
    else
421
0
    {
422
0
        GInt32 nTmp = 0;
423
0
        memcpy(&nTmp, poDS->pachHeader + 16, 4);
424
0
        poDS->nRasterXSize = nTmp;
425
0
        memcpy(&nTmp, poDS->pachHeader + 20, 4);
426
0
        poDS->nRasterYSize = nTmp;
427
0
    }
428
429
128
    GInt16 nTmp16 = 0;
430
128
    memcpy(&nTmp16, poDS->pachHeader + 6, 2);
431
432
128
    int nPixelOffset = 0;
433
128
    GDALDataType eDataType = GDT_Unknown;
434
128
    if (nTmp16 == 0)
435
37
    {
436
37
        eDataType = GDT_UInt8;
437
37
        nPixelOffset = 1;
438
37
    }
439
91
    else if (nTmp16 == 1)  // 4 bit
440
61
    {
441
61
        eDataType = GDT_UInt8;
442
61
        nPixelOffset = -1;
443
61
    }
444
30
    else if (nTmp16 == 2)
445
13
    {
446
13
        nPixelOffset = 2;
447
13
        eDataType = GDT_Int16;
448
13
    }
449
17
    else
450
17
    {
451
17
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported pixel type (%d).",
452
17
                 nTmp16);
453
17
        return nullptr;
454
17
    }
455
456
111
    memcpy(&nTmp16, poDS->pachHeader + 8, 2);
457
111
    const int nBandCount = nTmp16;
458
459
111
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
460
78
        !GDALCheckBandCount(nBandCount, FALSE))
461
34
    {
462
34
        return nullptr;
463
34
    }
464
465
    // cppcheck-suppress knownConditionTrueFalse
466
77
    if (nPixelOffset != -1 &&
467
31
        poDS->nRasterXSize > INT_MAX / (nPixelOffset * nBandCount))
468
0
    {
469
0
        CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
470
0
        return nullptr;
471
0
    }
472
473
    /* -------------------------------------------------------------------- */
474
    /*      Create band information object.                                 */
475
    /* -------------------------------------------------------------------- */
476
2.02k
    for (int iBand = 1; iBand <= nBandCount; iBand++)
477
1.94k
    {
478
1.94k
        if (nPixelOffset == -1) /* 4 bit case */
479
1.10k
            poDS->SetBand(iBand, new LAN4BitRasterBand(poDS.get(), iBand));
480
837
        else
481
837
        {
482
837
            auto poBand = RawRasterBand::Create(
483
837
                poDS.get(), iBand, poDS->fpImage,
484
837
                ERD_HEADER_SIZE +
485
837
                    (iBand - 1) * nPixelOffset * poDS->nRasterXSize,
486
837
                nPixelOffset, poDS->nRasterXSize * nPixelOffset * nBandCount,
487
837
                eDataType, eByteOrder, RawRasterBand::OwnFP::NO);
488
837
            if (!poBand)
489
0
                return nullptr;
490
837
            poDS->SetBand(iBand, std::move(poBand));
491
837
        }
492
1.94k
    }
493
494
    /* -------------------------------------------------------------------- */
495
    /*      Initialize any PAM information.                                 */
496
    /* -------------------------------------------------------------------- */
497
77
    poDS->SetDescription(poOpenInfo->pszFilename);
498
77
    poDS->CheckForStatistics();
499
77
    poDS->TryLoadXML();
500
501
    /* -------------------------------------------------------------------- */
502
    /*      Check for overviews.                                            */
503
    /* -------------------------------------------------------------------- */
504
77
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
505
506
    /* -------------------------------------------------------------------- */
507
    /*      Try to interpret georeferencing.                                */
508
    /* -------------------------------------------------------------------- */
509
77
    float fTmp = 0.0;
510
511
77
    memcpy(&fTmp, poDS->pachHeader + 112, 4);
512
77
    poDS->m_gt.xorig = fTmp;
513
77
    memcpy(&fTmp, poDS->pachHeader + 120, 4);
514
77
    poDS->m_gt.xscale = fTmp;
515
77
    poDS->m_gt.xrot = 0.0;
516
77
    memcpy(&fTmp, poDS->pachHeader + 116, 4);
517
77
    poDS->m_gt.yorig = fTmp;
518
77
    poDS->m_gt.yrot = 0.0;
519
77
    memcpy(&fTmp, poDS->pachHeader + 124, 4);
520
77
    poDS->m_gt.yscale = -fTmp;
521
522
    // adjust for center of pixel vs. top left corner of pixel.
523
77
    poDS->m_gt.xorig -= poDS->m_gt.xscale * 0.5;
524
77
    poDS->m_gt.yorig -= poDS->m_gt.yscale * 0.5;
525
526
    /* -------------------------------------------------------------------- */
527
    /*      If we didn't get any georeferencing, try for a worldfile.       */
528
    /* -------------------------------------------------------------------- */
529
77
    if (poDS->m_gt.xscale == 0.0 || poDS->m_gt.yscale == 0.0)
530
32
    {
531
32
        if (!GDALReadWorldFile(poOpenInfo->pszFilename, nullptr,
532
32
                               poDS->m_gt.data()))
533
32
            GDALReadWorldFile(poOpenInfo->pszFilename, ".wld",
534
32
                              poDS->m_gt.data());
535
32
    }
536
537
    /* -------------------------------------------------------------------- */
538
    /*      Try to come up with something for the coordinate system.        */
539
    /* -------------------------------------------------------------------- */
540
77
    memcpy(&nTmp16, poDS->pachHeader + 88, 2);
541
77
    int nCoordSys = nTmp16;
542
543
77
    poDS->m_poSRS = new OGRSpatialReference();
544
77
    poDS->m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
545
77
    if (nCoordSys == 0)
546
25
    {
547
25
        poDS->m_poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
548
25
    }
549
52
    else if (nCoordSys == 1)
550
3
    {
551
3
        poDS->m_poSRS->SetFromUserInput(
552
3
            "LOCAL_CS[\"UTM - Zone Unknown\",UNIT[\"Meter\",1]]");
553
3
    }
554
49
    else if (nCoordSys == 2)
555
0
    {
556
0
        poDS->m_poSRS->SetFromUserInput(
557
0
            "LOCAL_CS[\"State Plane - Zone Unknown\","
558
0
            "UNIT[\"US survey foot\",0.3048006096012192]]");
559
0
    }
560
49
    else
561
49
    {
562
49
        poDS->m_poSRS->SetFromUserInput(
563
49
            "LOCAL_CS[\"Unknown\",UNIT[\"Meter\",1]]");
564
49
    }
565
566
    /* -------------------------------------------------------------------- */
567
    /*      Check for a trailer file with a colormap in it.                 */
568
    /* -------------------------------------------------------------------- */
569
77
    char *pszPath = CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
570
77
    char *pszBasename =
571
77
        CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str());
572
77
    const std::string osTRLFilename =
573
77
        CPLFormCIFilenameSafe(pszPath, pszBasename, "trl");
574
77
    VSILFILE *fpTRL = VSIFOpenL(osTRLFilename.c_str(), "rb");
575
77
    if (fpTRL != nullptr)
576
0
    {
577
0
        char szTRLData[896] = {'\0'};
578
579
0
        CPL_IGNORE_RET_VAL(VSIFReadL(szTRLData, 1, 896, fpTRL));
580
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpTRL));
581
582
0
        GDALColorTable oCT;
583
0
        for (int iColor = 0; iColor < 256; iColor++)
584
0
        {
585
0
            GDALColorEntry sEntry = {0, 0, 0, 0};
586
587
0
            sEntry.c2 = reinterpret_cast<GByte *>(szTRLData)[iColor + 128];
588
0
            sEntry.c1 =
589
0
                reinterpret_cast<GByte *>(szTRLData)[iColor + 128 + 256];
590
0
            sEntry.c3 =
591
0
                reinterpret_cast<GByte *>(szTRLData)[iColor + 128 + 512];
592
0
            sEntry.c4 = 255;
593
0
            oCT.SetColorEntry(iColor, &sEntry);
594
595
            // Only 16 colors in 4bit files.
596
0
            if (nPixelOffset == -1 && iColor == 15)
597
0
                break;
598
0
        }
599
600
0
        poDS->GetRasterBand(1)->SetColorTable(&oCT);
601
0
        poDS->GetRasterBand(1)->SetColorInterpretation(GCI_PaletteIndex);
602
0
    }
603
604
77
    CPLFree(pszPath);
605
77
    CPLFree(pszBasename);
606
607
77
    return poDS.release();
608
77
}
609
610
/************************************************************************/
611
/*                          GetGeoTransform()                           */
612
/************************************************************************/
613
614
CPLErr LANDataset::GetGeoTransform(GDALGeoTransform &gt) const
615
616
77
{
617
77
    if (m_gt.xscale != 0.0 && m_gt.yscale != 0.0)
618
45
    {
619
45
        gt = m_gt;
620
45
        return CE_None;
621
45
    }
622
623
32
    return GDALPamDataset::GetGeoTransform(gt);
624
77
}
625
626
/************************************************************************/
627
/*                          GetSpatialRef()                             */
628
/*                                                                      */
629
/*      Use PAM coordinate system if available in preference to the     */
630
/*      generally poor value derived from the file itself.              */
631
/************************************************************************/
632
633
const OGRSpatialReference *LANDataset::GetSpatialRef() const
634
635
75
{
636
75
    const auto poSRS = GDALPamDataset::GetSpatialRef();
637
75
    if (poSRS)
638
0
        return poSRS;
639
640
75
    return m_poSRS;
641
75
}
642
643
/************************************************************************/
644
/*                            GetFileList()                             */
645
/************************************************************************/
646
647
char **LANDataset::GetFileList()
648
649
94
{
650
    // Main data file, etc.
651
94
    char **papszFileList = GDALPamDataset::GetFileList();
652
653
94
    if (!osSTAFilename.empty())
654
0
        papszFileList = CSLAddString(papszFileList, osSTAFilename);
655
656
94
    return papszFileList;
657
94
}
658
659
/************************************************************************/
660
/*                         CheckForStatistics()                         */
661
/************************************************************************/
662
663
void LANDataset::CheckForStatistics()
664
665
77
{
666
    /* -------------------------------------------------------------------- */
667
    /*      Do we have a statistics file?                                   */
668
    /* -------------------------------------------------------------------- */
669
77
    osSTAFilename = CPLResetExtensionSafe(GetDescription(), "sta");
670
671
77
    VSILFILE *fpSTA = VSIFOpenL(osSTAFilename, "r");
672
673
77
    if (fpSTA == nullptr && VSIIsCaseSensitiveFS(osSTAFilename))
674
77
    {
675
77
        osSTAFilename = CPLResetExtensionSafe(GetDescription(), "STA");
676
77
        fpSTA = VSIFOpenL(osSTAFilename, "r");
677
77
    }
678
679
77
    if (fpSTA == nullptr)
680
77
    {
681
77
        osSTAFilename = "";
682
77
        return;
683
77
    }
684
685
    /* -------------------------------------------------------------------- */
686
    /*      Read it one band at a time.                                     */
687
    /* -------------------------------------------------------------------- */
688
0
    GByte abyBandInfo[1152] = {'\0'};
689
690
0
    for (int iBand = 0; iBand < nBands; iBand++)
691
0
    {
692
0
        if (VSIFReadL(abyBandInfo, 1152, 1, fpSTA) != 1)
693
0
            break;
694
695
0
        const int nBandNumber = abyBandInfo[7];
696
0
        GDALRasterBand *poBand = GetRasterBand(nBandNumber);
697
0
        if (poBand == nullptr)
698
0
            break;
699
700
0
        GInt16 nMin = 0;
701
0
        GInt16 nMax = 0;
702
703
0
        if (poBand->GetRasterDataType() != GDT_UInt8)
704
0
        {
705
0
            memcpy(&nMin, abyBandInfo + 28, 2);
706
0
            memcpy(&nMax, abyBandInfo + 30, 2);
707
0
            CPL_LSBPTR16(&nMin);
708
0
            CPL_LSBPTR16(&nMax);
709
0
        }
710
0
        else
711
0
        {
712
0
            nMin = abyBandInfo[9];
713
0
            nMax = abyBandInfo[8];
714
0
        }
715
716
0
        float fMean = 0.0;
717
0
        float fStdDev = 0.0;
718
0
        memcpy(&fMean, abyBandInfo + 12, 4);
719
0
        memcpy(&fStdDev, abyBandInfo + 24, 4);
720
0
        CPL_LSBPTR32(&fMean);
721
0
        CPL_LSBPTR32(&fStdDev);
722
723
0
        poBand->SetStatistics(nMin, nMax, fMean, fStdDev);
724
0
    }
725
726
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpSTA));
727
0
}
728
729
/************************************************************************/
730
/*                          GDALRegister_LAN()                          */
731
/************************************************************************/
732
733
void GDALRegister_LAN()
734
735
22
{
736
22
    if (GDALGetDriverByName("LAN") != nullptr)
737
0
        return;
738
739
22
    GDALDriver *poDriver = new GDALDriver();
740
741
22
    poDriver->SetDescription("LAN");
742
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
743
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Erdas .LAN/.GIS");
744
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/lan.html");
745
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
746
747
22
    poDriver->pfnOpen = LANDataset::Open;
748
749
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
750
22
}