Coverage Report

Created: 2026-09-26 08:22

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
977
    : poCT(nullptr), eInterp(GCI_Undefined)
161
977
{
162
977
    poDS = poDSIn;
163
977
    nBand = nBandIn;
164
977
    eDataType = GDT_UInt8;
165
166
977
    nBlockXSize = poDSIn->GetRasterXSize();
167
977
    nBlockYSize = 1;
168
977
}
169
170
/************************************************************************/
171
/*                         ~LAN4BitRasterBand()                         */
172
/************************************************************************/
173
174
LAN4BitRasterBand::~LAN4BitRasterBand()
175
176
977
{
177
977
    if (poCT)
178
0
        delete poCT;
179
977
}
180
181
/************************************************************************/
182
/*                             IReadBlock()                             */
183
/************************************************************************/
184
185
CPLErr LAN4BitRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
186
                                     void *pImage)
187
188
8.84k
{
189
8.84k
    LANDataset *poLAN_DS = cpl::down_cast<LANDataset *>(poDS);
190
8.84k
    CPLAssert(nBlockXOff == 0);
191
192
    /* -------------------------------------------------------------------- */
193
    /*      Seek to profile.                                                */
194
    /* -------------------------------------------------------------------- */
195
8.84k
    const vsi_l_offset nOffset =
196
8.84k
        ERD_HEADER_SIZE +
197
8.84k
        (static_cast<vsi_l_offset>(nBlockYOff) * nRasterXSize *
198
8.84k
         poLAN_DS->GetRasterCount()) /
199
8.84k
            2 +
200
8.84k
        (static_cast<vsi_l_offset>(nBand - 1) * nRasterXSize) / 2;
201
202
8.84k
    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.84k
    if (VSIFReadL(pImage, 1, nRasterXSize / 2, poLAN_DS->fpImage) !=
213
8.84k
        static_cast<size_t>(nRasterXSize) / 2)
214
136
    {
215
136
        CPLError(CE_Failure, CPLE_FileIO, "LAN Read failed:%s",
216
136
                 VSIStrerror(errno));
217
136
        return CE_Failure;
218
136
    }
219
220
    /* -------------------------------------------------------------------- */
221
    /*      Convert 4bit to 8bit.                                           */
222
    /* -------------------------------------------------------------------- */
223
130k
    for (int i = nRasterXSize - 1; i >= 0; i--)
224
122k
    {
225
122k
        if ((i & 0x01) != 0)
226
60.9k
            reinterpret_cast<GByte *>(pImage)[i] =
227
60.9k
                reinterpret_cast<GByte *>(pImage)[i / 2] & 0x0f;
228
61.0k
        else
229
61.0k
            reinterpret_cast<GByte *>(pImage)[i] =
230
61.0k
                (reinterpret_cast<GByte *>(pImage)[i / 2] & 0xf0) / 16;
231
122k
    }
232
233
8.70k
    return CE_None;
234
8.84k
}
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
22
{
285
22
    return eInterp;
286
22
}
287
288
/************************************************************************/
289
/* ==================================================================== */
290
/*                              LANDataset                              */
291
/* ==================================================================== */
292
/************************************************************************/
293
294
/************************************************************************/
295
/*                             LANDataset()                             */
296
/************************************************************************/
297
298
136
LANDataset::LANDataset() : fpImage(nullptr)
299
136
{
300
136
    memset(pachHeader, 0, sizeof(pachHeader));
301
136
}
302
303
/************************************************************************/
304
/*                            ~LANDataset()                             */
305
/************************************************************************/
306
307
LANDataset::~LANDataset()
308
309
136
{
310
136
    LANDataset::Close();
311
136
}
312
313
/************************************************************************/
314
/*                               Close()                                */
315
/************************************************************************/
316
317
CPLErr LANDataset::Close(GDALProgressFunc, void *)
318
216
{
319
216
    CPLErr eErr = CE_None;
320
216
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
321
136
    {
322
136
        if (LANDataset::FlushCache(true) != CE_None)
323
0
            eErr = CE_Failure;
324
325
136
        if (fpImage)
326
136
        {
327
136
            if (VSIFCloseL(fpImage) != 0)
328
0
            {
329
0
                CPLError(CE_Failure, CPLE_FileIO, "I/O error");
330
0
                eErr = CE_Failure;
331
0
            }
332
136
        }
333
334
136
        if (m_poSRS)
335
80
            m_poSRS->Release();
336
337
136
        if (GDALPamDataset::Close() != CE_None)
338
0
            eErr = CE_Failure;
339
136
    }
340
216
    return eErr;
341
216
}
342
343
/************************************************************************/
344
/*                                Open()                                */
345
/************************************************************************/
346
347
GDALDataset *LANDataset::Open(GDALOpenInfo *poOpenInfo)
348
349
491k
{
350
    /* -------------------------------------------------------------------- */
351
    /*      We assume the user is pointing to the header (.pcb) file.       */
352
    /*      Does this appear to be a pcb file?                              */
353
    /* -------------------------------------------------------------------- */
354
491k
    if (poOpenInfo->nHeaderBytes < ERD_HEADER_SIZE ||
355
87.2k
        poOpenInfo->fpL == nullptr)
356
403k
        return nullptr;
357
358
87.2k
    if (!STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
359
87.1k
                        "HEADER") &&
360
87.1k
        !STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
361
87.2k
                        "HEAD74"))
