Coverage Report

Created: 2026-08-14 09:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/raw/fastdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  EOSAT FAST Format reader
4
 * Purpose:  Reads Landsat FAST-L7A, IRS 1C/1D
5
 * Author:   Andrey Kiselev, dron@ak4719.spb.edu
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Andrey Kiselev <dron@ak4719.spb.edu>
9
 * Copyright (c) 2007-2011, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
#include "cpl_conv.h"
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 <algorithm>
21
22
// constexpr int ADM_STD_HEADER_SIZE = 4608;  // Format specification says it
23
constexpr int ADM_HEADER_SIZE = 5000;  // Should be 4608, but some vendors
24
                                       // ship broken large datasets.
25
constexpr size_t ADM_MIN_HEADER_SIZE = 1536;  // And sometimes it can be
26
                                              // even 1/3 of standard size.
27
28
static const char ACQUISITION_DATE[] = "ACQUISITION DATE";
29
constexpr int ACQUISITION_DATE_SIZE = 8;
30
31
static const char SATELLITE_NAME[] = "SATELLITE";
32
constexpr int SATELLITE_NAME_SIZE = 10;
33
34
static const char SENSOR_NAME[] = "SENSOR";
35
constexpr int SENSOR_NAME_SIZE = 10;
36
37
static const char BANDS_PRESENT[] = "BANDS PRESENT";
38
constexpr int BANDS_PRESENT_SIZE = 32;
39
40
static const char FILENAME[] = "FILENAME";
41
constexpr int FILENAME_SIZE = 29;
42
43
static const char PIXELS[] = "PIXELS PER LINE";
44
constexpr int PIXELS_SIZE = 5;
45
46
static const char LINES1[] = "LINES PER BAND";
47
static const char LINES2[] = "LINES PER IMAGE";
48
constexpr int LINES_SIZE = 5;
49
50
static const char BITS_PER_PIXEL[] = "OUTPUT BITS PER PIXEL";
51
constexpr int BITS_PER_PIXEL_SIZE = 2;
52
53
static const char PROJECTION_NAME[] = "MAP PROJECTION";
54
constexpr int PROJECTION_NAME_SIZE = 4;
55
56
static const char ELLIPSOID_NAME[] = "ELLIPSOID";
57
constexpr int ELLIPSOID_NAME_SIZE = 18;
58
59
static const char DATUM_NAME[] = "DATUM";
60
constexpr int DATUM_NAME_SIZE = 6;
61
62
static const char ZONE_NUMBER[] = "USGS MAP ZONE";
63
constexpr int ZONE_NUMBER_SIZE = 6;
64
65
static const char USGS_PARAMETERS[] = "USGS PROJECTION PARAMETERS";
66
67
static const char CORNER_UPPER_LEFT[] = "UL ";
68
static const char CORNER_UPPER_RIGHT[] = "UR ";
69
static const char CORNER_LOWER_LEFT[] = "LL ";
70
static const char CORNER_LOWER_RIGHT[] = "LR ";
71
constexpr int CORNER_VALUE_SIZE = 13;
72
73
constexpr int VALUE_SIZE = 24;
74
75
enum FASTSatellite  // Satellites:
76
{
77
    LANDSAT,  // Landsat 7
78
    IRS,      // IRS 1C/1D
79
    FAST_UNKNOWN
80
};
81
82
constexpr int MAX_FILES = 7;
83
84
/************************************************************************/
85
/* ==================================================================== */
86
/*                              FASTDataset                             */
87
/* ==================================================================== */
88
/************************************************************************/
89
90
class FASTDataset final : public GDALPamDataset
91
{
92
    GDALGeoTransform m_gt{};
93
    OGRSpatialReference m_oSRS{};
94
95
    VSILFILE *fpHeader;
96
    CPLString apoChannelFilenames[MAX_FILES];
97
    VSILFILE *fpChannels[MAX_FILES];
98
    const char *pszFilename;
99
    char *pszDirname;
100
    GDALDataType eDataType;
101
    FASTSatellite iSatellite;
102
103
    int OpenChannel(const char *pszFilename, int iBand);
104
105
    CPL_DISALLOW_COPY_ASSIGN(FASTDataset)
106
107
  public:
108
    FASTDataset();
109
    ~FASTDataset() override;
110
111
    static GDALDataset *Open(GDALOpenInfo *);
112
113
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
114
115
    const OGRSpatialReference *GetSpatialRef() const override
116
0
    {
117
0
        return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
118
0
    }
119
120
    VSILFILE *FOpenChannel(const char *, int iBand, int iFASTBand);
121
    void TryEuromap_IRS_1C_1D_ChannelNameConvention(int &l_nBands);
122
123
    char **GetFileList() override;
124
};
125
126
/************************************************************************/
127
/* ==================================================================== */
128
/*                              FASTDataset                             */
129
/* ==================================================================== */
130
/************************************************************************/
131
132
/************************************************************************/
133
/*                            FASTDataset()                             */
134
/************************************************************************/
135
136
FASTDataset::FASTDataset()
137
3.09k
    : fpHeader(nullptr), pszFilename(nullptr), pszDirname(nullptr),
138
3.09k
      eDataType(GDT_Unknown), iSatellite(FAST_UNKNOWN)
139
3.09k
{
140
3.09k
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
141
    // TODO: Why does this not work?
142
    //   fill( fpChannels, fpChannels + CPL_ARRAYSIZE(fpChannels), NULL );
143
24.7k
    for (int i = 0; i < MAX_FILES; ++i)
144
21.6k
        fpChannels[i] = nullptr;
145
3.09k
}
146
147
/************************************************************************/
148
/*                            ~FASTDataset()                            */
149
/************************************************************************/
150
151
FASTDataset::~FASTDataset()
152
153
3.09k
{
154
3.09k
    FlushCache(true);
155
156
3.09k
    CPLFree(pszDirname);
157
24.7k
    for (int i = 0; i < MAX_FILES; i++)
158
21.6k
        if (fpChannels[i])
159
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpChannels[i]));
160
3.09k
    if (fpHeader != nullptr)
