Coverage Report

Created: 2025-06-09 08:44

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