LCOV - code coverage report
Current view: top level - frmts/raw - fastdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 418 550 76.0 %
Date: 2026-07-03 10:13:58 Functions: 14 14 100.0 %

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

Generated by: LCOV version 1.14