161
3.09k
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpHeader));
162
3.09k
}
163
164
/************************************************************************/
165
/*                          GetGeoTransform()                           */
166
/************************************************************************/
167
168
CPLErr FASTDataset::GetGeoTransform(GDALGeoTransform &gt) const
169
170
0
{
171
0
    gt = m_gt;
172
0
    return CE_None;
173
0
}
174
175
/************************************************************************/
176
/*                            GetFileList()                             */
177
/************************************************************************/
178
179
char **FASTDataset::GetFileList()
180
0
{
181
0
    char **papszFileList = GDALPamDataset::GetFileList();
182
183
0
    for (int i = 0; i < 6; i++)
184
0
    {
185
0
        if (!apoChannelFilenames[i].empty())
186
0
            papszFileList =
187
0
                CSLAddString(papszFileList, apoChannelFilenames[i].c_str());
188
0
    }
189
190
0
    return papszFileList;
191
0
}
192
193
/************************************************************************/
194
/*                            OpenChannel()                             */
195
/************************************************************************/
196
197
int FASTDataset::OpenChannel(const char *pszFilenameIn, int iBand)
198
279k
{
199
279k
    CPLAssert(fpChannels[iBand] == nullptr);
200
279k
    fpChannels[iBand] = VSIFOpenL(pszFilenameIn, "rb");
201
279k
    if (fpChannels[iBand])
202
0
        apoChannelFilenames[iBand] = pszFilenameIn;
203
279k
    return fpChannels[iBand] != nullptr;
204
279k
}
205
206
/************************************************************************/
207
/*                            FOpenChannel()                            */
208
/************************************************************************/
209
210
VSILFILE *FASTDataset::FOpenChannel(const char *pszBandname, int iBand,
211
                                    int iFASTBand)
