Coverage Report

Created: 2025-06-09 08:44

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