Coverage Report

Created: 2026-08-14 09:29

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
971
    : poCT(nullptr), eInterp(GCI_Undefined)
161
971
{
162
971
    poDS = poDSIn;
163
971
    nBand = nBandIn;
164
971
    eDataType = GDT_UInt8;
165
166
971
    nBlockXSize = poDSIn->GetRasterXSize();
167
971
    nBlockYSize = 1;
168
971
}
169
170
/************************************************************************/
171
/*                         ~LAN4BitRasterBand()                         */
172
/************************************************************************/
173
174
LAN4BitRasterBand::~LAN4BitRasterBand()
175
176
971
{
177
971
    if (poCT)
178
0
        delete poCT;
179
971
}
180
181
/************************************************************************/
182
/*                             IReadBlock()                             */
183
/************************************************************************/
184
185
CPLErr LAN4BitRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
186
                                     void *pImage)
187
188
8.31k
{
189
8.31k
    LANDataset *poLAN_DS = cpl::down_cast<LANDataset *>(poDS);
190
8.31k
    CPLAssert(nBlockXOff == 0);
191
192
    /* -------------------------------------------------------------------- */
193
    /*      Seek to profile.                                                */
194
    /* -------------------------------------------------------------------- */
195
8.31k
    const vsi_l_offset nOffset =
196
8.31k
        ERD_HEADER_SIZE +
197
8.31k
        (static_cast<vsi_l_offset>(nBlockYOff) * nRasterXSize *
198
8.31k
         poLAN_DS->GetRasterCount()) /
199
8.31k
            2 +
200
8.31k
        (static_cast<vsi_l_offset>(nBand - 1) * nRasterXSize) / 2;
201
202
8.31k
    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.31k
    if (VSIFReadL(pImage, 1, nRasterXSize / 2, poLAN_DS->fpImage) !=
213
8.31k
        static_cast<size_t>(nRasterXSize) / 2)
214
132
    {
215
132
        CPLError(CE_Failure, CPLE_FileIO, "LAN Read failed:%s",
216
132
                 VSIStrerror(errno));
217
132
        return CE_Failure;
218
132
    }
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.3k
        else
229
57.3k
            reinterpret_cast<GByte *>(pImage)[i] =
230
57.3k
                (reinterpret_cast<GByte *>(pImage)[i / 2] & 0xf0) / 16;
231
114k
    }
232
233
8.18k
    return CE_None;
234
8.31k
}
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
124
LANDataset::LANDataset() : fpImage(nullptr)
299
124
{
300
124
    memset(pachHeader, 0, sizeof(pachHeader));
301
124
}
302
303
/************************************************************************/
304
/*                            ~LANDataset()                             */
305
/************************************************************************/
306
307
LANDataset::~LANDataset()
308
309
124
{
310
124
    LANDataset::Close();
311
124
}
312
313
/************************************************************************/
314
/*                               Close()                                */
315
/************************************************************************/
316
317
CPLErr LANDataset::Close(GDALProgressFunc, void *)
318
197
{
319
197
    CPLErr eErr = CE_None;
320
197
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
321
124
    {
322
124
        if (LANDataset::FlushCache(true) != CE_None)
323
0
            eErr = CE_Failure;
324
325
124
        if (fpImage)
326
124
        {
327
124
            if (VSIFCloseL(fpImage) != 0)
328
0
            {
329
0
                CPLError(CE_Failure, CPLE_FileIO, "I/O error");
330
0
                eErr = CE_Failure;
331
0
            }
332
124
        }
333
334
124
        if (m_poSRS)
335
73
            m_poSRS->Release();
336
337
124
        if (GDALPamDataset::Close() != CE_None)
338
0
            eErr = CE_Failure;
339
124
    }
340
197
    return eErr;
341
197
}
342
343
/************************************************************************/
344
/*                                Open()                                */
345
/************************************************************************/
346
347
GDALDataset *LANDataset::Open(GDALOpenInfo *poOpenInfo)
348
349
529k
{
350
    /* -------------------------------------------------------------------- */
351
    /*      We assume the user is pointing to the header (.pcb) file.       */
352
    /*      Does this appear to be a pcb file?                              */
353
    /* -------------------------------------------------------------------- */
354
529k
    if (poOpenInfo->nHeaderBytes < ERD_HEADER_SIZE ||
355
85.5k
        poOpenInfo->fpL == nullptr)
356
443k
        return nullptr;
357
358
85.5k
    if (!STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
359
85.4k
                        "HEADER") &&
360
85.4k
        !STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
361
85.5k
                        "HEAD74"))