362
87.1k
        return nullptr;
363
364
136
    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
136
    auto poDS = std::make_unique<LANDataset>();
374
375
136
    poDS->eAccess = poOpenInfo->eAccess;
376
136
    std::swap(poDS->fpImage, poOpenInfo->fpL);
377
378
    /* -------------------------------------------------------------------- */
379
    /*      Do we need to byte swap the headers to local machine order?     */
380
    /* -------------------------------------------------------------------- */
381
136
    const RawRasterBand::ByteOrder eByteOrder =
382
136
        poOpenInfo->pabyHeader[8] == 0
383
136
            ? RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN
384
136
            : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
385
386
136
    memcpy(poDS->pachHeader, poOpenInfo->pabyHeader, ERD_HEADER_SIZE);
387
388
136
    if (eByteOrder != RawRasterBand::NATIVE_BYTE_ORDER)
389
106
    {
390
106
        CPL_SWAP16PTR(poDS->pachHeader + 6);
391
106
        CPL_SWAP16PTR(poDS->pachHeader + 8);
392
393
106
        CPL_SWAP32PTR(poDS->pachHeader + 16);
394
106
        CPL_SWAP32PTR(poDS->pachHeader + 20);
395
106
        CPL_SWAP32PTR(poDS->pachHeader + 24);
396
106
        CPL_SWAP32PTR(poDS->pachHeader + 28);
397
398
106
        CPL_SWAP16PTR(poDS->pachHeader + 88);
399
106
        CPL_SWAP16PTR(poDS->pachHeader + 90);
400
401
106
        CPL_SWAP16PTR(poDS->pachHeader + 106);
402
106
        CPL_SWAP32PTR(poDS->pachHeader + 108);
403
106
        CPL_SWAP32PTR(poDS->pachHeader + 112);
404
106
        CPL_SWAP32PTR(poDS->pachHeader + 116);
405
106
        CPL_SWAP32PTR(poDS->pachHeader + 120);
406
106
        CPL_SWAP32PTR(poDS->pachHeader + 124);
407
106
    }
408
409
    /* -------------------------------------------------------------------- */
410
    /*      Capture some information from the file that is of interest.     */
411
    /* -------------------------------------------------------------------- */
412
136
    if (STARTS_WITH_CI(poDS->pachHeader, "HEADER"))
413
136
    {
414
136
        float fTmp = 0.0;
415
136
        memcpy(&fTmp, poDS->pachHeader + 16, 4);
416
136
        poDS->nRasterXSize = static_cast<int>(fTmp);
417
136
        memcpy(&fTmp, poDS->pachHeader + 20, 4);
418
136
        poDS->nRasterYSize = static_cast<int>(fTmp);
419
136
    }
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
136
    GInt16 nTmp16 = 0;
