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

Generated by: LCOV version 1.14