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

Generated by: LCOV version 1.14