212
21.5k
{
213
21.5k
    std::string osChannelFilename;
214
21.5k
    const std::string osPrefix = CPLGetBasenameSafe(pszFilename);
215
21.5k
    const std::string osSuffix = CPLGetExtensionSafe(pszFilename);
216
217
21.5k
    fpChannels[iBand] = nullptr;
218
219
21.5k
    switch (iSatellite)
220
21.5k
    {
221
77
        case LANDSAT:
222
77
            if (pszBandname && !EQUAL(pszBandname, ""))
223
22
            {
224
22
                if (CPLHasPathTraversal(pszBandname))
225
0
                {
226
0
                    CPLError(CE_Failure, CPLE_AppDefined,
227
0
                             "Path traversal detected in %s", pszBandname);
228
0
                    return nullptr;
229
0
                }
230
22
                osChannelFilename =
231
22
                    CPLFormCIFilenameSafe(pszDirname, pszBandname, nullptr);
232
22
                if (OpenChannel(osChannelFilename.c_str(), iBand))
233
0
                    break;
234
22
                osChannelFilename = CPLFormFilenameSafe(
235
22
                    pszDirname,
236
22
                    CPLSPrintf("%s.b%02d", osPrefix.c_str(), iFASTBand),
237
22
                    nullptr);
238
22
                CPL_IGNORE_RET_VAL(
239
22
                    OpenChannel(osChannelFilename.c_str(), iBand));
240
22
            }
241
77
            break;
242
21.4k
        case IRS:
243
21.4k
        default:
244
21.4k
            osChannelFilename = CPLFormFilenameSafe(
245
21.4k
                pszDirname, CPLSPrintf("%s.%d", osPrefix.c_str(), iFASTBand),
246
21.4k
                osSuffix.c_str());
247
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
248
0
                break;
249
21.4k
            osChannelFilename = CPLFormFilenameSafe(
250
21.4k
                pszDirname, CPLSPrintf("IMAGERY%d", iFASTBand),
251
21.4k
                osSuffix.c_str());
252
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
253
0
                break;
254
21.4k
            osChannelFilename = CPLFormFilenameSafe(
255
21.4k
                pszDirname, CPLSPrintf("imagery%d", iFASTBand),
256
21.4k
                osSuffix.c_str());
257
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
258
0
                break;
259
21.4k
            osChannelFilename = CPLFormFilenameSafe(
260
21.4k
                pszDirname, CPLSPrintf("IMAGERY%d.DAT", iFASTBand), nullptr);
261
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
262
0
                break;
263
21.4k
            osChannelFilename = CPLFormFilenameSafe(
264
21.4k
                pszDirname, CPLSPrintf("imagery%d.dat", iFASTBand), nullptr);
265
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
266
0
                break;
267
21.4k
            osChannelFilename = CPLFormFilenameSafe(
268
21.4k
                pszDirname, CPLSPrintf("IMAGERY%d.dat", iFASTBand), nullptr);
269
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
270
0
                break;
271
21.4k
            osChannelFilename = CPLFormFilenameSafe(
272
21.4k
                pszDirname, CPLSPrintf("imagery%d.DAT", iFASTBand), nullptr);
273
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
274
0
                break;
275
21.4k
            osChannelFilename = CPLFormFilenameSafe(
276
21.4k
                pszDirname, CPLSPrintf("BAND%d", iFASTBand), osSuffix.c_str());
277
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
278
0
                break;
279
21.4k
            osChannelFilename = CPLFormFilenameSafe(
280
21.4k
                pszDirname, CPLSPrintf("band%d", iFASTBand), osSuffix.c_str());
281
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
282
0
                break;
283
21.4k
            osChannelFilename = CPLFormFilenameSafe(
284
21.4k
                pszDirname, CPLSPrintf("BAND%d.DAT", iFASTBand), nullptr);
285
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
286
0
                break;
287
21.4k
            osChannelFilename = CPLFormFilenameSafe(
288
21.4k
                pszDirname, CPLSPrintf("band%d.dat", iFASTBand), nullptr);
289
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
290
0
                break;
291
21.4k
            osChannelFilename = CPLFormFilenameSafe(
292
21.4k
                pszDirname, CPLSPrintf("BAND%d.dat", iFASTBand), nullptr);
293
21.4k
            if (OpenChannel(osChannelFilename.c_str(), iBand))
294
0
                break;
295
21.4k
            osChannelFilename = CPLFormFilenameSafe(
296
21.4k
                pszDirname, CPLSPrintf("band%d.DAT", iFASTBand), nullptr);
297
21.4k
            CPL_IGNORE_RET_VAL(OpenChannel(osChannelFilename.c_str(), iBand));
298
21.4k
            break;
299
21.5k
    }
300
301
21.5k
    CPLDebug("FAST", "Band %d filename=%s", iBand + 1,
302
21.5k
             osChannelFilename.c_str());
303
304
21.5k
    return fpChannels[iBand];
305
21.5k
}
306
307
/************************************************************************/
308
/*             TryEuromap_IRS_1C_1D_ChannelNameConvention()             */
309
/************************************************************************/
310
311
void FASTDataset::TryEuromap_IRS_1C_1D_ChannelNameConvention(int &l_nBands)
312
3
{
313
    // Filename convention explained in:
314
    // http://www.euromap.de/download/em_names.pdf
315
316
3
    char chLastLetterHeader = pszFilename[strlen(pszFilename) - 1];
317
3
    if (EQUAL(GetMetadataItem("SENSOR"), "PAN"))
318
1
    {
319
        /* Converting upper-case to lower case */
320
1
        if (chLastLetterHeader >= 'A' && chLastLetterHeader <= 'M')
321
0
            chLastLetterHeader += 'a' - 'A';
322
323
1
        if (chLastLetterHeader >= 'a' && chLastLetterHeader <= 'j')
324
0
        {
325
0
            const char chLastLetterData = chLastLetterHeader - 'a' + '0';
326
0
            char *pszChannelFilename = CPLStrdup(pszFilename);
327
0
            pszChannelFilename[strlen(pszChannelFilename) - 1] =
328
0
                chLastLetterData;
329
0
            if (OpenChannel(pszChannelFilename, 0))
330
0
                l_nBands++;
331
0
            else
332
0
                CPLDebug("FAST", "Could not find %s", pszChannelFilename);
333
0
            CPLFree(pszChannelFilename);
334
0
        }
335
1
        else if (chLastLetterHeader >= 'k' && chLastLetterHeader <= 'm')
336
0
        {
337
0
            const char chLastLetterData = chLastLetterHeader - 'k' + 'n';
338
0
            char *pszChannelFilename = CPLStrdup(pszFilename);
339
0
            pszChannelFilename[strlen(pszChannelFilename) - 1] =
340
0
                chLastLetterData;
341
0
            if (OpenChannel(pszChannelFilename, 0))
342
0
            {
343
0
                l_nBands++;
344
0
            }
345
0
            else
346
0
            {
347
                /* Trying upper-case */
348
0
                pszChannelFilename[strlen(pszChannelFilename) - 1] =
349
0
                    chLastLetterData - 'a' + 'A';
350
0
                if (OpenChannel(pszChannelFilename, 0))
351
0
                    l_nBands++;
352
0
                else
353
0
                    CPLDebug("FAST", "Could not find %s", pszChannelFilename);
354
0
            }
355
0
            CPLFree(pszChannelFilename);
356
0
        }
357
1
        else
358
1
        {
359
1
            CPLDebug(
360
1
                "FAST",
361
1
                "Unknown last letter (%c) for a IRS PAN Euromap FAST dataset",
362
1
                chLastLetterHeader);
363
1
        }
364
1
    }
365
2
    else if (EQUAL(GetMetadataItem("SENSOR"), "LISS3"))
366
1
    {
367
1
        const char apchLISSFilenames[7][5] = {
368
1
            {'0', '2', '3', '4', '5'}, {'6', '7', '8', '9', 'a'},
369
1
            {'b', 'c', 'd', 'e', 'f'}, {'g', 'h', 'i', 'j', 'k'},
370
1
            {'l', 'm', 'n', 'o', 'p'}, {'q', 'r', 's', 't', 'u'},
371
1
            {'v', 'w', 'x', 'y', 'z'}};
372
373
1
        int i = 0;
374
8
        for (; i < 7; i++)
375
7
        {
376
7
            if (chLastLetterHeader == apchLISSFilenames[i][0] ||
377
7
                (apchLISSFilenames[i][0] >= 'a' &&
378
5
                 apchLISSFilenames[i][0] <= 'z' &&
379
5
                 (apchLISSFilenames[i][0] - chLastLetterHeader == 0 ||
380
5
                  apchLISSFilenames[i][0] - chLastLetterHeader == 32)))
381
0
            {
382
0
                for (int j = 0; j < 4; j++)
383
0
                {
384
0
                    char *pszChannelFilename = CPLStrdup(pszFilename);
385
0
                    pszChannelFilename[strlen(pszChannelFilename) - 1] =
386
0
                        apchLISSFilenames[i][j + 1];
387
0
                    if (OpenChannel(pszChannelFilename, l_nBands))
388
0
                        l_nBands++;
389
0
                    else if (apchLISSFilenames[i][j + 1] >= 'a' &&
390
0
                             apchLISSFilenames[i][j + 1] <= 'z')
391
0
                    {
392
                        /* Trying upper-case */
393
0
                        pszChannelFilename[strlen(pszChannelFilename) - 1] =
394
0
                            apchLISSFilenames[i][j + 1] - 'a' + 'A';
395
0
                        if (OpenChannel(pszChannelFilename, l_nBands))
396
0
                        {
397
0
                            l_nBands++;
398
0
                        }
399
0
                        else
400
0
                        {
401
0
                            CPLDebug("FAST", "Could not find %s",
402
0
                                     pszChannelFilename);
403
0
                        }
404
0
                    }
405
0
                    else
406
0
                    {
407
0
                        CPLDebug("FAST", "Could not find %s",
408
0
                                 pszChannelFilename);
409
0
                    }
410
0
                    CPLFree(pszChannelFilename);
411
0
                }
412
0
                break;
413
0
            }
414
7
        }
415
1
        if (i == 7)
416
1
        {
417
1
            CPLDebug(
418
1
                "FAST",
419
1
                "Unknown last letter (%c) for a IRS LISS3 Euromap FAST dataset",
420
1
                chLastLetterHeader);
421
1
        }
422
1
    }
423
1
    else if (EQUAL(GetMetadataItem("SENSOR"), "WIFS"))
424
1
    {
425
1
        if (chLastLetterHeader == '0')
426
0
        {
427
0
            for (int j = 0; j < 2; j++)
428
0
            {
429
0
                char *pszChannelFilename = CPLStrdup(pszFilename);
430
0
                pszChannelFilename[strlen(pszChannelFilename) - 1] =
431
0
                    static_cast<char>('1' + j);
432
0
                if (OpenChannel(pszChannelFilename, l_nBands))
433
0
                {
434
0
                    l_nBands++;
435
0
                }
436
0
                else
437
0
                {
438
0
                    CPLDebug("FAST", "Could not find %s", pszChannelFilename);
439
0
                }
440
0
                CPLFree(pszChannelFilename);
441
0
            }
442
0
        }
443
1
        else
444
1
        {
445
1
            CPLDebug(
446
1
                "FAST",
447
1
                "Unknown last letter (%c) for a IRS WIFS Euromap FAST dataset",
448
1
                chLastLetterHeader);
449
1
        }
450
1
    }
451
0
    else
452
0
    {
453
0
        CPLAssert(false);
454
0
    }
455
3
}
456
457
/************************************************************************/
458
/*                              GetValue()                              */
459
/************************************************************************/
460
461
static char *GetValue(const char *pszString, const char *pszName,
462
                      int iValueSize, int bNormalize)
