LCOV - code coverage report
Current view: top level - frmts/raw - mffdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 563 736 76.5 %
Date: 2024-05-03 15:49:35 Functions: 20 23 87.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GView
       4             :  * Purpose:  Implementation of Atlantis MFF Support
       5             :  * Author:   Frank Warmerdam, warmerda@home.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, Frank Warmerdam
       9             :  * Copyright (c) 2008-2012, 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             : 
      30             : #include "atlsci_spheroid.h"
      31             : #include "cpl_string.h"
      32             : #include "gdal_frmts.h"
      33             : #include "ogr_spatialref.h"
      34             : #include "rawdataset.h"
      35             : 
      36             : #include <cctype>
      37             : #include <cmath>
      38             : #include <algorithm>
      39             : 
      40             : enum
      41             : {
      42             :     MFFPRJ_NONE,
      43             :     MFFPRJ_LL,
      44             :     MFFPRJ_UTM,
      45             :     MFFPRJ_UNRECOGNIZED
      46             : };
      47             : 
      48             : static int GetMFFProjectionType(const OGRSpatialReference *poSRS);
      49             : 
      50             : /************************************************************************/
      51             : /* ==================================================================== */
      52             : /*                              MFFDataset                              */
      53             : /* ==================================================================== */
      54             : /************************************************************************/
      55             : 
      56             : class MFFDataset final : public RawDataset
      57             : {
      58             :     int nGCPCount;
      59             :     GDAL_GCP *pasGCPList;
      60             : 
      61             :     OGRSpatialReference m_oSRS{};
      62             :     OGRSpatialReference m_oGCPSRS{};
      63             :     double adfGeoTransform[6];
      64             :     char **m_papszFileList;
      65             : 
      66             :     void ScanForGCPs();
      67             :     void ScanForProjectionInfo();
      68             : 
      69             :     CPL_DISALLOW_COPY_ASSIGN(MFFDataset)
      70             : 
      71             :     CPLErr Close() override;
      72             : 
      73             :   public:
      74             :     MFFDataset();
      75             :     ~MFFDataset() override;
      76             : 
      77             :     char **papszHdrLines;
      78             : 
      79             :     VSILFILE **pafpBandFiles;
      80             : 
      81             :     char **GetFileList() override;
      82             : 
      83             :     int GetGCPCount() override;
      84             : 
      85           0 :     const OGRSpatialReference *GetGCPSpatialRef() const override
      86             :     {
      87           0 :         return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
      88             :     }
      89             : 
      90             :     const GDAL_GCP *GetGCPs() override;
      91             : 
      92           9 :     const OGRSpatialReference *GetSpatialRef() const override
      93             :     {
      94           9 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
      95             :     }
      96             : 
      97             :     CPLErr GetGeoTransform(double *) override;
      98             : 
      99             :     static GDALDataset *Open(GDALOpenInfo *);
     100             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     101             :                                int nBandsIn, GDALDataType eType,
     102             :                                char **papszParamList);
     103             :     static GDALDataset *CreateCopy(const char *pszFilename,
     104             :                                    GDALDataset *poSrcDS, int bStrict,
     105             :                                    char **papszOptions,
     106             :                                    GDALProgressFunc pfnProgress,
     107             :                                    void *pProgressData);
     108             : };
     109             : 
     110             : /************************************************************************/
     111             : /* ==================================================================== */
     112             : /*                            MFFTiledBand                              */
     113             : /* ==================================================================== */
     114             : /************************************************************************/
     115             : 
     116             : class MFFTiledBand final : public GDALRasterBand
     117             : {
     118             :     friend class MFFDataset;
     119             : 
     120             :     VSILFILE *fpRaw;
     121             :     RawRasterBand::ByteOrder eByteOrder;
     122             : 
     123             :     CPL_DISALLOW_COPY_ASSIGN(MFFTiledBand)
     124             : 
     125             :   public:
     126             :     MFFTiledBand(MFFDataset *, int, VSILFILE *, int, int, GDALDataType,
     127             :                  RawRasterBand::ByteOrder);
     128             :     ~MFFTiledBand() override;
     129             : 
     130             :     CPLErr IReadBlock(int, int, void *) override;
     131             : };
     132             : 
     133             : /************************************************************************/
     134             : /*                            MFFTiledBand()                            */
     135             : /************************************************************************/
     136             : 
     137           2 : MFFTiledBand::MFFTiledBand(MFFDataset *poDSIn, int nBandIn, VSILFILE *fp,
     138             :                            int nTileXSize, int nTileYSize,
     139             :                            GDALDataType eDataTypeIn,
     140           2 :                            RawRasterBand::ByteOrder eByteOrderIn)
     141           2 :     : fpRaw(fp), eByteOrder(eByteOrderIn)
     142             : {
     143           2 :     poDS = poDSIn;
     144           2 :     nBand = nBandIn;
     145             : 
     146           2 :     eDataType = eDataTypeIn;
     147             : 
     148           2 :     nBlockXSize = nTileXSize;
     149           2 :     nBlockYSize = nTileYSize;
     150           2 : }
     151             : 
     152             : /************************************************************************/
     153             : /*                           ~MFFTiledBand()                            */
     154             : /************************************************************************/
     155             : 
     156           4 : MFFTiledBand::~MFFTiledBand()
     157             : 
     158             : {
     159           2 :     if (VSIFCloseL(fpRaw) != 0)
     160             :     {
     161           0 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     162             :     }
     163           4 : }
     164             : 
     165             : /************************************************************************/
     166             : /*                             IReadBlock()                             */
     167             : /************************************************************************/
     168             : 
     169           1 : CPLErr MFFTiledBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     170             : 
     171             : {
     172           1 :     const int nTilesPerRow = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
     173           1 :     const int nWordSize = GDALGetDataTypeSize(eDataType) / 8;
     174           1 :     const int nBlockSize = nWordSize * nBlockXSize * nBlockYSize;
     175             : 
     176           1 :     const vsi_l_offset nOffset =
     177           1 :         nBlockSize *
     178           1 :         (nBlockXOff + static_cast<vsi_l_offset>(nBlockYOff) * nTilesPerRow);
     179             : 
     180           2 :     if (VSIFSeekL(fpRaw, nOffset, SEEK_SET) == -1 ||
     181           1 :         VSIFReadL(pImage, 1, nBlockSize, fpRaw) < 1)
     182             :     {
     183           0 :         CPLError(CE_Failure, CPLE_FileIO,
     184             :                  "Read of tile %d/%d failed with fseek or fread error.",
     185             :                  nBlockXOff, nBlockYOff);
     186           0 :         return CE_Failure;
     187             :     }
     188             : 
     189           1 :     if (eByteOrder != RawRasterBand::NATIVE_BYTE_ORDER && nWordSize > 1)
     190             :     {
     191           0 :         if (GDALDataTypeIsComplex(eDataType))
     192             :         {
     193           0 :             GDALSwapWords(pImage, nWordSize / 2, nBlockXSize * nBlockYSize,
     194             :                           nWordSize);
     195           0 :             GDALSwapWords(reinterpret_cast<GByte *>(pImage) + nWordSize / 2,
     196           0 :                           nWordSize / 2, nBlockXSize * nBlockYSize, nWordSize);
     197             :         }
     198             :         else
     199           0 :             GDALSwapWords(pImage, nWordSize, nBlockXSize * nBlockYSize,
     200             :                           nWordSize);
     201             :     }
     202             : 
     203           1 :     return CE_None;
     204             : }
     205             : 
     206             : /************************************************************************/
     207             : /*                      MFF Spheroids                                   */
     208             : /************************************************************************/
     209             : 
     210             : class MFFSpheroidList : public SpheroidList
     211             : {
     212             :   public:
     213             :     MFFSpheroidList();
     214             : 
     215          15 :     ~MFFSpheroidList()
     216          15 :     {
     217          15 :     }
     218             : };
     219             : 
     220          15 : MFFSpheroidList ::MFFSpheroidList()
     221             : {
     222          15 :     num_spheroids = 18;
     223             : 
     224          15 :     epsilonR = 0.1;
     225          15 :     epsilonI = 0.000001;
     226             : 
     227          15 :     spheroids[0].SetValuesByRadii("SPHERE", 6371007.0, 6371007.0);
     228          15 :     spheroids[1].SetValuesByRadii("EVEREST", 6377304.0, 6356103.0);
     229          15 :     spheroids[2].SetValuesByRadii("BESSEL", 6377397.0, 6356082.0);
     230          15 :     spheroids[3].SetValuesByRadii("AIRY", 6377563.0, 6356300.0);
     231          15 :     spheroids[4].SetValuesByRadii("CLARKE_1858", 6378294.0, 6356621.0);
     232          15 :     spheroids[5].SetValuesByRadii("CLARKE_1866", 6378206.4, 6356583.8);
     233          15 :     spheroids[6].SetValuesByRadii("CLARKE_1880", 6378249.0, 6356517.0);
     234          15 :     spheroids[7].SetValuesByRadii("HAYFORD", 6378388.0, 6356915.0);
     235          15 :     spheroids[8].SetValuesByRadii("KRASOVSKI", 6378245.0, 6356863.0);
     236          15 :     spheroids[9].SetValuesByRadii("HOUGH", 6378270.0, 6356794.0);
     237          15 :     spheroids[10].SetValuesByRadii("FISHER_60", 6378166.0, 6356784.0);
     238          15 :     spheroids[11].SetValuesByRadii("KAULA", 6378165.0, 6356345.0);
     239          15 :     spheroids[12].SetValuesByRadii("IUGG_67", 6378160.0, 6356775.0);
     240          15 :     spheroids[13].SetValuesByRadii("FISHER_68", 6378150.0, 6356330.0);
     241          15 :     spheroids[14].SetValuesByRadii("WGS_72", 6378135.0, 6356751.0);
     242          15 :     spheroids[15].SetValuesByRadii("IUGG_75", 6378140.0, 6356755.0);
     243          15 :     spheroids[16].SetValuesByRadii("WGS_84", 6378137.0, 6356752.0);
     244          15 :     spheroids[17].SetValuesByRadii("HUGHES", 6378273.0, 6356889.4);
     245          15 : }
     246             : 
     247             : /************************************************************************/
     248             : /* ==================================================================== */
     249             : /*                              MFFDataset                              */
     250             : /* ==================================================================== */
     251             : /************************************************************************/
     252             : 
     253             : /************************************************************************/
     254             : /*                            MFFDataset()                             */
     255             : /************************************************************************/
     256             : 
     257          29 : MFFDataset::MFFDataset()
     258             :     : nGCPCount(0), pasGCPList(nullptr), m_papszFileList(nullptr),
     259          29 :       papszHdrLines(nullptr), pafpBandFiles(nullptr)
     260             : {
     261          29 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     262          29 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     263          29 :     adfGeoTransform[0] = 0.0;
     264          29 :     adfGeoTransform[1] = 1.0;
     265          29 :     adfGeoTransform[2] = 0.0;
     266          29 :     adfGeoTransform[3] = 0.0;
     267          29 :     adfGeoTransform[4] = 0.0;
     268          29 :     adfGeoTransform[5] = 1.0;
     269          29 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                            ~MFFDataset()                            */
     273             : /************************************************************************/
     274             : 
     275          58 : MFFDataset::~MFFDataset()
     276             : 
     277             : {
     278          29 :     MFFDataset::Close();
     279          58 : }
     280             : 
     281             : /************************************************************************/
     282             : /*                              Close()                                 */
     283             : /************************************************************************/
     284             : 
     285          58 : CPLErr MFFDataset::Close()
     286             : {
     287          58 :     CPLErr eErr = CE_None;
     288          58 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     289             :     {
     290          29 :         if (MFFDataset::FlushCache(true) != CE_None)
     291           0 :             eErr = CE_Failure;
     292             : 
     293          29 :         CSLDestroy(papszHdrLines);
     294          29 :         if (pafpBandFiles)
     295             :         {
     296           0 :             for (int i = 0; i < GetRasterCount(); i++)
     297             :             {
     298           0 :                 if (pafpBandFiles[i])
     299             :                 {
     300           0 :                     if (VSIFCloseL(pafpBandFiles[i]) != 0)
     301             :                     {
     302           0 :                         eErr = CE_Failure;
     303           0 :                         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     304             :                     }
     305             :                 }
     306             :             }
     307           0 :             CPLFree(pafpBandFiles);
     308             :         }
     309             : 
     310          29 :         if (nGCPCount > 0)
     311             :         {
     312           6 :             GDALDeinitGCPs(nGCPCount, pasGCPList);
     313             :         }
     314          29 :         CPLFree(pasGCPList);
     315          29 :         CSLDestroy(m_papszFileList);
     316             : 
     317          29 :         if (GDALPamDataset::Close() != CE_None)
     318           0 :             eErr = CE_Failure;
     319             :     }
     320          58 :     return eErr;
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                            GetFileList()                             */
     325             : /************************************************************************/
     326             : 
     327           3 : char **MFFDataset::GetFileList()
     328             : {
     329           3 :     char **papszFileList = RawDataset::GetFileList();
     330           3 :     papszFileList = CSLInsertStrings(papszFileList, -1, m_papszFileList);
     331           3 :     return papszFileList;
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                            GetGCPCount()                             */
     336             : /************************************************************************/
     337             : 
     338           0 : int MFFDataset::GetGCPCount()
     339             : 
     340             : {
     341           0 :     return nGCPCount;
     342             : }
     343             : 
     344             : /************************************************************************/
     345             : /*                          GetGeoTransform()                           */
     346             : /************************************************************************/
     347             : 
     348           9 : CPLErr MFFDataset::GetGeoTransform(double *padfTransform)
     349             : 
     350             : {
     351           9 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     352           9 :     return CE_None;
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                               GetGCP()                               */
     357             : /************************************************************************/
     358             : 
     359           0 : const GDAL_GCP *MFFDataset::GetGCPs()
     360             : 
     361             : {
     362           0 :     return pasGCPList;
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                            ScanForGCPs()                             */
     367             : /************************************************************************/
     368             : 
     369          29 : void MFFDataset::ScanForGCPs()
     370             : 
     371             : {
     372          29 :     int NUM_GCPS = 0;
     373             : 
     374          29 :     if (CSLFetchNameValue(papszHdrLines, "NUM_GCPS") != nullptr)
     375           4 :         NUM_GCPS = atoi(CSLFetchNameValue(papszHdrLines, "NUM_GCPS"));
     376          29 :     if (NUM_GCPS < 0)
     377           0 :         return;
     378             : 
     379          29 :     nGCPCount = 0;
     380          29 :     pasGCPList =
     381          29 :         static_cast<GDAL_GCP *>(VSICalloc(sizeof(GDAL_GCP), 5 + NUM_GCPS));
     382          29 :     if (pasGCPList == nullptr)
     383           0 :         return;
     384             : 
     385         174 :     for (int nCorner = 0; nCorner < 5; nCorner++)
     386             :     {
     387         145 :         const char *pszBase = nullptr;
     388         145 :         double dfRasterX = 0.0;
     389         145 :         double dfRasterY = 0.0;
     390             : 
     391         145 :         if (nCorner == 0)
     392             :         {
     393          29 :             dfRasterX = 0.5;
     394          29 :             dfRasterY = 0.5;
     395          29 :             pszBase = "TOP_LEFT_CORNER";
     396             :         }
     397         116 :         else if (nCorner == 1)
     398             :         {
     399          29 :             dfRasterX = GetRasterXSize() - 0.5;
     400          29 :             dfRasterY = 0.5;
     401          29 :             pszBase = "TOP_RIGHT_CORNER";
     402             :         }
     403          87 :         else if (nCorner == 2)
     404             :         {
     405          29 :             dfRasterX = GetRasterXSize() - 0.5;
     406          29 :             dfRasterY = GetRasterYSize() - 0.5;
     407          29 :             pszBase = "BOTTOM_RIGHT_CORNER";
     408             :         }
     409          58 :         else if (nCorner == 3)
     410             :         {
     411          29 :             dfRasterX = 0.5;
     412          29 :             dfRasterY = GetRasterYSize() - 0.5;
     413          29 :             pszBase = "BOTTOM_LEFT_CORNER";
     414             :         }
     415             :         else /* if( nCorner == 4 ) */
     416             :         {
     417          29 :             dfRasterX = GetRasterXSize() / 2.0;
     418          29 :             dfRasterY = GetRasterYSize() / 2.0;
     419          29 :             pszBase = "CENTRE";
     420             :         }
     421             : 
     422         145 :         char szLatName[40] = {'\0'};
     423         145 :         char szLongName[40] = {'\0'};
     424         145 :         snprintf(szLatName, sizeof(szLatName), "%s_LATITUDE", pszBase);
     425         145 :         snprintf(szLongName, sizeof(szLongName), "%s_LONGITUDE", pszBase);
     426             : 
     427         155 :         if (CSLFetchNameValue(papszHdrLines, szLatName) != nullptr &&
     428          10 :             CSLFetchNameValue(papszHdrLines, szLongName) != nullptr)
     429             :         {
     430          10 :             GDALInitGCPs(1, pasGCPList + nGCPCount);
     431             : 
     432          10 :             CPLFree(pasGCPList[nGCPCount].pszId);
     433             : 
     434          10 :             pasGCPList[nGCPCount].pszId = CPLStrdup(pszBase);
     435             : 
     436          20 :             pasGCPList[nGCPCount].dfGCPX =
     437          10 :                 CPLAtof(CSLFetchNameValue(papszHdrLines, szLongName));
     438          20 :             pasGCPList[nGCPCount].dfGCPY =
     439          10 :                 CPLAtof(CSLFetchNameValue(papszHdrLines, szLatName));
     440          10 :             pasGCPList[nGCPCount].dfGCPZ = 0.0;
     441             : 
     442          10 :             pasGCPList[nGCPCount].dfGCPPixel = dfRasterX;
     443          10 :             pasGCPList[nGCPCount].dfGCPLine = dfRasterY;
     444             : 
     445          10 :             nGCPCount++;
     446             :         }
     447             :     }
     448             : 
     449             :     /* -------------------------------------------------------------------- */
     450             :     /*      Collect standalone GCPs.  They look like:                       */
     451             :     /*                                                                      */
     452             :     /*      GCPn = row, col, lat, long                                      */
     453             :     /*      GCP1 = 1, 1, 45.0, -75.0                                        */
     454             :     /* -------------------------------------------------------------------- */
     455          33 :     for (int i = 0; i < NUM_GCPS; i++)
     456             :     {
     457           4 :         char szName[25] = {'\0'};
     458           4 :         snprintf(szName, sizeof(szName), "GCP%d", i + 1);
     459           4 :         if (CSLFetchNameValue(papszHdrLines, szName) == nullptr)
     460           0 :             continue;
     461             : 
     462           4 :         char **papszTokens = CSLTokenizeStringComplex(
     463           4 :             CSLFetchNameValue(papszHdrLines, szName), ",", FALSE, FALSE);
     464           4 :         if (CSLCount(papszTokens) == 4)
     465             :         {
     466           4 :             GDALInitGCPs(1, pasGCPList + nGCPCount);
     467             : 
     468           4 :             CPLFree(pasGCPList[nGCPCount].pszId);
     469           4 :             pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
     470             : 
     471           4 :             pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[3]);
     472           4 :             pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[2]);
     473           4 :             pasGCPList[nGCPCount].dfGCPZ = 0.0;
     474           4 :             pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[1]) + 0.5;
     475           4 :             pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[0]) + 0.5;
     476             : 
     477           4 :             nGCPCount++;
     478             :         }
     479             : 
     480           4 :         CSLDestroy(papszTokens);
     481             :     }
     482             : }
     483             : 
     484             : /************************************************************************/
     485             : /*                        ScanForProjectionInfo                         */
     486             : /************************************************************************/
     487             : 
     488          29 : void MFFDataset::ScanForProjectionInfo()
     489             : {
     490             :     const char *pszProjName =
     491          29 :         CSLFetchNameValue(papszHdrLines, "PROJECTION_NAME");
     492             :     const char *pszOriginLong =
     493          29 :         CSLFetchNameValue(papszHdrLines, "PROJECTION_ORIGIN_LONGITUDE");
     494             :     const char *pszSpheroidName =
     495          29 :         CSLFetchNameValue(papszHdrLines, "SPHEROID_NAME");
     496             : 
     497          29 :     if (pszProjName == nullptr)
     498             :     {
     499          23 :         m_oSRS.Clear();
     500          23 :         m_oGCPSRS.Clear();
     501          23 :         return;
     502             :     }
     503           6 :     else if ((!EQUAL(pszProjName, "utm")) && (!EQUAL(pszProjName, "ll")))
     504             :     {
     505           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     506             :                  "Only utm and lat/long projections are currently supported.");
     507           0 :         m_oSRS.Clear();
     508           0 :         m_oGCPSRS.Clear();
     509           0 :         return;
     510             :     }
     511           6 :     MFFSpheroidList *mffEllipsoids = new MFFSpheroidList;
     512             : 
     513          12 :     OGRSpatialReference oProj;
     514           6 :     oProj.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     515           6 :     if (EQUAL(pszProjName, "utm"))
     516             :     {
     517             :         int nZone;
     518             : 
     519           6 :         if (pszOriginLong == nullptr)
     520             :         {
     521             :             // If origin not specified, assume 0.0.
     522           4 :             CPLError(
     523             :                 CE_Warning, CPLE_AppDefined,
     524             :                 "No projection origin longitude specified.  Assuming 0.0.");
     525           4 :             nZone = 31;
     526             :         }
     527             :         else
     528           2 :             nZone = 31 + static_cast<int>(floor(CPLAtof(pszOriginLong) / 6.0));
     529             : 
     530           6 :         if (nGCPCount >= 5 && pasGCPList[4].dfGCPY < 0)
     531           0 :             oProj.SetUTM(nZone, 0);
     532             :         else
     533           6 :             oProj.SetUTM(nZone, 1);
     534             : 
     535           6 :         if (pszOriginLong != nullptr)
     536           2 :             oProj.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, CPLAtof(pszOriginLong));
     537             :     }
     538             : 
     539          12 :     OGRSpatialReference oLL;
     540           6 :     oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     541           6 :     if (pszOriginLong != nullptr)
     542           2 :         oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
     543             : 
     544           6 :     if (pszSpheroidName == nullptr)
     545             :     {
     546           4 :         CPLError(CE_Warning, CPLE_AppDefined,
     547             :                  "Unspecified ellipsoid.  Using wgs-84 parameters.\n");
     548             : 
     549           4 :         oProj.SetWellKnownGeogCS("WGS84");
     550           4 :         oLL.SetWellKnownGeogCS("WGS84");
     551             :     }
     552             :     else
     553             :     {
     554           2 :         if (mffEllipsoids->SpheroidInList(pszSpheroidName))
     555             :         {
     556           2 :             oProj.SetGeogCS(
     557             :                 "unknown", "unknown", pszSpheroidName,
     558             :                 mffEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
     559             :                 mffEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName));
     560           2 :             oLL.SetGeogCS(
     561             :                 "unknown", "unknown", pszSpheroidName,
     562             :                 mffEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
     563             :                 mffEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName));
     564             :         }
     565           0 :         else if (EQUAL(pszSpheroidName, "USER_DEFINED"))
     566             :         {
     567             :             const char *pszSpheroidEqRadius =
     568           0 :                 CSLFetchNameValue(papszHdrLines, "SPHEROID_EQUATORIAL_RADIUS");
     569             :             const char *pszSpheroidPolarRadius =
     570           0 :                 CSLFetchNameValue(papszHdrLines, "SPHEROID_POLAR_RADIUS");
     571           0 :             if ((pszSpheroidEqRadius != nullptr) &&
     572             :                 (pszSpheroidPolarRadius != nullptr))
     573             :             {
     574           0 :                 const double eq_radius = CPLAtof(pszSpheroidEqRadius);
     575           0 :                 const double polar_radius = CPLAtof(pszSpheroidPolarRadius);
     576           0 :                 oProj.SetGeogCS("unknown", "unknown", "unknown", eq_radius,
     577           0 :                                 eq_radius / (eq_radius - polar_radius));
     578           0 :                 oLL.SetGeogCS("unknown", "unknown", "unknown", eq_radius,
     579           0 :                               eq_radius / (eq_radius - polar_radius));
     580             :             }
     581             :             else
     582             :             {
     583           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     584             :                          "Radii not specified for user-defined ellipsoid. "
     585             :                          "Using wgs-84 parameters.");
     586           0 :                 oProj.SetWellKnownGeogCS("WGS84");
     587           0 :                 oLL.SetWellKnownGeogCS("WGS84");
     588             :             }
     589             :         }
     590             :         else
     591             :         {
     592           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     593             :                      "Unrecognized ellipsoid.  Using wgs-84 parameters.");
     594           0 :             oProj.SetWellKnownGeogCS("WGS84");
     595           0 :             oLL.SetWellKnownGeogCS("WGS84");
     596             :         }
     597             :     }
     598             : 
     599             :     /* If a geotransform is sufficient to represent the GCP's (i.e. each */
     600             :     /* estimated gcp is within 0.25*pixel size of the actual value- this */
     601             :     /* is the test applied by GDALGCPsToGeoTransform), store the         */
     602             :     /* geotransform.                                                     */
     603           6 :     bool transform_ok = false;
     604             : 
     605           6 :     if (EQUAL(pszProjName, "LL"))
     606             :     {
     607           0 :         transform_ok = CPL_TO_BOOL(
     608           0 :             GDALGCPsToGeoTransform(nGCPCount, pasGCPList, adfGeoTransform, 0));
     609             :     }
     610             :     else
     611             :     {
     612             :         OGRCoordinateTransformation *poTransform =
     613           6 :             OGRCreateCoordinateTransformation(&oLL, &oProj);
     614           6 :         bool bSuccess = true;
     615           6 :         if (poTransform == nullptr)
     616             :         {
     617           0 :             CPLErrorReset();
     618           0 :             bSuccess = FALSE;
     619             :         }
     620             : 
     621             :         double *dfPrjX =
     622           6 :             static_cast<double *>(CPLMalloc(nGCPCount * sizeof(double)));
     623             :         double *dfPrjY =
     624           6 :             static_cast<double *>(CPLMalloc(nGCPCount * sizeof(double)));
     625             : 
     626          20 :         for (int gcp_index = 0; gcp_index < nGCPCount; gcp_index++)
     627             :         {
     628          14 :             dfPrjX[gcp_index] = pasGCPList[gcp_index].dfGCPX;
     629          14 :             dfPrjY[gcp_index] = pasGCPList[gcp_index].dfGCPY;
     630             : 
     631          28 :             if (bSuccess && !poTransform->Transform(1, &(dfPrjX[gcp_index]),
     632          14 :                                                     &(dfPrjY[gcp_index])))
     633           0 :                 bSuccess = FALSE;
     634             :         }
     635             : 
     636           6 :         if (bSuccess)
     637             :         {
     638          20 :             for (int gcp_index = 0; gcp_index < nGCPCount; gcp_index++)
     639             :             {
     640          14 :                 pasGCPList[gcp_index].dfGCPX = dfPrjX[gcp_index];
     641          14 :                 pasGCPList[gcp_index].dfGCPY = dfPrjY[gcp_index];
     642             :             }
     643           6 :             transform_ok = CPL_TO_BOOL(GDALGCPsToGeoTransform(
     644           6 :                 nGCPCount, pasGCPList, adfGeoTransform, 0));
     645             :         }
     646             : 
     647           6 :         if (poTransform)
     648           6 :             delete poTransform;
     649             : 
     650           6 :         CPLFree(dfPrjX);
     651           6 :         CPLFree(dfPrjY);
     652             :     }
     653             : 
     654           6 :     m_oSRS = oProj;
     655           6 :     m_oGCPSRS = std::move(oProj);
     656             : 
     657           6 :     if (!transform_ok)
     658             :     {
     659             :         /* transform is sufficient in some cases (slant range, standalone gcps)
     660             :          */
     661           4 :         adfGeoTransform[0] = 0.0;
     662           4 :         adfGeoTransform[1] = 1.0;
     663           4 :         adfGeoTransform[2] = 0.0;
     664           4 :         adfGeoTransform[3] = 0.0;
     665           4 :         adfGeoTransform[4] = 0.0;
     666           4 :         adfGeoTransform[5] = 1.0;
     667           4 :         m_oSRS.Clear();
     668             :     }
     669             : 
     670           6 :     delete mffEllipsoids;
     671             : }
     672             : 
     673             : /************************************************************************/
     674             : /*                                Open()                                */
     675             : /************************************************************************/
     676             : 
     677       29281 : GDALDataset *MFFDataset::Open(GDALOpenInfo *poOpenInfo)
     678             : 
     679             : {
     680             :     /* -------------------------------------------------------------------- */
     681             :     /*      We assume the user is pointing to the header file.              */
     682             :     /* -------------------------------------------------------------------- */
     683       29281 :     if (poOpenInfo->nHeaderBytes < 17 || poOpenInfo->fpL == nullptr)
     684       26096 :         return nullptr;
     685             : 
     686        3185 :     if (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hdr"))
     687        3146 :         return nullptr;
     688             : 
     689             :     /* -------------------------------------------------------------------- */
     690             :     /*      Load the .hdr file, and compress white space out around the     */
     691             :     /*      equal sign.                                                     */
     692             :     /* -------------------------------------------------------------------- */
     693          39 :     char **papszHdrLines = CSLLoad(poOpenInfo->pszFilename);
     694          39 :     if (papszHdrLines == nullptr)
     695           0 :         return nullptr;
     696             : 
     697             :     // Remove spaces.  e.g.
     698             :     // SPHEROID_NAME = CLARKE_1866 -> SPHEROID_NAME=CLARKE_1866
     699         712 :     for (int i = 0; papszHdrLines[i] != nullptr; i++)
     700             :     {
     701         673 :         int iDst = 0;
     702         673 :         char *pszLine = papszHdrLines[i];
     703             : 
     704       18285 :         for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
     705             :         {
     706       17612 :             if (pszLine[iSrc] != ' ')
     707             :             {
     708       15438 :                 pszLine[iDst++] = pszLine[iSrc];
     709             :             }
     710             :         }
     711         673 :         pszLine[iDst] = '\0';
     712             :     }
     713             : 
     714             :     /* -------------------------------------------------------------------- */
     715             :     /*      Verify it is an MFF file.                                       */
     716             :     /* -------------------------------------------------------------------- */
     717          68 :     if (CSLFetchNameValue(papszHdrLines, "IMAGE_FILE_FORMAT") != nullptr &&
     718          29 :         !EQUAL(CSLFetchNameValue(papszHdrLines, "IMAGE_FILE_FORMAT"), "MFF"))
     719             :     {
     720           0 :         CSLDestroy(papszHdrLines);
     721           0 :         return nullptr;
     722             :     }
     723             : 
     724          39 :     if ((CSLFetchNameValue(papszHdrLines, "IMAGE_LINES") == nullptr ||
     725          49 :          CSLFetchNameValue(papszHdrLines, "LINE_SAMPLES") == nullptr) &&
     726          10 :         (CSLFetchNameValue(papszHdrLines, "no_rows") == nullptr ||
     727           0 :          CSLFetchNameValue(papszHdrLines, "no_columns") == nullptr))
     728             :     {
     729          10 :         CSLDestroy(papszHdrLines);
     730          10 :         return nullptr;
     731             :     }
     732             : 
     733             :     /* -------------------------------------------------------------------- */
     734             :     /*      Create a corresponding GDALDataset.                             */
     735             :     /* -------------------------------------------------------------------- */
     736          58 :     auto poDS = std::make_unique<MFFDataset>();
     737             : 
     738          29 :     poDS->papszHdrLines = papszHdrLines;
     739             : 
     740          29 :     poDS->eAccess = poOpenInfo->eAccess;
     741             : 
     742             :     /* -------------------------------------------------------------------- */
     743             :     /*      Set some dataset wide information.                              */
     744             :     /* -------------------------------------------------------------------- */
     745          31 :     if (CSLFetchNameValue(papszHdrLines, "no_rows") != nullptr &&
     746           2 :         CSLFetchNameValue(papszHdrLines, "no_columns") != nullptr)
     747             :     {
     748           0 :         poDS->nRasterXSize =
     749           0 :             atoi(CSLFetchNameValue(papszHdrLines, "no_columns"));
     750           0 :         poDS->nRasterYSize = atoi(CSLFetchNameValue(papszHdrLines, "no_rows"));
     751             :     }
     752             :     else
     753             :     {
     754          58 :         poDS->nRasterXSize =
     755          29 :             atoi(CSLFetchNameValue(papszHdrLines, "LINE_SAMPLES"));
     756          29 :         poDS->nRasterYSize =
     757          29 :             atoi(CSLFetchNameValue(papszHdrLines, "IMAGE_LINES"));
     758             :     }
     759             : 
     760          29 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     761             :     {
     762           0 :         return nullptr;
     763             :     }
     764             : 
     765          29 :     RawRasterBand::ByteOrder eByteOrder = RawRasterBand::NATIVE_BYTE_ORDER;
     766             : 
     767          29 :     const char *pszByteOrder = CSLFetchNameValue(papszHdrLines, "BYTE_ORDER");
     768          29 :     if (pszByteOrder)
     769             :     {
     770          25 :         eByteOrder = EQUAL(pszByteOrder, "LSB")
     771          25 :                          ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
     772             :                          : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
     773             :     }
     774             : 
     775             :     /* -------------------------------------------------------------------- */
     776             :     /*      Get some information specific to APP tiled files.               */
     777             :     /* -------------------------------------------------------------------- */
     778          29 :     int nTileXSize = 0;
     779          29 :     int nTileYSize = 0;
     780          29 :     const char *pszRefinedType = CSLFetchNameValue(papszHdrLines, "type");
     781          29 :     const bool bTiled = CSLFetchNameValue(papszHdrLines, "no_rows") != nullptr;
     782             : 
     783          29 :     if (bTiled)
     784             :     {
     785           2 :         if (CSLFetchNameValue(papszHdrLines, "tile_size_rows"))
     786           2 :             nTileYSize =
     787           2 :                 atoi(CSLFetchNameValue(papszHdrLines, "tile_size_rows"));
     788           2 :         if (CSLFetchNameValue(papszHdrLines, "tile_size_columns"))
     789           2 :             nTileXSize =
     790           2 :                 atoi(CSLFetchNameValue(papszHdrLines, "tile_size_columns"));
     791             : 
     792           2 :         if (nTileXSize <= 0 || nTileYSize <= 0 ||
     793           6 :             poDS->nRasterXSize - 1 > INT_MAX - nTileXSize ||
     794           2 :             poDS->nRasterYSize - 1 > INT_MAX - nTileYSize)
     795             :         {
     796           0 :             return nullptr;
     797             :         }
     798             :     }
     799             : 
     800             :     /* -------------------------------------------------------------------- */
     801             :     /*      Read the directory to find matching band files.                 */
     802             :     /* -------------------------------------------------------------------- */
     803          29 :     char *const pszTargetPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename));
     804             :     char *const pszTargetBase =
     805          29 :         CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename));
     806          29 :     char **papszDirFiles = VSIReadDir(CPLGetPath(poOpenInfo->pszFilename));
     807          29 :     if (papszDirFiles == nullptr)
     808             :     {
     809           0 :         CPLFree(pszTargetPath);
     810           0 :         CPLFree(pszTargetBase);
     811           0 :         return nullptr;
     812             :     }
     813             : 
     814          29 :     int nSkipped = 0;
     815          29 :     for (int nRawBand = 0; true; nRawBand++)
     816             :     {
     817          86 :         const char *pszExtension = nullptr;
     818             : 
     819             :         /* Find the next raw band file. */
     820             : 
     821          86 :         int i = 0;  // Used after for.
     822         408 :         for (; papszDirFiles[i] != nullptr; i++)
     823             :         {
     824         379 :             if (!EQUAL(CPLGetBasename(papszDirFiles[i]), pszTargetBase))
     825         133 :                 continue;
     826             : 
     827         246 :             pszExtension = CPLGetExtension(papszDirFiles[i]);
     828         246 :             if (strlen(pszExtension) >= 2 &&
     829         246 :                 isdigit(static_cast<unsigned char>(pszExtension[1])) &&
     830         166 :                 atoi(pszExtension + 1) == nRawBand &&
     831          57 :                 strchr("bBcCiIjJrRxXzZ", pszExtension[0]) != nullptr)
     832          57 :                 break;
     833             :         }
     834             : 
     835          86 :         if (papszDirFiles[i] == nullptr)
     836          29 :             break;
     837             : 
     838             :         /* open the file for required level of access */
     839             :         const char *pszRawFilename =
     840          57 :             CPLFormFilename(pszTargetPath, papszDirFiles[i], nullptr);
     841             : 
     842          57 :         VSILFILE *fpRaw = nullptr;
     843          57 :         if (poOpenInfo->eAccess == GA_Update)
     844          50 :             fpRaw = VSIFOpenL(pszRawFilename, "rb+");
     845             :         else
     846           7 :             fpRaw = VSIFOpenL(pszRawFilename, "rb");
     847             : 
     848          57 :         if (fpRaw == nullptr)
     849             :         {
     850           0 :             CPLError(CE_Warning, CPLE_OpenFailed,
     851             :                      "Unable to open %s ... skipping.", pszRawFilename);
     852           0 :             nSkipped++;
     853           0 :             continue;
     854             :         }
     855         114 :         poDS->m_papszFileList =
     856          57 :             CSLAddString(poDS->m_papszFileList, pszRawFilename);
     857             : 
     858          57 :         GDALDataType eDataType = GDT_Unknown;
     859          57 :         pszExtension = CPLGetExtension(papszDirFiles[i]);
     860          57 :         if (pszRefinedType != nullptr)
     861             :         {
     862           0 :             if (EQUAL(pszRefinedType, "C*4"))
     863           0 :                 eDataType = GDT_CFloat32;
     864           0 :             else if (EQUAL(pszRefinedType, "C*8"))
     865           0 :                 eDataType = GDT_CFloat64;
     866           0 :             else if (EQUAL(pszRefinedType, "R*4"))
     867           0 :                 eDataType = GDT_Float32;
     868           0 :             else if (EQUAL(pszRefinedType, "R*8"))
     869           0 :                 eDataType = GDT_Float64;
     870           0 :             else if (EQUAL(pszRefinedType, "I*1"))
     871           0 :                 eDataType = GDT_Byte;
     872           0 :             else if (EQUAL(pszRefinedType, "I*2"))
     873           0 :                 eDataType = GDT_Int16;
     874           0 :             else if (EQUAL(pszRefinedType, "I*4"))
     875           0 :                 eDataType = GDT_Int32;
     876           0 :             else if (EQUAL(pszRefinedType, "U*2"))
     877           0 :                 eDataType = GDT_UInt16;
     878           0 :             else if (EQUAL(pszRefinedType, "U*4"))
     879           0 :                 eDataType = GDT_UInt32;
     880           0 :             else if (EQUAL(pszRefinedType, "J*1"))
     881             :             {
     882           0 :                 CPLError(
     883             :                     CE_Warning, CPLE_OpenFailed,
     884             :                     "Unable to open band %d because type J*1 is not handled. "
     885             :                     "Skipping.",
     886             :                     nRawBand + 1);
     887           0 :                 nSkipped++;
     888           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
     889           0 :                 continue;  // Does not support 1 byte complex.
     890             :             }
     891           0 :             else if (EQUAL(pszRefinedType, "J*2"))
     892           0 :                 eDataType = GDT_CInt16;
     893           0 :             else if (EQUAL(pszRefinedType, "K*4"))
     894           0 :                 eDataType = GDT_CInt32;
     895             :             else
     896             :             {
     897           0 :                 CPLError(
     898             :                     CE_Warning, CPLE_OpenFailed,
     899             :                     "Unable to open band %d because type %s is not handled. "
     900             :                     "Skipping.\n",
     901             :                     nRawBand + 1, pszRefinedType);
     902           0 :                 nSkipped++;
     903           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
     904           0 :                 continue;
     905             :             }
     906             :         }
     907          57 :         else if (STARTS_WITH_CI(pszExtension, "b"))
     908             :         {
     909          36 :             eDataType = GDT_Byte;
     910             :         }
     911          21 :         else if (STARTS_WITH_CI(pszExtension, "i"))
     912             :         {
     913           5 :             eDataType = GDT_UInt16;
     914             :         }
     915          16 :         else if (STARTS_WITH_CI(pszExtension, "j"))
     916             :         {
     917           5 :             eDataType = GDT_CInt16;
     918             :         }
     919          11 :         else if (STARTS_WITH_CI(pszExtension, "r"))
     920             :         {
     921           6 :             eDataType = GDT_Float32;
     922             :         }
     923           5 :         else if (STARTS_WITH_CI(pszExtension, "x"))
     924             :         {
     925           5 :             eDataType = GDT_CFloat32;
     926             :         }
     927             :         else
     928             :         {
     929           0 :             CPLError(CE_Warning, CPLE_OpenFailed,
     930             :                      "Unable to open band %d because extension %s is not "
     931             :                      "handled.  Skipping.",
     932             :                      nRawBand + 1, pszExtension);
     933           0 :             nSkipped++;
     934           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
     935           0 :             continue;
     936             :         }
     937             : 
     938          57 :         const int nBand = poDS->GetRasterCount() + 1;
     939             : 
     940          57 :         const int nPixelOffset = GDALGetDataTypeSizeBytes(eDataType);
     941           0 :         std::unique_ptr<GDALRasterBand> poBand;
     942             : 
     943          57 :         if (bTiled)
     944             :         {
     945           4 :             poBand = std::make_unique<MFFTiledBand>(poDS.get(), nBand, fpRaw,
     946             :                                                     nTileXSize, nTileYSize,
     947           2 :                                                     eDataType, eByteOrder);
     948             :         }
     949             :         else
     950             :         {
     951         110 :             if (nPixelOffset != 0 &&
     952          55 :                 poDS->GetRasterXSize() > INT_MAX / nPixelOffset)
     953             :             {
     954           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     955             :                          "Int overflow occurred... skipping");
     956           0 :                 nSkipped++;
     957           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fpRaw));
     958           0 :                 continue;
     959             :             }
     960             : 
     961         110 :             poBand = RawRasterBand::Create(
     962          55 :                 poDS.get(), nBand, fpRaw, 0, nPixelOffset,
     963          55 :                 nPixelOffset * poDS->GetRasterXSize(), eDataType, eByteOrder,
     964          55 :                 RawRasterBand::OwnFP::YES);
     965             :         }
     966             : 
     967          57 :         poDS->SetBand(nBand, std::move(poBand));
     968          57 :     }
     969             : 
     970          29 :     CPLFree(pszTargetPath);
     971          29 :     CPLFree(pszTargetBase);
     972          29 :     CSLDestroy(papszDirFiles);
     973             : 
     974             :     /* -------------------------------------------------------------------- */
     975             :     /*      Check if we have bands.                                         */
     976             :     /* -------------------------------------------------------------------- */
     977          29 :     if (poDS->GetRasterCount() == 0)
     978             :     {
     979           0 :         if (nSkipped > 0 && poOpenInfo->eAccess)
     980             :         {
     981           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     982             :                      "Failed to open %d files that were apparently bands.  "
     983             :                      "Perhaps this dataset is readonly?",
     984             :                      nSkipped);
     985           0 :             return nullptr;
     986             :         }
     987             :         else
     988             :         {
     989           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     990             :                      "MFF header file read successfully, but no bands "
     991             :                      "were successfully found and opened.");
     992           0 :             return nullptr;
     993             :         }
     994             :     }
     995             : 
     996             :     /* -------------------------------------------------------------------- */
     997             :     /*      Set all information from the .hdr that isn't well know to be    */
     998             :     /*      metadata.                                                       */
     999             :     /* -------------------------------------------------------------------- */
    1000         226 :     for (int i = 0; papszHdrLines[i] != nullptr; i++)
    1001             :     {
    1002         197 :         char *pszName = nullptr;
    1003             : 
    1004         197 :         const char *pszValue = CPLParseNameValue(papszHdrLines[i], &pszName);
    1005         197 :         if (pszName == nullptr || pszValue == nullptr)
    1006          16 :             continue;
    1007             : 
    1008         181 :         if (!EQUAL(pszName, "END") && !EQUAL(pszName, "FILE_TYPE") &&
    1009         156 :             !EQUAL(pszName, "BYTE_ORDER") && !EQUAL(pszName, "no_columns") &&
    1010         131 :             !EQUAL(pszName, "no_rows") && !EQUAL(pszName, "type") &&
    1011         129 :             !EQUAL(pszName, "tile_size_rows") &&
    1012         127 :             !EQUAL(pszName, "tile_size_columns") &&
    1013         125 :             !EQUAL(pszName, "IMAGE_FILE_FORMAT") &&
    1014          96 :             !EQUAL(pszName, "IMAGE_LINES") && !EQUAL(pszName, "LINE_SAMPLES"))
    1015             :         {
    1016          38 :             poDS->SetMetadataItem(pszName, pszValue);
    1017             :         }
    1018             : 
    1019         181 :         CPLFree(pszName);
    1020             :     }
    1021             : 
    1022             :     /* -------------------------------------------------------------------- */
    1023             :     /*      Any GCPs in header file?                                        */
    1024             :     /* -------------------------------------------------------------------- */
    1025          29 :     poDS->ScanForGCPs();
    1026          29 :     poDS->ScanForProjectionInfo();
    1027          29 :     if (poDS->nGCPCount == 0)
    1028          23 :         poDS->m_oGCPSRS.Clear();
    1029             : 
    1030             :     /* -------------------------------------------------------------------- */
    1031             :     /*      Initialize any PAM information.                                 */
    1032             :     /* -------------------------------------------------------------------- */
    1033          29 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1034          29 :     poDS->TryLoadXML();
    1035             : 
    1036             :     /* -------------------------------------------------------------------- */
    1037             :     /*      Check for overviews.                                            */
    1038             :     /* -------------------------------------------------------------------- */
    1039          29 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
    1040             : 
    1041          29 :     return poDS.release();
    1042             : }
    1043             : 
    1044           9 : int GetMFFProjectionType(const OGRSpatialReference *poSRS)
    1045             : {
    1046           9 :     if (poSRS == nullptr)
    1047             :     {
    1048           0 :         return MFFPRJ_NONE;
    1049             :     }
    1050           9 :     if (poSRS->IsProjected() && poSRS->GetAttrValue("PROJECTION") &&
    1051           0 :         EQUAL(poSRS->GetAttrValue("PROJECTION"), SRS_PT_TRANSVERSE_MERCATOR))
    1052             :     {
    1053           0 :         return MFFPRJ_UTM;
    1054             :     }
    1055           9 :     else if (poSRS->IsGeographic())
    1056             :     {
    1057           9 :         return MFFPRJ_LL;
    1058             :     }
    1059             :     else
    1060             :     {
    1061           0 :         return MFFPRJ_UNRECOGNIZED;
    1062             :     }
    1063             : }
    1064             : 
    1065             : /************************************************************************/
    1066             : /*                               Create()                               */
    1067             : /************************************************************************/
    1068             : 
    1069          50 : GDALDataset *MFFDataset::Create(const char *pszFilenameIn, int nXSize,
    1070             :                                 int nYSize, int nBandsIn, GDALDataType eType,
    1071             :                                 char **papszParamList)
    1072             : 
    1073             : {
    1074             :     /* -------------------------------------------------------------------- */
    1075             :     /*      Verify input options.                                           */
    1076             :     /* -------------------------------------------------------------------- */
    1077          50 :     if (nBandsIn <= 0)
    1078             :     {
    1079           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1080             :                  "MFF driver does not support %d bands.", nBandsIn);
    1081           1 :         return nullptr;
    1082             :     }
    1083             : 
    1084          49 :     if (eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 &&
    1085          27 :         eType != GDT_CInt16 && eType != GDT_CFloat32)
    1086             :     {
    1087          24 :         CPLError(CE_Failure, CPLE_AppDefined,
    1088             :                  "Attempt to create MFF file with currently unsupported\n"
    1089             :                  "data type (%s).\n",
    1090             :                  GDALGetDataTypeName(eType));
    1091             : 
    1092          24 :         return nullptr;
    1093             :     }
    1094             : 
    1095             :     /* -------------------------------------------------------------------- */
    1096             :     /*      Establish the base filename (path+filename, less extension).    */
    1097             :     /* -------------------------------------------------------------------- */
    1098             :     char *pszBaseFilename =
    1099          25 :         static_cast<char *>(CPLMalloc(strlen(pszFilenameIn) + 5));
    1100          25 :     strcpy(pszBaseFilename, pszFilenameIn);
    1101             : 
    1102         100 :     for (int i = static_cast<int>(strlen(pszBaseFilename)) - 1; i > 0; i--)
    1103             :     {
    1104         100 :         if (pszBaseFilename[i] == '.')
    1105             :         {
    1106           0 :             pszBaseFilename[i] = '\0';
    1107           0 :             break;
    1108             :         }
    1109             : 
    1110         100 :         if (pszBaseFilename[i] == '/' || pszBaseFilename[i] == '\\')
    1111             :             break;
    1112             :     }
    1113             : 
    1114             :     /* -------------------------------------------------------------------- */
    1115             :     /*      Create the header file.                                         */
    1116             :     /* -------------------------------------------------------------------- */
    1117          25 :     const char *pszFilename = CPLFormFilename(nullptr, pszBaseFilename, "hdr");
    1118             : 
    1119          25 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wt");
    1120          25 :     if (fp == nullptr)
    1121             :     {
    1122           3 :         CPLError(CE_Failure, CPLE_OpenFailed, "Couldn't create %s.\n",
    1123             :                  pszFilename);
    1124           3 :         CPLFree(pszBaseFilename);
    1125           3 :         return nullptr;
    1126             :     }
    1127             : 
    1128          22 :     bool bOK = VSIFPrintfL(fp, "IMAGE_FILE_FORMAT = MFF\n") >= 0;
    1129          22 :     bOK &= VSIFPrintfL(fp, "FILE_TYPE = IMAGE\n") >= 0;
    1130          22 :     bOK &= VSIFPrintfL(fp, "IMAGE_LINES = %d\n", nYSize) >= 0;
    1131          22 :     bOK &= VSIFPrintfL(fp, "LINE_SAMPLES = %d\n", nXSize) >= 0;
    1132             : #ifdef CPL_MSB
    1133             :     bOK &= VSIFPrintfL(fp, "BYTE_ORDER = MSB\n") >= 0;
    1134             : #else
    1135          22 :     bOK &= VSIFPrintfL(fp, "BYTE_ORDER = LSB\n") >= 0;
    1136             : #endif
    1137             : 
    1138          22 :     if (CSLFetchNameValue(papszParamList, "NO_END") == nullptr)
    1139          13 :         bOK &= VSIFPrintfL(fp, "END\n") >= 0;
    1140             : 
    1141          22 :     if (VSIFCloseL(fp) != 0)
    1142           0 :         bOK = false;
    1143             : 
    1144             :     /* -------------------------------------------------------------------- */
    1145             :     /*      Create the data files, but don't bother writing any data to them.*/
    1146             :     /* -------------------------------------------------------------------- */
    1147          72 :     for (int iBand = 0; bOK && iBand < nBandsIn; iBand++)
    1148             :     {
    1149          50 :         char szExtension[4] = {'\0'};
    1150             : 
    1151          50 :         if (eType == GDT_Byte)
    1152          30 :             CPLsnprintf(szExtension, sizeof(szExtension), "b%02d", iBand);
    1153          20 :         else if (eType == GDT_UInt16)
    1154           5 :             CPLsnprintf(szExtension, sizeof(szExtension), "i%02d", iBand);
    1155          15 :         else if (eType == GDT_Float32)
    1156           5 :             CPLsnprintf(szExtension, sizeof(szExtension), "r%02d", iBand);
    1157          10 :         else if (eType == GDT_CInt16)
    1158           5 :             CPLsnprintf(szExtension, sizeof(szExtension), "j%02d", iBand);
    1159           5 :         else if (eType == GDT_CFloat32)
    1160           5 :             CPLsnprintf(szExtension, sizeof(szExtension), "x%02d", iBand);
    1161             : 
    1162          50 :         pszFilename = CPLFormFilename(nullptr, pszBaseFilename, szExtension);
    1163          50 :         fp = VSIFOpenL(pszFilename, "wb");
    1164          50 :         if (fp == nullptr)
    1165             :         {
    1166           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Couldn't create %s.\n",
    1167             :                      pszFilename);
    1168           0 :             CPLFree(pszBaseFilename);
    1169           0 :             return nullptr;
    1170             :         }
    1171             : 
    1172          50 :         bOK &= VSIFWriteL("", 1, 1, fp) == 1;
    1173          50 :         if (VSIFCloseL(fp) != 0)
    1174           0 :             bOK = false;
    1175             :     }
    1176             : 
    1177          22 :     if (!bOK)
    1178             :     {
    1179           0 :         CPLFree(pszBaseFilename);
    1180           0 :         return nullptr;
    1181             :     }
    1182             : 
    1183             :     /* -------------------------------------------------------------------- */
    1184             :     /*      Open the dataset normally.                                      */
    1185             :     /* -------------------------------------------------------------------- */
    1186          22 :     strcat(pszBaseFilename, ".hdr");
    1187             :     GDALDataset *poDS =
    1188          22 :         static_cast<GDALDataset *>(GDALOpen(pszBaseFilename, GA_Update));
    1189          22 :     CPLFree(pszBaseFilename);
    1190             : 
    1191          22 :     return poDS;
    1192             : }
    1193             : 
    1194             : /************************************************************************/
    1195             : /*                             CreateCopy()                             */
    1196             : /************************************************************************/
    1197             : 
    1198          19 : GDALDataset *MFFDataset::CreateCopy(const char *pszFilename,
    1199             :                                     GDALDataset *poSrcDS, int /* bStrict */,
    1200             :                                     char **papszOptions,
    1201             :                                     GDALProgressFunc pfnProgress,
    1202             :                                     void *pProgressData)
    1203             : {
    1204          19 :     const int nBands = poSrcDS->GetRasterCount();
    1205          19 :     if (nBands == 0)
    1206             :     {
    1207           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1208             :                  "MFF driver does not support source dataset with zero band.");
    1209           1 :         return nullptr;
    1210             :     }
    1211             : 
    1212          18 :     GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    1213          18 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    1214           0 :         return nullptr;
    1215             : 
    1216             :     // Check that other bands match type- sets type
    1217             :     // to unknown if they differ.
    1218          28 :     for (int iBand = 1; iBand < poSrcDS->GetRasterCount(); iBand++)
    1219             :     {
    1220          10 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
    1221          10 :         eType = GDALDataTypeUnion(eType, poBand->GetRasterDataType());
    1222             :     }
    1223             : 
    1224          18 :     char **newpapszOptions = CSLDuplicate(papszOptions);
    1225          18 :     newpapszOptions = CSLSetNameValue(newpapszOptions, "NO_END", "TRUE");
    1226             : 
    1227          18 :     MFFDataset *poDS = reinterpret_cast<MFFDataset *>(Create(
    1228             :         pszFilename, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
    1229             :         poSrcDS->GetRasterCount(), eType, newpapszOptions));
    1230             : 
    1231          18 :     CSLDestroy(newpapszOptions);
    1232             : 
    1233          18 :     if (poDS == nullptr)
    1234           9 :         return nullptr;
    1235             : 
    1236             :     /* -------------------------------------------------------------------- */
    1237             :     /*      Copy the image data.                                            */
    1238             :     /* -------------------------------------------------------------------- */
    1239           9 :     const int nXSize = poDS->GetRasterXSize();
    1240           9 :     const int nYSize = poDS->GetRasterYSize();
    1241             : 
    1242           9 :     int nBlockXSize = 0;
    1243           9 :     int nBlockYSize = 0;
    1244           9 :     poDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1245             : 
    1246           9 :     const int nBlockTotal = ((nXSize + nBlockXSize - 1) / nBlockXSize) *
    1247           9 :                             ((nYSize + nBlockYSize - 1) / nBlockYSize) *
    1248           9 :                             poSrcDS->GetRasterCount();
    1249             : 
    1250           9 :     int nBlocksDone = 0;
    1251          28 :     for (int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++)
    1252             :     {
    1253          19 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
    1254          19 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(iBand + 1);
    1255             : 
    1256          57 :         void *pData = CPLMalloc(static_cast<size_t>(nBlockXSize) * nBlockYSize *
    1257          19 :                                 GDALGetDataTypeSizeBytes(eType));
    1258             : 
    1259         209 :         for (int iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize)
    1260             :         {
    1261         380 :             for (int iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize)
    1262             :             {
    1263         190 :                 if (!pfnProgress((nBlocksDone++) /
    1264         190 :                                      static_cast<float>(nBlockTotal),
    1265             :                                  nullptr, pProgressData))
    1266             :                 {
    1267           0 :                     CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1268           0 :                     delete poDS;
    1269           0 :                     CPLFree(pData);
    1270             : 
    1271             :                     GDALDriver *poMFFDriver =
    1272           0 :                         static_cast<GDALDriver *>(GDALGetDriverByName("MFF"));
    1273           0 :                     poMFFDriver->Delete(pszFilename);
    1274           0 :                     return nullptr;
    1275             :                 }
    1276             : 
    1277         190 :                 const int nTBXSize = std::min(nBlockXSize, nXSize - iXOffset);
    1278         190 :                 const int nTBYSize = std::min(nBlockYSize, nYSize - iYOffset);
    1279             : 
    1280         190 :                 CPLErr eErr = poSrcBand->RasterIO(
    1281             :                     GF_Read, iXOffset, iYOffset, nTBXSize, nTBYSize, pData,
    1282             :                     nTBXSize, nTBYSize, eType, 0, 0, nullptr);
    1283             : 
    1284         190 :                 if (eErr != CE_None)
    1285             :                 {
    1286           0 :                     delete poDS;
    1287           0 :                     CPLFree(pData);
    1288           0 :                     return nullptr;
    1289             :                 }
    1290             : 
    1291         190 :                 eErr = poDstBand->RasterIO(GF_Write, iXOffset, iYOffset,
    1292             :                                            nTBXSize, nTBYSize, pData, nTBXSize,
    1293             :                                            nTBYSize, eType, 0, 0, nullptr);
    1294             : 
    1295         190 :                 if (eErr != CE_None)
    1296             :                 {
    1297           0 :                     delete poDS;
    1298           0 :                     CPLFree(pData);
    1299           0 :                     return nullptr;
    1300             :                 }
    1301             :             }
    1302             :         }
    1303             : 
    1304          19 :         CPLFree(pData);
    1305             :     }
    1306             : 
    1307             :     /* -------------------------------------------------------------------- */
    1308             :     /*      Copy georeferencing information, if enough is available.        */
    1309             :     /* -------------------------------------------------------------------- */
    1310             : 
    1311             :     /* -------------------------------------------------------------------- */
    1312             :     /*      Establish the base filename (path+filename, less extension).    */
    1313             :     /* -------------------------------------------------------------------- */
    1314             :     char *pszBaseFilename =
    1315           9 :         static_cast<char *>(CPLMalloc(strlen(pszFilename) + 5));
    1316           9 :     strcpy(pszBaseFilename, pszFilename);
    1317             : 
    1318          36 :     for (int i = static_cast<int>(strlen(pszBaseFilename)) - 1; i > 0; i--)
    1319             :     {
    1320          36 :         if (pszBaseFilename[i] == '.')
    1321             :         {
    1322           0 :             pszBaseFilename[i] = '\0';
    1323           0 :             break;
    1324             :         }
    1325             : 
    1326          36 :         if (pszBaseFilename[i] == '/' || pszBaseFilename[i] == '\\')
    1327             :             break;
    1328             :     }
    1329             : 
    1330             :     const char *pszFilenameGEO =
    1331           9 :         CPLFormFilename(nullptr, pszBaseFilename, "hdr");
    1332             : 
    1333           9 :     VSILFILE *fp = VSIFOpenL(pszFilenameGEO, "at");
    1334           9 :     if (fp == nullptr)
    1335             :     {
    1336           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1337             :                  "Couldn't open %s for appending.\n", pszFilenameGEO);
    1338           0 :         CPLFree(pszBaseFilename);
    1339           0 :         return nullptr;
    1340             :     }
    1341             : 
    1342             :     /* MFF requires corner and center gcps */
    1343           9 :     bool georef_created = false;
    1344             : 
    1345             :     double *padfTiepoints =
    1346           9 :         static_cast<double *>(CPLMalloc(2 * sizeof(double) * 5));
    1347             : 
    1348           9 :     const int src_prj = GetMFFProjectionType(poSrcDS->GetSpatialRef());
    1349             : 
    1350           9 :     if ((src_prj != MFFPRJ_NONE) && (src_prj != MFFPRJ_UNRECOGNIZED))
    1351             :     {
    1352             :         double *tempGeoTransform =
    1353           9 :             static_cast<double *>(CPLMalloc(6 * sizeof(double)));
    1354             : 
    1355          18 :         if ((poSrcDS->GetGeoTransform(tempGeoTransform) == CE_None) &&
    1356           9 :             (tempGeoTransform[0] != 0.0 || tempGeoTransform[1] != 1.0 ||
    1357           0 :              tempGeoTransform[2] != 0.0 || tempGeoTransform[3] != 0.0 ||
    1358           0 :              tempGeoTransform[4] != 0.0 ||
    1359           0 :              std::abs(tempGeoTransform[5]) != 1.0))
    1360             :         {
    1361           9 :             padfTiepoints[0] = tempGeoTransform[0] + tempGeoTransform[1] * 0.5 +
    1362           9 :                                tempGeoTransform[2] * 0.5;
    1363             : 
    1364           9 :             padfTiepoints[1] = tempGeoTransform[3] + tempGeoTransform[4] * 0.5 +
    1365           9 :                                tempGeoTransform[5] * 0.5;
    1366             : 
    1367           9 :             padfTiepoints[2] =
    1368          18 :                 tempGeoTransform[0] + tempGeoTransform[2] * 0.5 +
    1369           9 :                 tempGeoTransform[1] * (poSrcDS->GetRasterXSize() - 0.5);
    1370             : 
    1371           9 :             padfTiepoints[3] =
    1372          18 :                 tempGeoTransform[3] + tempGeoTransform[5] * 0.5 +
    1373           9 :                 tempGeoTransform[4] * (poSrcDS->GetRasterXSize() - 0.5);
    1374             : 
    1375           9 :             padfTiepoints[4] =
    1376          18 :                 tempGeoTransform[0] + tempGeoTransform[1] * 0.5 +
    1377           9 :                 tempGeoTransform[2] * (poSrcDS->GetRasterYSize() - 0.5);
    1378             : 
    1379           9 :             padfTiepoints[5] =
    1380          18 :                 tempGeoTransform[3] + tempGeoTransform[4] * 0.5 +
    1381           9 :                 tempGeoTransform[5] * (poSrcDS->GetRasterYSize() - 0.5);
    1382             : 
    1383           9 :             padfTiepoints[6] =
    1384          18 :                 tempGeoTransform[0] +
    1385           9 :                 tempGeoTransform[1] * (poSrcDS->GetRasterXSize() - 0.5) +
    1386           9 :                 tempGeoTransform[2] * (poSrcDS->GetRasterYSize() - 0.5);
    1387             : 
    1388           9 :             padfTiepoints[7] =
    1389          18 :                 tempGeoTransform[3] +
    1390           9 :                 tempGeoTransform[4] * (poSrcDS->GetRasterXSize() - 0.5) +
    1391           9 :                 tempGeoTransform[5] * (poSrcDS->GetRasterYSize() - 0.5);
    1392             : 
    1393           9 :             padfTiepoints[8] =
    1394          18 :                 tempGeoTransform[0] +
    1395           9 :                 tempGeoTransform[1] * (poSrcDS->GetRasterXSize()) / 2.0 +
    1396           9 :                 tempGeoTransform[2] * (poSrcDS->GetRasterYSize()) / 2.0;
    1397             : 
    1398           9 :             padfTiepoints[9] =
    1399          18 :                 tempGeoTransform[3] +
    1400           9 :                 tempGeoTransform[4] * (poSrcDS->GetRasterXSize()) / 2.0 +
    1401           9 :                 tempGeoTransform[5] * (poSrcDS->GetRasterYSize()) / 2.0;
    1402             : 
    1403          18 :             OGRSpatialReference oUTMorLL;
    1404           9 :             const auto poSrcSRS = poSrcDS->GetSpatialRef();
    1405           9 :             if (poSrcSRS)
    1406           9 :                 oUTMorLL = *poSrcSRS;
    1407           9 :             auto poLLSRS = oUTMorLL.CloneGeogCS();
    1408           9 :             if (poLLSRS && oUTMorLL.IsProjected())
    1409             :             {
    1410           0 :                 poLLSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1411             :                 OGRCoordinateTransformation *poTransform =
    1412           0 :                     OGRCreateCoordinateTransformation(&oUTMorLL, poLLSRS);
    1413             : 
    1414             :                 // projected coordinate system- need to translate gcps */
    1415           0 :                 bool bSuccess = poTransform != nullptr;
    1416             : 
    1417           0 :                 for (int index = 0; index < 5; index++)
    1418             :                 {
    1419             :                     // TODO: If bSuccess is false, set it to false?
    1420           0 :                     if (!bSuccess || !poTransform->Transform(
    1421             :                                          1, &(padfTiepoints[index * 2]),
    1422             :                                          &(padfTiepoints[index * 2 + 1])))
    1423           0 :                         bSuccess = false;
    1424             :                 }
    1425           0 :                 if (bSuccess)
    1426           0 :                     georef_created = true;
    1427             :             }
    1428             :             else
    1429             :             {
    1430           9 :                 georef_created = true;
    1431             :             }
    1432           9 :             delete poLLSRS;
    1433             :         }
    1434           9 :         CPLFree(tempGeoTransform);
    1435             :     }
    1436             : 
    1437           9 :     bool bOK = true;
    1438           9 :     if (georef_created)
    1439             :     {
    1440             :         /* --------------------------------------------------------------------
    1441             :          */
    1442             :         /*      top left */
    1443             :         /* --------------------------------------------------------------------
    1444             :          */
    1445          18 :         bOK &= VSIFPrintfL(fp, "TOP_LEFT_CORNER_LATITUDE = %.10f\n",
    1446           9 :                            padfTiepoints[1]) >= 0;
    1447           9 :         bOK &= VSIFPrintfL(fp, "TOP_LEFT_CORNER_LONGITUDE = %.10f\n",
    1448           9 :                            padfTiepoints[0]) >= 0;
    1449             :         /* --------------------------------------------------------------------
    1450             :          */
    1451             :         /*      top_right */
    1452             :         /* --------------------------------------------------------------------
    1453             :          */
    1454          18 :         bOK &= VSIFPrintfL(fp, "TOP_RIGHT_CORNER_LATITUDE = %.10f\n",
    1455           9 :                            padfTiepoints[3]) >= 0;
    1456          18 :         bOK &= VSIFPrintfL(fp, "TOP_RIGHT_CORNER_LONGITUDE = %.10f\n",
    1457           9 :                            padfTiepoints[2]) >= 0;
    1458             :         /* --------------------------------------------------------------------
    1459             :          */
    1460             :         /*      bottom_left */
    1461             :         /* --------------------------------------------------------------------
    1462             :          */
    1463          18 :         bOK &= VSIFPrintfL(fp, "BOTTOM_LEFT_CORNER_LATITUDE = %.10f\n",
    1464           9 :                            padfTiepoints[5]) >= 0;
    1465          18 :         bOK &= VSIFPrintfL(fp, "BOTTOM_LEFT_CORNER_LONGITUDE = %.10f\n",
    1466           9 :                            padfTiepoints[4]) >= 0;
    1467             :         /* --------------------------------------------------------------------
    1468             :          */
    1469             :         /*      bottom_right */
    1470             :         /* --------------------------------------------------------------------
    1471             :          */
    1472          18 :         bOK &= VSIFPrintfL(fp, "BOTTOM_RIGHT_CORNER_LATITUDE = %.10f\n",
    1473           9 :                            padfTiepoints[7]) >= 0;
    1474          18 :         bOK &= VSIFPrintfL(fp, "BOTTOM_RIGHT_CORNER_LONGITUDE = %.10f\n",
    1475           9 :                            padfTiepoints[6]) >= 0;
    1476             :         /* --------------------------------------------------------------------
    1477             :          */
    1478             :         /*      Center */
    1479             :         /* --------------------------------------------------------------------
    1480             :          */
    1481           9 :         bOK &=
    1482           9 :             VSIFPrintfL(fp, "CENTRE_LATITUDE = %.10f\n", padfTiepoints[9]) >= 0;
    1483          18 :         bOK &= VSIFPrintfL(fp, "CENTRE_LONGITUDE = %.10f\n",
    1484           9 :                            padfTiepoints[8]) >= 0;
    1485             :         /* -------------------------------------------------------------------
    1486             :          */
    1487             :         /*     Ellipsoid/projection */
    1488             :         /* --------------------------------------------------------------------*/
    1489             : 
    1490           9 :         const auto poSrcSRS = poSrcDS->GetSpatialRef();
    1491           9 :         char *spheroid_name = nullptr;
    1492             : 
    1493           9 :         if (poSrcSRS != nullptr)
    1494             :         {
    1495           9 :             if (poSrcSRS->IsProjected() &&
    1496           9 :                 poSrcSRS->GetAttrValue("PROJECTION") != nullptr &&
    1497           0 :                 EQUAL(poSrcSRS->GetAttrValue("PROJECTION"),
    1498             :                       SRS_PT_TRANSVERSE_MERCATOR))
    1499             :             {
    1500           0 :                 bOK &= VSIFPrintfL(fp, "PROJECTION_NAME = UTM\n") >= 0;
    1501           0 :                 OGRErr ogrerrorOl = OGRERR_NONE;
    1502           0 :                 bOK &=
    1503           0 :                     VSIFPrintfL(fp, "PROJECTION_ORIGIN_LONGITUDE = %f\n",
    1504             :                                 poSrcSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,
    1505           0 :                                                       0.0, &ogrerrorOl)) >= 0;
    1506             :             }
    1507           9 :             else if (poSrcSRS->IsGeographic())
    1508             :             {
    1509           9 :                 bOK &= VSIFPrintfL(fp, "PROJECTION_NAME = LL\n") >= 0;
    1510             :             }
    1511             :             else
    1512             :             {
    1513           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1514             :                          "Unrecognized projection- no georeferencing "
    1515             :                          "information transferred.");
    1516           0 :                 bOK &= VSIFPrintfL(fp, "PROJECTION_NAME = LL\n") >= 0;
    1517             :             }
    1518           9 :             OGRErr ogrerrorEq = OGRERR_NONE;
    1519           9 :             const double eq_radius = poSrcSRS->GetSemiMajor(&ogrerrorEq);
    1520           9 :             OGRErr ogrerrorInvf = OGRERR_NONE;
    1521             :             const double inv_flattening =
    1522           9 :                 poSrcSRS->GetInvFlattening(&ogrerrorInvf);
    1523           9 :             if (ogrerrorEq == OGRERR_NONE && ogrerrorInvf == OGRERR_NONE)
    1524             :             {
    1525           9 :                 MFFSpheroidList *mffEllipsoids = new MFFSpheroidList;
    1526             :                 spheroid_name =
    1527           9 :                     mffEllipsoids->GetSpheroidNameByEqRadiusAndInvFlattening(
    1528             :                         eq_radius, inv_flattening);
    1529           9 :                 if (spheroid_name != nullptr)
    1530             :                 {
    1531           0 :                     bOK &= VSIFPrintfL(fp, "SPHEROID_NAME = %s\n",
    1532           0 :                                        spheroid_name) >= 0;
    1533             :                 }
    1534             :                 else
    1535             :                 {
    1536          18 :                     bOK &= VSIFPrintfL(fp,
    1537             :                                        "SPHEROID_NAME = USER_DEFINED\n"
    1538             :                                        "SPHEROID_EQUATORIAL_RADIUS = %.10f\n"
    1539             :                                        "SPHEROID_POLAR_RADIUS = %.10f\n",
    1540             :                                        eq_radius,
    1541             :                                        eq_radius *
    1542           9 :                                            (1 - 1.0 / inv_flattening)) >= 0;
    1543             :                 }
    1544           9 :                 delete mffEllipsoids;
    1545           9 :                 CPLFree(spheroid_name);
    1546             :             }
    1547             :         }
    1548             :     }
    1549             : 
    1550           9 :     CPLFree(padfTiepoints);
    1551           9 :     bOK &= VSIFPrintfL(fp, "END\n") >= 0;
    1552           9 :     if (VSIFCloseL(fp) != 0)
    1553           0 :         bOK = false;
    1554             : 
    1555           9 :     if (!bOK)
    1556             :     {
    1557           0 :         delete poDS;
    1558           0 :         CPLFree(pszBaseFilename);
    1559           0 :         return nullptr;
    1560             :     }
    1561             : 
    1562             :     /* End of georeferencing stuff */
    1563             : 
    1564             :     /* Make sure image data gets flushed */
    1565          28 :     for (int iBand = 0; iBand < poDS->GetRasterCount(); iBand++)
    1566             :     {
    1567             :         RawRasterBand *poDstBand =
    1568          19 :             reinterpret_cast<RawRasterBand *>(poDS->GetRasterBand(iBand + 1));
    1569          19 :         poDstBand->FlushCache(false);
    1570             :     }
    1571             : 
    1572           9 :     if (!pfnProgress(1.0, nullptr, pProgressData))
    1573             :     {
    1574           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1575           0 :         delete poDS;
    1576             : 
    1577             :         GDALDriver *poMFFDriver =
    1578           0 :             static_cast<GDALDriver *>(GDALGetDriverByName("MFF"));
    1579           0 :         poMFFDriver->Delete(pszFilename);
    1580           0 :         CPLFree(pszBaseFilename);
    1581           0 :         return nullptr;
    1582             :     }
    1583             : 
    1584           9 :     poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    1585           9 :     CPLFree(pszBaseFilename);
    1586             : 
    1587           9 :     return poDS;
    1588             : }
    1589             : 
    1590             : /************************************************************************/
    1591             : /*                         GDALRegister_MFF()                           */
    1592             : /************************************************************************/
    1593             : 
    1594        1511 : void GDALRegister_MFF()
    1595             : 
    1596             : {
    1597        1511 :     if (GDALGetDriverByName("MFF") != nullptr)
    1598         295 :         return;
    1599             : 
    1600        1216 :     GDALDriver *poDriver = new GDALDriver();
    1601             : 
    1602        1216 :     poDriver->SetDescription("MFF");
    1603        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1604        1216 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Vexcel MFF Raster");
    1605        1216 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/mff.html");
    1606        1216 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hdr");
    1607        1216 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    1608        1216 :                               "Byte UInt16 Float32 CInt16 CFloat32");
    1609             : 
    1610        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1611             : 
    1612        1216 :     poDriver->pfnOpen = MFFDataset::Open;
    1613        1216 :     poDriver->pfnCreate = MFFDataset::Create;
    1614        1216 :     poDriver->pfnCreateCopy = MFFDataset::CreateCopy;
    1615             : 
    1616        1216 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1617             : }

Generated by: LCOV version 1.14