362
85.4k
        return nullptr;
363
364
124
    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
124
    auto poDS = std::make_unique<LANDataset>();
374
375
124
    poDS->eAccess = poOpenInfo->eAccess;
376
124
    std::swap(poDS->fpImage, poOpenInfo->fpL);
377
378
    /* -------------------------------------------------------------------- */
379
    /*      Do we need to byte swap the headers to local machine order?     */
380
    /* -------------------------------------------------------------------- */
381
124
    const RawRasterBand::ByteOrder eByteOrder =
382
124
        poOpenInfo->pabyHeader[8] == 0
383
124
            ? RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN
384
124
            : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
385
386
124
    memcpy(poDS->pachHeader, poOpenInfo->pabyHeader, ERD_HEADER_SIZE);
387
388
124
    if (eByteOrder != RawRasterBand::NATIVE_BYTE_ORDER)
389
101
    {
390
101
        CPL_SWAP16PTR(poDS->pachHeader + 6);
391
101
        CPL_SWAP16PTR(poDS->pachHeader + 8);
392
393
101
        CPL_SWAP32PTR(poDS->pachHeader + 16);
394
101
        CPL_SWAP32PTR(poDS->pachHeader + 20);
395
101
        CPL_SWAP32PTR(poDS->pachHeader + 24);
396
101
        CPL_SWAP32PTR(poDS->pachHeader + 28);
397
398
101
        CPL_SWAP16PTR(poDS->pachHeader + 88);
399
101
        CPL_SWAP16PTR(poDS->pachHeader + 90);
400
401
101
        CPL_SWAP16PTR(poDS->pachHeader + 106);
402
101
        CPL_SWAP32PTR(poDS->pachHeader + 108);
403
101
        CPL_SWAP32PTR(poDS->pachHeader + 112);
404
101
        CPL_SWAP32PTR(poDS->pachHeader + 116);
405
101
        CPL_SWAP32PTR(poDS->pachHeader + 120);
406
101
        CPL_SWAP32PTR(poDS->pachHeader + 124);
407
101
    }
408
409
    /* -------------------------------------------------------------------- */
410
    /*      Capture some information from the file that is of interest.     */
411
    /* -------------------------------------------------------------------- */
412
124
    if (STARTS_WITH_CI(poDS->pachHeader, "HEADER"))
413
123
    {
414
123
        float fTmp = 0.0;
415
123
        memcpy(&fTmp, poDS->pachHeader + 16, 4);
416
123
        poDS->nRasterXSize = static_cast<int>(fTmp);
417
123
        memcpy(&fTmp, poDS->pachHeader + 20, 4);
418
123
        poDS->nRasterYSize = static_cast<int>(fTmp);
419
123
    }
420
1
    else
421
1
    {
422
1
        GInt32 nTmp = 0;
423
1
        memcpy(&nTmp, poDS->pachHeader + 16, 4);
424
1
        poDS->nRasterXSize = nTmp;
425
1
        memcpy(&nTmp, poDS->pachHeader + 20, 4);
426
1
        poDS->nRasterYSize = nTmp;
427
1
    }
428
429
124
    GInt16 nTmp16 = 0;