430
136
    memcpy(&nTmp16, poDS->pachHeader + 6, 2);
431
432
136
    int nPixelOffset = 0;
433
136
    GDALDataType eDataType = GDT_Unknown;
434
136
    if (nTmp16 == 0)
435
35
    {
436
35
        eDataType = GDT_UInt8;
437
35
        nPixelOffset = 1;
438
35
    }
439
101
    else if (nTmp16 == 1)  // 4 bit
440
58
    {
441
58
        eDataType = GDT_UInt8;
442
58
        nPixelOffset = -1;
443
58
    }
444
43
    else if (nTmp16 == 2)
445
21
    {
446
21
        nPixelOffset = 2;
447
21
        eDataType = GDT_Int16;
448
21
    }
449
22
    else
450
22
    {
451
22
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported pixel type (%d).",
452
22
                 nTmp16);
453
22
        return nullptr;
454
22
    }
455
456
114
    memcpy(&nTmp16, poDS->pachHeader + 8, 2);
457
114
    const int nBandCount = nTmp16;
458
459
114
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
460
81
        !GDALCheckBandCount(nBandCount, FALSE))
461
34
    {
462
34
        return nullptr;
463
34
    }
464
465
    // cppcheck-suppress knownConditionTrueFalse
466
80
    if (nPixelOffset != -1 &&
467
37
        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.91k
    for (int iBand = 1; iBand <= nBandCount; iBand++)
477
1.83k
    {
478
1.83k
        if (nPixelOffset == -1) /* 4 bit case */
479
977
            poDS->SetBand(iBand, new LAN4BitRasterBand(poDS.get(), iBand));
480
857
        else
481
857
        {
482
857
            auto poBand = RawRasterBand::Create(
483
857
                poDS.get(), iBand, poDS->fpImage,
484
857
                ERD_HEADER_SIZE +
485
857
                    (iBand - 1) * nPixelOffset * poDS->nRasterXSize,
486
857
                nPixelOffset, poDS->nRasterXSize * nPixelOffset * nBandCount,
487
857
                eDataType, eByteOrder, RawRasterBand::OwnFP::NO);
488
857
            if (!poBand)
489
0
                return nullptr;
490
857
            poDS->SetBand(iBand, std::move(poBand));
491
857
        }
492
1.83k
    }
493
494
    /* -------------------------------------------------------------------- */
495
    /*      Initialize any PAM information.                                 */
496
    /* -------------------------------------------------------------------- */
497
80
    poDS->SetDescription(poOpenInfo->pszFilename);
498
80
    poDS->CheckForStatistics();
499
80
    poDS->TryLoadXML();
500
501
    /* -------------------------------------------------------------------- */
502
    /*      Check for overviews.                                            */
503
    /* -------------------------------------------------------------------- */
504
80
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
505
506
    /* -------------------------------------------------------------------- */
507
    /*      Try to interpret georeferencing.                                */
508
    /* -------------------------------------------------------------------- */
509
80
    float fTmp = 0.0;
510
511
80
    memcpy(&fTmp, poDS->pachHeader + 112, 4);
512
80
    poDS->m_gt.xorig = fTmp;
513
80
    memcpy(&fTmp, poDS->pachHeader + 120, 4);
514
80
    poDS->m_gt.xscale = fTmp;
515
80
    poDS->m_gt.xrot = 0.0;
516
80
    memcpy(&fTmp, poDS->pachHeader + 116, 4);
517
80
    poDS->m_gt.yorig = fTmp;
518
80
    poDS->m_gt.yrot = 0.0;
519
80
    memcpy(&fTmp, poDS->pachHeader + 124, 4);
520
80
    poDS->m_gt.yscale = -fTmp;
521
522
    // adjust for center of pixel vs. top left corner of pixel.
523
80
    poDS->m_gt.xorig -= poDS->m_gt.xscale * 0.5;
524
80
    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
80
    if (poDS->m_gt.xscale == 0.0 || poDS->m_gt.yscale == 0.0)
530
31
    {
531
31
        if (!GDALReadWorldFile(poOpenInfo->pszFilename, nullptr,
532
31
                               poDS->m_gt.data()))
533
31
            GDALReadWorldFile(poOpenInfo->pszFilename, ".wld",
534
31
                              poDS->m_gt.data());
535
31
    }
536
537
    /* -------------------------------------------------------------------- */
538
    /*      Try to come up with something for the coordinate system.        */
539
    /* -------------------------------------------------------------------- */
540
80
    memcpy(&nTmp16, poDS->pachHeader + 88, 2);
541
80
    int nCoordSys = nTmp16;
542
543
80
    poDS->m_poSRS = new OGRSpatialReference();
544
80
    poDS->m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
545
80
    if (nCoordSys == 0)
546
32
    {
547
32
        poDS->m_poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
548
32
    }
549
48
    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
45
    else if (nCoordSys == 2)
555
1
    {
556
1
        poDS->m_poSRS->SetFromUserInput(
557
1
            "LOCAL_CS[\"State Plane - Zone Unknown\","
558
1
            "UNIT[\"US survey foot\",0.3048006096012192]]");
559
1
    }
560
44
    else
561
44
    {
562
44
        poDS->m_poSRS->SetFromUserInput(
563
44
            "LOCAL_CS[\"Unknown\",UNIT[\"Meter\",1]]");
564
44
    }
565
566
    /* -------------------------------------------------------------------- */
567
    /*      Check for a trailer file with a colormap in it.                 */
568
    /* -------------------------------------------------------------------- */
569
80
    char *pszPath = CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
570
80
    char *pszBasename =
571
80
        CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str());