463
9.24k
{
464
9.24k
    char *pszTemp = strstr(const_cast<char *>(pszString), pszName);
465
9.24k
    if (pszTemp)
466
3.29k
    {
467
        // Skip the parameter name
468
3.29k
        pszTemp += strlen(pszName);
469
        // Skip whitespaces and equal signs
470
6.46k
        while (*pszTemp == ' ')
471
3.16k
            pszTemp++;
472
6.38k
        while (*pszTemp == '=')
473
3.08k
            pszTemp++;
474
475
3.29k
        pszTemp = CPLScanString(pszTemp, iValueSize, TRUE, bNormalize);
476
3.29k
    }
477
478
9.24k
    return pszTemp;
479
9.24k
}
480
481
/************************************************************************/
482
/*                         USGSMnemonicToCode()                         */
483
/************************************************************************/
484
485
static long USGSMnemonicToCode(const char *pszMnemonic)
486
0
{
487
0
    if (EQUAL(pszMnemonic, "UTM"))
488
0
        return 1L;
489
0
    else if (EQUAL(pszMnemonic, "LCC"))
490
0
        return 4L;
491
0
    else if (EQUAL(pszMnemonic, "PS"))
492
0
        return 6L;
493
0
    else if (EQUAL(pszMnemonic, "PC"))
494
0
        return 7L;
495
0
    else if (EQUAL(pszMnemonic, "TM"))
496
0
        return 9L;
497
0
    else if (EQUAL(pszMnemonic, "OM"))
498
0
        return 20L;
499
0
    else if (EQUAL(pszMnemonic, "SOM"))
500
0
        return 22L;
501
0
    else
502
0
        return 1L;  // UTM by default
503
0
}
504
505
/************************************************************************/
506
/*                        USGSEllipsoidToCode()                         */
507
/************************************************************************/
508
509
static long USGSEllipsoidToCode(const char *pszMnemonic)
510
0
{
511
0
    if (EQUAL(pszMnemonic, "CLARKE_1866"))
512
0
        return 0L;
513
0
    else if (EQUAL(pszMnemonic, "CLARKE_1880"))
514
0
        return 1L;
515
0
    else if (EQUAL(pszMnemonic, "BESSEL"))
516
0
        return 2L;
517
0
    else if (EQUAL(pszMnemonic, "INTERNATL_1967"))
518
0
        return 3L;
519
0
    else if (EQUAL(pszMnemonic, "INTERNATL_1909"))
520
0
        return 4L;
521
0
    else if (EQUAL(pszMnemonic, "WGS72") || EQUAL(pszMnemonic, "WGS_72"))
522
0
        return 5L;
523
0
    else if (EQUAL(pszMnemonic, "EVEREST"))
524
0
        return 6L;
525
0
    else if (EQUAL(pszMnemonic, "WGS66") || EQUAL(pszMnemonic, "WGS_66"))
526
0
        return 7L;
527
0
    else if (EQUAL(pszMnemonic, "GRS_80"))
528
0
        return 8L;
529
0
    else if (EQUAL(pszMnemonic, "AIRY"))
530
0
        return 9L;
531
0
    else if (EQUAL(pszMnemonic, "MODIFIED_EVEREST"))
532
0
        return 10L;
533
0
    else if (EQUAL(pszMnemonic, "MODIFIED_AIRY"))
534
0
        return 11L;
535
0
    else if (EQUAL(pszMnemonic, "WGS84") || EQUAL(pszMnemonic, "WGS_84"))
536
0
        return 12L;
537
0
    else if (EQUAL(pszMnemonic, "SOUTHEAST_ASIA"))
538
0
        return 13L;
539
0
    else if (EQUAL(pszMnemonic, "AUSTRALIAN_NATL"))
540
0
        return 14L;
541
0
    else if (EQUAL(pszMnemonic, "KRASSOVSKY"))
542
0
        return 15L;
543
0
    else if (EQUAL(pszMnemonic, "HOUGH"))
544
0
        return 16L;
545
0
    else if (EQUAL(pszMnemonic, "MERCURY_1960"))
546
0
        return 17L;
547
0
    else if (EQUAL(pszMnemonic, "MOD_MERC_1968"))
548
0
        return 18L;
549
0
    else if (EQUAL(pszMnemonic, "6370997_M_SPHERE"))
550
0
        return 19L;
551
0
    else
552
0
        return 0L;
553
0
}
554
555
/************************************************************************/
556
/*                                Open()                                */
557
/************************************************************************/
558
559
GDALDataset *FASTDataset::Open(GDALOpenInfo *poOpenInfo)
560
561
529k
{
562
529k
    if (poOpenInfo->nHeaderBytes < 1024 || poOpenInfo->fpL == nullptr)
563
462k
        return nullptr;
564
565
67.2k
    if (!EQUALN(reinterpret_cast<const char *>(poOpenInfo->pabyHeader) + 52,
566
67.2k
                "ACQUISITION DATE =", 18) &&
567
64.1k
        !EQUALN(reinterpret_cast<const char *>(poOpenInfo->pabyHeader) + 36,
568
67.2k
                "ACQUISITION DATE =", 18))
569
64.1k
        return nullptr;
570
571
    /* -------------------------------------------------------------------- */
572
    /*  Create a corresponding GDALDataset.                                 */
573
    /* -------------------------------------------------------------------- */
574
3.09k
    auto poDS = std::make_unique<FASTDataset>();
575
576
3.09k
    std::swap(poDS->fpHeader, poOpenInfo->fpL);
577
578
3.09k
    poDS->pszFilename = poOpenInfo->pszFilename;
579
3.09k
    poDS->pszDirname =
580
3.09k
        CPLStrdup(CPLGetDirnameSafe(poOpenInfo->pszFilename).c_str());
581
582
    /* -------------------------------------------------------------------- */
583
    /*  Read the administrative record.                                     */
584
    /* -------------------------------------------------------------------- */
585
3.09k
    std::string osHeader;
586
3.09k
    osHeader.resize(ADM_HEADER_SIZE);
587
588
3.09k
    size_t nBytesRead = 0;
589
3.09k
    if (VSIFSeekL(poDS->fpHeader, 0, SEEK_SET) >= 0)
590
3.09k
        nBytesRead =
591
3.09k
            VSIFReadL(&osHeader[0], 1, ADM_HEADER_SIZE, poDS->fpHeader);
592
3.09k
    if (nBytesRead < ADM_MIN_HEADER_SIZE)
593
16
    {
594
16
        CPLDebug("FAST", "Header file too short. Reading failed");
595
16
        return nullptr;
596
16
    }
597
3.08k
    osHeader.resize(nBytesRead);
598
3.08k
    const char *pszHeader = osHeader.c_str();
599
600
    // Read acquisition date
601
3.08k
    {
602
3.08k
        char *pszTemp =
603
3.08k
            GetValue(pszHeader, ACQUISITION_DATE, ACQUISITION_DATE_SIZE, TRUE);
604
3.08k
        if (pszTemp == nullptr)
605
30
        {
606
30
            CPLDebug("FAST", "Cannot get ACQUISITION_DATE, using empty value.");
607
30
            pszTemp = CPLStrdup("");
608
30
        }
609
3.08k
        poDS->SetMetadataItem("ACQUISITION_DATE", pszTemp);
610
3.08k
        CPLFree(pszTemp);
611
3.08k
    }
612
613
    // Read satellite name (will read the first one only)
614
3.08k
    {
615
3.08k
        char *pszTemp =
616
3.08k
            GetValue(pszHeader, SATELLITE_NAME, SATELLITE_NAME_SIZE, TRUE);
617
3.08k
        if (pszTemp == nullptr)
618
3.06k
        {
619
3.06k
            CPLDebug("FAST", "Cannot get SATELLITE_NAME, using empty value.");
620
3.06k
            pszTemp = CPLStrdup("");
621
3.06k
        }
622
3.08k
        poDS->SetMetadataItem("SATELLITE", pszTemp);
623
3.08k
        if (STARTS_WITH_CI(pszTemp, "LANDSAT"))
624
11
            poDS->iSatellite = LANDSAT;
625
        // TODO(schwehr): Was this a bug that both are IRS?
626
        // else if ( STARTS_WITH_CI(pszTemp, "IRS") )
627
        //    poDS->iSatellite = IRS;
628
3.07k
        else
629
3.07k
            poDS->iSatellite =
630
3.07k
                IRS;  // TODO(schwehr): Should this be FAST_UNKNOWN?
631
3.08k
        CPLFree(pszTemp);
632
3.08k
    }
633
634
    // Read sensor name (will read the first one only)
635
3.08k
    {
636
3.08k
        char *pszTemp =
637
3.08k
            GetValue(pszHeader, SENSOR_NAME, SENSOR_NAME_SIZE, TRUE);
638
3.08k
        if (pszTemp == nullptr)
639
2.85k
        {
640
2.85k
            CPLDebug("FAST", "Cannot get SENSOR_NAME, using empty value.");
641
2.85k
            pszTemp = CPLStrdup("");
642
2.85k
        }
643
3.08k
        poDS->SetMetadataItem("SENSOR", pszTemp);
644
3.08k
        CPLFree(pszTemp);
645
3.08k
    }
646
647
    // Read filenames
648
3.08k
    int l_nBands = 0;
649
650
3.08k
    if (strstr(pszHeader, FILENAME) == nullptr)
651
3.05k
    {
652
3.05k
        if (strstr(pszHeader, "GENERATING AGENCY =EUROMAP"))
653
7
        {
654
            // If we don't find the FILENAME field, let's try with the Euromap
655
            // PAN / LISS3 / WIFS IRS filename convention.
656
7
            if ((EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1C") ||
657
6
                 EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1D")) &&
658
4
                (EQUAL(poDS->GetMetadataItem("SENSOR"), "PAN") ||
659
3
                 EQUAL(poDS->GetMetadataItem("SENSOR"), "LISS3") ||
660
2
                 EQUAL(poDS->GetMetadataItem("SENSOR"), "WIFS")))
661
3
            {
662
3
                poDS->TryEuromap_IRS_1C_1D_ChannelNameConvention(l_nBands);
663
3
            }
664
4
            else if (EQUAL(poDS->GetMetadataItem("SATELLITE"), "CARTOSAT-1") &&
665
0
                     (EQUAL(poDS->GetMetadataItem("SENSOR"), "FORE") ||
666
0
                      EQUAL(poDS->GetMetadataItem("SENSOR"), "AFT")))
667
0
            {
668
                // See appendix F in
669
                // http://www.euromap.de/download/p5fast_20050301.pdf
670
0
                const CPLString osSuffix =
671
0
                    CPLGetExtensionSafe(poDS->pszFilename);
672
0
                const char *papszBasenames[] = {"BANDF", "bandf", "BANDA",
673
0
                                                "banda"};
674
0
                for (int i = 0; i < 4; i++)
675
0
                {
676
0
                    const CPLString osChannelFilename = CPLFormFilenameSafe(
677
0
                        poDS->pszDirname, papszBasenames[i], osSuffix);
678
0
                    if (poDS->OpenChannel(osChannelFilename, 0))
679
0
                    {
680
0
                        l_nBands = 1;
681
0
                        break;
682
0
                    }
683
0
                }
684
0
            }
685
4
            else if (EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS P6"))
686
0
            {
687
                // If BANDS_PRESENT="2345", the file bands are "BAND2.DAT",
688
                // "BAND3.DAT", etc.
689
0
                char *pszTemp = GetValue(pszHeader, BANDS_PRESENT,
690
0
                                         BANDS_PRESENT_SIZE, TRUE);
691
0
                if (pszTemp)
692
0
                {
693
0
                    for (int i = 0; pszTemp[i] != '\0' && l_nBands < MAX_FILES;
694
0
                         i++)
695
0
                    {
696
0
                        if (pszTemp[i] >= '2' && pszTemp[i] <= '5')
697
0
                        {
698
0
                            if (poDS->FOpenChannel(poDS->pszFilename, l_nBands,
699
0
                                                   pszTemp[i] - '0'))
700
0
                                l_nBands++;
701
0
                        }
702
0
                    }
703
0
                    CPLFree(pszTemp);
704
0
                }
705
0
            }
706
7
        }
707
3.05k
    }
708
709
    // If the previous lookup for band files didn't success, fallback to the
710
    // standard way of finding them, either by the FILENAME field, either with
711
    // the usual patterns like bandX.dat, etc.
712
3.08k
    if (!l_nBands)
713
3.08k
    {
714
3.08k
        const char *pszTemp = pszHeader;
715
24.6k
        for (int i = 0; i < 7; i++)
716
21.5k
        {
717
21.5k
            char *pszFilename = nullptr;
718
21.5k
            if (pszTemp)
719
3.14k
                pszTemp = strstr(pszTemp, FILENAME);
720
21.5k
            if (pszTemp)
721
60
            {
722
                // Skip the parameter name
723
60
                pszTemp += strlen(FILENAME);
724
                // Skip whitespaces and equal signs
725
150
                while (*pszTemp == ' ')
726
90
                    pszTemp++;
727
97
                while (*pszTemp == '=')
728
37
                    pszTemp++;
729
60
                pszFilename =
730
60
                    CPLScanString(pszTemp, FILENAME_SIZE, TRUE, FALSE);
731
60
            }
732
21.5k
            else
733
21.5k
                pszTemp = nullptr;
734
21.5k
            if (poDS->FOpenChannel(pszFilename, l_nBands, l_nBands + 1))
735
0
                l_nBands++;
736
21.5k
            if (pszFilename)
737
60
                CPLFree(pszFilename);
738
21.5k
        }
739
3.08k
    }
740
741
3.08k
    if (!l_nBands)
742
3.08k
    {
743
3.08k
        CPLError(CE_Failure, CPLE_NotSupported,
744
3.08k
                 "Failed to find and open band data files.");
745
3.08k
        return nullptr;
746
3.08k
    }
747
748
    // Read number of pixels/lines and bit depth
749
0
    {
750
0
        char *pszTemp = GetValue(pszHeader, PIXELS, PIXELS_SIZE, FALSE);
751
0
        if (pszTemp)
752
0
        {
753
0
            poDS->nRasterXSize = atoi(pszTemp);
754
0
            CPLFree(pszTemp);
755
0
        }
756
0
        else
757
0
        {
758
0
            CPLDebug("FAST", "Failed to find number of pixels in line.");
759
0
            return nullptr;
760
0
        }
761
0
    }
762
763
0
    {
764
0
        char *pszTemp = GetValue(pszHeader, LINES1, LINES_SIZE, FALSE);
765
0
        if (!pszTemp)
766
0
            pszTemp = GetValue(pszHeader, LINES2, LINES_SIZE, FALSE);
767
0
        if (pszTemp)
768
0
        {
769
0
            poDS->nRasterYSize = atoi(pszTemp);
770
0
            CPLFree(pszTemp);
771
0
        }
772
0
        else
773
0
        {
774
0
            CPLDebug("FAST", "Failed to find number of lines in raster.");
775
0
            return nullptr;
776
0
        }
777
0
    }
778
779
0
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
780
0
    {
781
0
        return nullptr;
782
0
    }
783
784
0
    {
785
0
        char *pszTemp =
786
0
            GetValue(pszHeader, BITS_PER_PIXEL, BITS_PER_PIXEL_SIZE, FALSE);
787
0
        if (pszTemp)
788
0
        {
789
0
            switch (atoi(pszTemp))
790
0
            {
791
0
                case 8:
792
0
                default:
793
0
                    poDS->eDataType = GDT_UInt8;
794
0
                    break;
795
                // For a strange reason, some Euromap products declare 10 bits
796
                // output, but are 16 bits.
797
0
                case 10:
798
0
                case 16:
799
0
                    poDS->eDataType = GDT_UInt16;
800
0
                    break;
801
0
            }
802
0
            CPLFree(pszTemp);
803
0
        }
804
0
        else
805
0
        {
806
0
            poDS->eDataType = GDT_UInt8;
807
0
        }
808
0
    }
809
810
    /* -------------------------------------------------------------------- */
811
    /*  Read radiometric record.                                            */
812
    /* -------------------------------------------------------------------- */
813
0
    {
814
0
        const char *pszFirst = nullptr;
815
0
        const char *pszSecond = nullptr;
816
817
        // Read gains and biases. This is a trick!
818
0
        const char *pszTemp =
819
0
            strstr(pszHeader, "BIASES");  // It may be "BIASES AND GAINS"
820
                                          // or "GAINS AND BIASES"
821
0
        const char *pszGains = strstr(pszHeader, "GAINS");
822
0
        if (pszTemp == nullptr || pszGains == nullptr)
823
0
        {
824
0
            CPLDebug("FAST", "No BIASES and/or GAINS");
825
0
            return nullptr;
826
0
        }
827
0
        if (pszTemp > pszGains)
828
0
        {
829
0
            pszFirst = "GAIN%d";
830
0
            pszSecond = "BIAS%d";
831
0
        }
832
0
        else
833
0
        {
834
0
            pszFirst = "BIAS%d";
835
0
            pszSecond = "GAIN%d";
836
0
        }
837
838
        // Now search for the first number occurrence after that string.
839
0
        for (int i = 1; i <= l_nBands; i++)
840
0
        {
841
0
            char *pszValue = nullptr;
842
0
            size_t nValueLen = VALUE_SIZE;
843
844
0
            pszTemp = strpbrk(pszTemp, "-.0123456789");
845
0
            if (pszTemp)
846
0
            {
847
0
                nValueLen = strspn(pszTemp, "+-.0123456789");
848
0
                pszValue = CPLScanString(pszTemp, static_cast<int>(nValueLen),
849
0
                                         TRUE, TRUE);
850
0
                poDS->SetMetadataItem(CPLSPrintf(pszFirst, i), pszValue);
851
0
                CPLFree(pszValue);
852
0
            }
853
0
            else
854
0
            {
855
0
                return nullptr;
856
0
            }
857
0
            pszTemp += nValueLen;
858
0
            pszTemp = strpbrk(pszTemp, "-.0123456789");
859
0
            if (pszTemp)
860
0
            {
861
0
                nValueLen = strspn(pszTemp, "+-.0123456789");
862
0
                pszValue = CPLScanString(pszTemp, static_cast<int>(nValueLen),
863
0
                                         TRUE, TRUE);
864
0
                poDS->SetMetadataItem(CPLSPrintf(pszSecond, i), pszValue);
865
0
                CPLFree(pszValue);
866
0
            }
867
0
            else
868
0
            {
869
0
                return nullptr;
870
0
            }
871
0
            pszTemp += nValueLen;
872
0
        }
873
0
    }
874
875
    /* -------------------------------------------------------------------- */
876
    /*  Read geometric record.                                              */
877
    /* -------------------------------------------------------------------- */
878
    // Coordinates of pixel's centers
879
0
    double dfULX = 0.0;
880
0
    double dfULY = 0.0;
881
0
    double dfURX = 0.0;
882
0
    double dfURY = 0.0;
883
0
    double dfLLX = 0.0;
884
0
    double dfLLY = 0.0;
885
0
    double dfLRX = 0.0;
886
0
    double dfLRY = 0.0;
887
888
    // Read projection name
889
0
    long iProjSys = 0;
890
0
    {
891
0
        char *pszTemp =
892
0
            GetValue(pszHeader, PROJECTION_NAME, PROJECTION_NAME_SIZE, FALSE);
893
0
        if (pszTemp && !EQUAL(pszTemp, ""))
894
0
            iProjSys = USGSMnemonicToCode(pszTemp);
895
0
        else
896
0
            iProjSys = 1L;  // UTM by default
897
0
        CPLFree(pszTemp);
898
0
    }
899
900
    // Read ellipsoid name
901
0
    long iDatum = 0;  // Clarke, 1866 (NAD1927) by default.
902
0
    {
903
0
        char *pszTemp =
904
0
            GetValue(pszHeader, ELLIPSOID_NAME, ELLIPSOID_NAME_SIZE, FALSE);
905
0
        if (pszTemp && !EQUAL(pszTemp, ""))
906
0
            iDatum = USGSEllipsoidToCode(pszTemp);
907
0
        CPLFree(pszTemp);
908
0
    }
909
910
    // Read zone number.
911
0
    long iZone = 0;
912
0
    {
913
0
        char *pszTemp =
914
0
            GetValue(pszHeader, ZONE_NUMBER, ZONE_NUMBER_SIZE, FALSE);
915
0
        if (pszTemp && !EQUAL(pszTemp, ""))
916
0
            iZone = atoi(pszTemp);
917
0
        CPLFree(pszTemp);
918
0
    }
919
920
    // Read 15 USGS projection parameters
921
0
    double adfProjParams[15] = {0.0};
922
0
    {
923
0
        const char *pszTemp = strstr(pszHeader, USGS_PARAMETERS);
924
0
        if (pszTemp && !EQUAL(pszTemp, ""))
925
0
        {
926
0
            pszTemp += strlen(USGS_PARAMETERS);
927
0
            for (int i = 0; i < 15; i++)
928
0
            {
929
0
                pszTemp = strpbrk(pszTemp, "-.0123456789");
930
0
                if (pszTemp)
931
0
                {
932
0
                    adfProjParams[i] = CPLScanDouble(pszTemp, VALUE_SIZE);
933
0
                    pszTemp = strpbrk(pszTemp, " \t");
934
0
                }
935
0
                if (pszTemp == nullptr)
936
0
                {
937
0
                    return nullptr;
938
0
                }
939
0
            }
940
0
        }
941
0
    }
942
943
    // Coordinates should follow the word "PROJECTION", otherwise we can
944
    // be confused by other occurrences of the corner keywords.
945
0
    const char *pszGeomRecord = strstr(pszHeader, "PROJECTION");
946
0
    if (pszGeomRecord)
947
0
    {
948
        // Read corner coordinates
949
0
        const char *pszTemp = strstr(pszGeomRecord, CORNER_UPPER_LEFT);
950
0
        if (pszTemp && !EQUAL(pszTemp, "") &&
951
0
            strlen(pszTemp) >=
952
0
                strlen(CORNER_UPPER_LEFT) + 28 + CORNER_VALUE_SIZE + 1)
953
0
        {
954
0
            pszTemp += strlen(CORNER_UPPER_LEFT) + 28;
955
0
            dfULX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
956
0
            pszTemp += CORNER_VALUE_SIZE + 1;
957
0
            dfULY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
958
0
        }
959
960
0
        pszTemp = strstr(pszGeomRecord, CORNER_UPPER_RIGHT);
961
0
        if (pszTemp && !EQUAL(pszTemp, "") &&
962
0
            strlen(pszTemp) >=
963
0
                strlen(CORNER_UPPER_RIGHT) + 28 + CORNER_VALUE_SIZE + 1)
964
0
        {
965
0
            pszTemp += strlen(CORNER_UPPER_RIGHT) + 28;
966
0
            dfURX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
967
0
            pszTemp += CORNER_VALUE_SIZE + 1;
968
0
            dfURY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
969
0
        }
970
971
0
        pszTemp = strstr(pszGeomRecord, CORNER_LOWER_LEFT);
972
0
        if (pszTemp && !EQUAL(pszTemp, "") &&
973
0
            strlen(pszTemp) >=
974
0
                strlen(CORNER_LOWER_LEFT) + 28 + CORNER_VALUE_SIZE + 1)
975
0
        {
976
0
            pszTemp += strlen(CORNER_LOWER_LEFT) + 28;
977
0
            dfLLX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
978
0
            pszTemp += CORNER_VALUE_SIZE + 1;
979
0
            dfLLY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
980
0
        }
981
982
0
        pszTemp = strstr(pszGeomRecord, CORNER_LOWER_RIGHT);
983
0
        if (pszTemp && !EQUAL(pszTemp, "") &&
984
0
            strlen(pszTemp) >=
985
0
                strlen(CORNER_LOWER_RIGHT) + 28 + CORNER_VALUE_SIZE + 1)
986
0
        {
987
0
            pszTemp += strlen(CORNER_LOWER_RIGHT) + 28;
988
0
            dfLRX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
989
0
            pszTemp += CORNER_VALUE_SIZE + 1;
990
0
            dfLRY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
991
0
        }
992
0
    }
993
994
0
    if (dfULX != 0.0 && dfULY != 0.0 && dfURX != 0.0 && dfURY != 0.0 &&
995
0
        dfLLX != 0.0 && dfLLY != 0.0 && dfLRX != 0.0 && dfLRY != 0.0)
996
0
    {
997
        // Strip out zone number from the easting values, if either
998
0
        if (dfULX >= 1000000.0)
999
0
            dfULX -= static_cast<double>(iZone) * 1000000.0;
1000
0
        if (dfURX >= 1000000.0)
1001
0
            dfURX -= static_cast<double>(iZone) * 1000000.0;
1002
0
        if (dfLLX >= 1000000.0)
1003
0
            dfLLX -= static_cast<double>(iZone) * 1000000.0;
1004
0
        if (dfLRX >= 1000000.0)
1005
0
            dfLRX -= static_cast<double>(iZone) * 1000000.0;
1006
1007
        // In EOSAT FAST Rev C, the angles are in decimal degrees
1008
        // otherwise they are in packed DMS format.
1009
0
        const int bAnglesInPackedDMSFormat =
1010
0
            strstr(pszHeader, "REV            C") == nullptr;
1011
1012
        // Create projection definition
1013
0
        OGRErr eErr = poDS->m_oSRS.importFromUSGS(
1014
0
            iProjSys, iZone, adfProjParams, iDatum, bAnglesInPackedDMSFormat);
1015
0
        if (eErr != OGRERR_NONE)
1016
0
            CPLDebug("FAST", "Import projection from USGS failed: %d", eErr);
1017
0
        else
1018
0
        {
1019
0
            poDS->m_oSRS.SetLinearUnits(SRS_UL_METER, 1.0);
1020
1021
            // Read datum name
1022
0
            char *pszTemp =
1023
0
                GetValue(pszHeader, DATUM_NAME, DATUM_NAME_SIZE, FALSE);
1024
0
            if (pszTemp)
1025
0
            {
1026
0
                if (EQUAL(pszTemp, "WGS84"))
1027
0
                    poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
1028
0
                else if (EQUAL(pszTemp, "NAD27"))
1029
0
                    poDS->m_oSRS.SetWellKnownGeogCS("NAD27");
1030
0
                else if (EQUAL(pszTemp, "NAD83"))
1031
0
                    poDS->m_oSRS.SetWellKnownGeogCS("NAD83");
1032
0
                CPLFree(pszTemp);
1033
0
            }
1034
0
            else
1035
0
            {
1036
                // Reasonable fallback
1037
0
                poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
1038
0
            }
1039
0
        }
1040
1041
        // Generate GCPs
1042
0
        GDAL_GCP *pasGCPList =
1043
0
            static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 4));
1044
0
        GDALInitGCPs(4, pasGCPList);
1045
0
        CPLFree(pasGCPList[0].pszId);
1046
0
        CPLFree(pasGCPList[1].pszId);
1047
0
        CPLFree(pasGCPList[2].pszId);
1048
0
        CPLFree(pasGCPList[3].pszId);
1049
1050
        /* Let's order the GCP in TL, TR, BR, BL order to benefit from the */
1051
        /* GDALGCPsToGeoTransform optimization */
1052
0
        pasGCPList[0].pszId = CPLStrdup("UPPER_LEFT");
1053
0
        pasGCPList[0].dfGCPX = dfULX;
1054
0
        pasGCPList[0].dfGCPY = dfULY;
1055
0
        pasGCPList[0].dfGCPZ = 0.0;
1056
0
        pasGCPList[0].dfGCPPixel = 0.5;
1057
0
        pasGCPList[0].dfGCPLine = 0.5;
1058
0
        pasGCPList[1].pszId = CPLStrdup("UPPER_RIGHT");
1059
0
        pasGCPList[1].dfGCPX = dfURX;
1060
0
        pasGCPList[1].dfGCPY = dfURY;
1061
0
        pasGCPList[1].dfGCPZ = 0.0;
1062
0
        pasGCPList[1].dfGCPPixel = poDS->nRasterXSize - 0.5;
1063
0
        pasGCPList[1].dfGCPLine = 0.5;
1064
0
        pasGCPList[2].pszId = CPLStrdup("LOWER_RIGHT");
1065
0
        pasGCPList[2].dfGCPX = dfLRX;
1066
0
        pasGCPList[2].dfGCPY = dfLRY;
1067
0
        pasGCPList[2].dfGCPZ = 0.0;
1068
0
        pasGCPList[2].dfGCPPixel = poDS->nRasterXSize - 0.5;
1069
0
        pasGCPList[2].dfGCPLine = poDS->nRasterYSize - 0.5;
1070
0
        pasGCPList[3].pszId = CPLStrdup("LOWER_LEFT");
1071
0
        pasGCPList[3].dfGCPX = dfLLX;
1072
0
        pasGCPList[3].dfGCPY = dfLLY;
1073
0
        pasGCPList[3].dfGCPZ = 0.0;
1074
0
        pasGCPList[3].dfGCPPixel = 0.5;
1075
0
        pasGCPList[3].dfGCPLine = poDS->nRasterYSize - 0.5;
1076
1077
        // Calculate transformation matrix, if accurate
1078
0
        const bool transform_ok = CPL_TO_BOOL(
1079
0
            GDALGCPsToGeoTransform(4, pasGCPList, poDS->m_gt.data(), 0));
1080
0
        if (!transform_ok)
1081
0
        {
1082
0
            poDS->m_gt = GDALGeoTransform();
1083
0
            poDS->m_oSRS.Clear();
1084
0
        }
1085
1086
0
        GDALDeinitGCPs(4, pasGCPList);
1087
0
        CPLFree(pasGCPList);
1088
0
    }
1089
1090
    /* -------------------------------------------------------------------- */
1091
    /*      Create band information objects.                                */
1092
    /* -------------------------------------------------------------------- */
1093
0
    const int nPixelOffset = GDALGetDataTypeSizeBytes(poDS->eDataType);
1094
0
    const int nLineOffset = poDS->nRasterXSize * nPixelOffset;
1095
1096
0
    for (int i = 1; i <= l_nBands; i++)
1097
0
    {
1098
0
        auto poBand = RawRasterBand::Create(
1099
0
            poDS.get(), i, poDS->fpChannels[i - 1], 0, nPixelOffset,
1100
0
            nLineOffset, poDS->eDataType, RawRasterBand::NATIVE_BYTE_ORDER,
1101
0
            RawRasterBand::OwnFP::NO);
1102
0
        if (!poBand)
1103
0
            return nullptr;
1104
0
        poDS->SetBand(i, std::move(poBand));
1105
0
    }
1106
1107
    /* -------------------------------------------------------------------- */
1108
    /*      Initialize any PAM information.                                 */
1109
    /* -------------------------------------------------------------------- */
1110
0
    poDS->SetDescription(poOpenInfo->pszFilename);
1111
0
    poDS->TryLoadXML();
1112
1113
    // opens overviews.
1114
0
    poDS->oOvManager.Initialize(poDS.get(), poDS->pszFilename);
1115
1116
    /* -------------------------------------------------------------------- */
1117
    /*      Confirm the requested access is supported.                      */
1118
    /* -------------------------------------------------------------------- */
1119
0
    if (poOpenInfo->eAccess == GA_Update)
1120
0
    {
1121
0
        ReportUpdateNotSupportedByDriver("FAST");
1122
0
        return nullptr;
1123
0
    }
1124
1125
0
    return poDS.release();
1126
0
}
1127
1128
/************************************************************************/
1129
/*                         GDALRegister_FAST()                          */
1130
/************************************************************************/
1131
1132
void GDALRegister_FAST()
1133
1134
22
{
1135
22
    if (GDALGetDriverByName("FAST") != nullptr)
1136
0
        return;
1137
1138
22
    GDALDriver *poDriver = new GDALDriver();
1139
1140
22
    poDriver->SetDescription("FAST");
1141
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1142
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "EOSAT FAST Format");
1143
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/fast.html");
1144
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1145
1146
22
    poDriver->pfnOpen = FASTDataset::Open;
1147
1148
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
1149
22
}