Coverage Report

Created: 2025-12-31 08:30

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