572
80
    const std::string osTRLFilename =
573
80
        CPLFormCIFilenameSafe(pszPath, pszBasename, "trl");
574
80
    VSILFILE *fpTRL = VSIFOpenL(osTRLFilename.c_str(), "rb");
575
80
    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
80
    CPLFree(pszPath);
605
80
    CPLFree(pszBasename);
606
607
80
    return poDS.release();
608
80
}
609
610
/************************************************************************/
611
/*                          GetGeoTransform()                           */
612
/************************************************************************/
613
614
CPLErr LANDataset::GetGeoTransform(GDALGeoTransform &gt) const
615
616
80
{
617
80
    if (m_gt.xscale != 0.0 && m_gt.yscale != 0.0)
618
49
    {
619
49
        gt = m_gt;
620
49
        return CE_None;
621
49
    }
622
623
31
    return GDALPamDataset::GetGeoTransform(gt);
624
80
}
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
78
{
636
78
    const auto poSRS = GDALPamDataset::GetSpatialRef();
637
78
    if (poSRS)
638
0
        return poSRS;
639
640
78
    return m_poSRS;
641
78
}
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
80
{
666
    /* -------------------------------------------------------------------- */
667
    /*      Do we have a statistics file?                                   */
668
    /* -------------------------------------------------------------------- */
669
80
    osSTAFilename = CPLResetExtensionSafe(GetDescription(), "sta");
670
671
80
    VSILFILE *fpSTA = VSIFOpenL(osSTAFilename, "r");
672
673
80
    if (fpSTA == nullptr && VSIIsCaseSensitiveFS(osSTAFilename))
674
80
    {
675
80
        osSTAFilename = CPLResetExtensionSafe(GetDescription(), "STA");
676
80
        fpSTA = VSIFOpenL(osSTAFilename, "r");
677
80
    }
678
679
80
    if (fpSTA == nullptr)
680
80
    {
681
80
        osSTAFilename = "";
682
80
        return;
683
80
    }
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
24
{
736
24
    if (GDALGetDriverByName("LAN") != nullptr)
737
0
        return;
738
739
24
    GDALDriver *poDriver = new GDALDriver();
740
741
24
    poDriver->SetDescription("LAN");
742
24
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
743
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Erdas .LAN/.GIS");
744
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/lan.html");
745
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
746
747
24
    poDriver->pfnOpen = LANDataset::Open;
748
749
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
750
24
}