430
124
    memcpy(&nTmp16, poDS->pachHeader + 6, 2);
431
432
124
    int nPixelOffset = 0;
433
124
    GDALDataType eDataType = GDT_Unknown;
434
124
    if (nTmp16 == 0)
435
34
    {
436
34
        eDataType = GDT_UInt8;
437
34
        nPixelOffset = 1;
438
34
    }
439
90
    else if (nTmp16 == 1)  // 4 bit
440
55
    {
441
55
        eDataType = GDT_UInt8;
442
55
        nPixelOffset = -1;
443
55
    }
444
35
    else if (nTmp16 == 2)
445
18
    {
446
18
        nPixelOffset = 2;
447
18
        eDataType = GDT_Int16;
448
18
    }
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
107
    memcpy(&nTmp16, poDS->pachHeader + 8, 2);
457
107
    const int nBandCount = nTmp16;
458
459
107
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
460
74
        !GDALCheckBandCount(nBandCount, FALSE))
461
34
    {
462
34
        return nullptr;
463
34
    }
464
465
    // cppcheck-suppress knownConditionTrueFalse
466
73
    if (nPixelOffset != -1 &&
467
33
        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
1.88k
    for (int iBand = 1; iBand <= nBandCount; iBand++)
477
1.81k
    {
478
1.81k
        if (nPixelOffset == -1) /* 4 bit case */
479
971
            poDS->SetBand(iBand, new LAN4BitRasterBand(poDS.get(), iBand));
480
842
        else
481
842
        {
482
842
            auto poBand = RawRasterBand::Create(
483
842
                poDS.get(), iBand, poDS->fpImage,
484
842
                ERD_HEADER_SIZE +
485
842
                    (iBand - 1) * nPixelOffset * poDS->nRasterXSize,
486
842
                nPixelOffset, poDS->nRasterXSize * nPixelOffset * nBandCount,
487
842
                eDataType, eByteOrder, RawRasterBand::OwnFP::NO);
488
842
            if (!poBand)
489
0
                return nullptr;
490
842
            poDS->SetBand(iBand, std::move(poBand));
491
842
        }
492
1.81k
    }
493
494
    /* -------------------------------------------------------------------- */
495
    /*      Initialize any PAM information.                                 */
496
    /* -------------------------------------------------------------------- */
497
73
    poDS->SetDescription(poOpenInfo->pszFilename);
498
73
    poDS->CheckForStatistics();
499
73
    poDS->TryLoadXML();
500
501
    /* -------------------------------------------------------------------- */
502
    /*      Check for overviews.                                            */
503
    /* -------------------------------------------------------------------- */
504
73
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
505
506
    /* -------------------------------------------------------------------- */
507
    /*      Try to interpret georeferencing.                                */
508
    /* -------------------------------------------------------------------- */
509
73
    float fTmp = 0.0;
510
511
73
    memcpy(&fTmp, poDS->pachHeader + 112, 4);
512
73
    poDS->m_gt.xorig = fTmp;
513
73
    memcpy(&fTmp, poDS->pachHeader + 120, 4);
514
73
    poDS->m_gt.xscale = fTmp;
515
73
    poDS->m_gt.xrot = 0.0;
516
73
    memcpy(&fTmp, poDS->pachHeader + 116, 4);
517
73
    poDS->m_gt.yorig = fTmp;
518
73
    poDS->m_gt.yrot = 0.0;
519
73
    memcpy(&fTmp, poDS->pachHeader + 124, 4);
520
73
    poDS->m_gt.yscale = -fTmp;
521
522
    // adjust for center of pixel vs. top left corner of pixel.
523
73
    poDS->m_gt.xorig -= poDS->m_gt.xscale * 0.5;
524
73
    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
73
    if (poDS->m_gt.xscale == 0.0 || poDS->m_gt.yscale == 0.0)
530
30
    {
531
30
        if (!GDALReadWorldFile(poOpenInfo->pszFilename, nullptr,
532
30
                               poDS->m_gt.data()))
533
30
            GDALReadWorldFile(poOpenInfo->pszFilename, ".wld",
534
30
                              poDS->m_gt.data());
535
30
    }
536
537
    /* -------------------------------------------------------------------- */
538
    /*      Try to come up with something for the coordinate system.        */
539
    /* -------------------------------------------------------------------- */
540
73
    memcpy(&nTmp16, poDS->pachHeader + 88, 2);
541
73
    int nCoordSys = nTmp16;
542
543
73
    poDS->m_poSRS = new OGRSpatialReference();
544
73
    poDS->m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
545
73
    if (nCoordSys == 0)
546
24
    {
547
24
        poDS->m_poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
548
24
    }
549
49
    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
46
    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
46
    else
561
46
    {
562
46
        poDS->m_poSRS->SetFromUserInput(
563
46
            "LOCAL_CS[\"Unknown\",UNIT[\"Meter\",1]]");
564
46
    }
565
566
    /* -------------------------------------------------------------------- */
567
    /*      Check for a trailer file with a colormap in it.                 */
568
    /* -------------------------------------------------------------------- */
569
73
    char *pszPath = CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
570
73
    char *pszBasename =
571
73
        CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str());
572
73
    const std::string osTRLFilename =
573
73
        CPLFormCIFilenameSafe(pszPath, pszBasename, "trl");
574
73
    VSILFILE *fpTRL = VSIFOpenL(osTRLFilename.c_str(), "rb");
575
73
    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
73
    CPLFree(pszPath);
605
73
    CPLFree(pszBasename);
606
607
73
    return poDS.release();
608
73
}
609
610
/************************************************************************/
611
/*                          GetGeoTransform()                           */
612
/************************************************************************/
613
614
CPLErr LANDataset::GetGeoTransform(GDALGeoTransform &gt) const
615
616
73
{
617
73
    if (m_gt.xscale != 0.0 && m_gt.yscale != 0.0)
618
43
    {
619
43
        gt = m_gt;
620
43
        return CE_None;
621
43
    }
622
623
30
    return GDALPamDataset::GetGeoTransform(gt);
624
73
}
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
71
{
636
71
    const auto poSRS = GDALPamDataset::GetSpatialRef();
637
71
    if (poSRS)
638
0
        return poSRS;
639
640
71
    return m_poSRS;
641
71
}
642
643
/************************************************************************/
644
/*                            GetFileList()                             */
645
/************************************************************************/
646
647
char **LANDataset::GetFileList()
648
649
86
{
650
    // Main data file, etc.
651
86
    char **papszFileList = GDALPamDataset::GetFileList();
652
653
86
    if (!osSTAFilename.empty())
654
0
        papszFileList = CSLAddString(papszFileList, osSTAFilename);
655
656
86
    return papszFileList;
657
86
}
658
659
/************************************************************************/
660
/*                         CheckForStatistics()                         */
661
/************************************************************************/
662
663
void LANDataset::CheckForStatistics()
664
665
73
{
666
    /* -------------------------------------------------------------------- */
667
    /*      Do we have a statistics file?                                   */
668
    /* -------------------------------------------------------------------- */
669
73
    osSTAFilename = CPLResetExtensionSafe(GetDescription(), "sta");
670
671
73
    VSILFILE *fpSTA = VSIFOpenL(osSTAFilename, "r");
672
673
73
    if (fpSTA == nullptr && VSIIsCaseSensitiveFS(osSTAFilename))
674
73
    {
675
73
        osSTAFilename = CPLResetExtensionSafe(GetDescription(), "STA");
676
73
        fpSTA = VSIFOpenL(osSTAFilename, "r");
677
73
    }
678
679
73
    if (fpSTA == nullptr)
680
73
    {
681
73
        osSTAFilename = "";
682
73
        return;
683
73
    }
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
}