LCOV - code coverage report
Current view: top level - frmts/raw - hkvdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 610 808 75.5 %
Date: 2024-05-04 12:52:34 Functions: 23 27 85.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GView
       4             :  * Purpose:  Implementation of Atlantis HKV labelled blob support
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.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 <ctype.h>
      31             : 
      32             : #include "atlsci_spheroid.h"
      33             : #include "cpl_string.h"
      34             : #include "gdal_frmts.h"
      35             : #include "ogr_spatialref.h"
      36             : #include "rawdataset.h"
      37             : 
      38             : #include <cmath>
      39             : 
      40             : #include <algorithm>
      41             : 
      42             : /************************************************************************/
      43             : /* ==================================================================== */
      44             : /*                            HKVRasterBand                             */
      45             : /* ==================================================================== */
      46             : /************************************************************************/
      47             : 
      48             : class HKVDataset;
      49             : 
      50             : class HKVRasterBand final : public RawRasterBand
      51             : {
      52             :     friend class HKVDataset;
      53             : 
      54             :   public:
      55             :     HKVRasterBand(HKVDataset *poDS, int nBand, VSILFILE *fpRaw,
      56             :                   unsigned int nImgOffset, int nPixelOffset, int nLineOffset,
      57             :                   GDALDataType eDataType, int bNativeOrder);
      58             : 
      59         188 :     ~HKVRasterBand() override
      60          94 :     {
      61         188 :     }
      62             : 
      63             :     CPLErr SetNoDataValue(double) override;
      64             : };
      65             : 
      66             : /************************************************************************/
      67             : /*                      HKV Spheroids                                   */
      68             : /************************************************************************/
      69             : 
      70             : class HKVSpheroidList : public SpheroidList
      71             : {
      72             :   public:
      73             :     HKVSpheroidList();
      74             : 
      75          44 :     ~HKVSpheroidList()
      76          44 :     {
      77          44 :     }
      78             : };
      79             : 
      80          44 : HKVSpheroidList ::HKVSpheroidList()
      81             : {
      82          44 :     num_spheroids = 58;
      83          44 :     epsilonR = 0.1;
      84          44 :     epsilonI = 0.000001;
      85             : 
      86          44 :     spheroids[0].SetValuesByEqRadiusAndInvFlattening("airy-1830", 6377563.396,
      87             :                                                      299.3249646);
      88          44 :     spheroids[1].SetValuesByEqRadiusAndInvFlattening("modified-airy",
      89             :                                                      6377340.189, 299.3249646);
      90          44 :     spheroids[2].SetValuesByEqRadiusAndInvFlattening("australian-national",
      91             :                                                      6378160, 298.25);
      92          44 :     spheroids[3].SetValuesByEqRadiusAndInvFlattening("bessel-1841-namibia",
      93             :                                                      6377483.865, 299.1528128);
      94          44 :     spheroids[4].SetValuesByEqRadiusAndInvFlattening("bessel-1841", 6377397.155,
      95             :                                                      299.1528128);
      96          44 :     spheroids[5].SetValuesByEqRadiusAndInvFlattening("clarke-1858", 6378294.0,
      97             :                                                      294.297);
      98          44 :     spheroids[6].SetValuesByEqRadiusAndInvFlattening("clarke-1866", 6378206.4,
      99             :                                                      294.9786982);
     100          44 :     spheroids[7].SetValuesByEqRadiusAndInvFlattening("clarke-1880", 6378249.145,
     101             :                                                      293.465);
     102          44 :     spheroids[8].SetValuesByEqRadiusAndInvFlattening("everest-india-1830",
     103             :                                                      6377276.345, 300.8017);
     104          44 :     spheroids[9].SetValuesByEqRadiusAndInvFlattening("everest-sabah-sarawak",
     105             :                                                      6377298.556, 300.8017);
     106          44 :     spheroids[10].SetValuesByEqRadiusAndInvFlattening("everest-india-1956",
     107             :                                                       6377301.243, 300.8017);
     108          44 :     spheroids[11].SetValuesByEqRadiusAndInvFlattening("everest-malaysia-1969",
     109             :                                                       6377295.664, 300.8017);
     110          44 :     spheroids[12].SetValuesByEqRadiusAndInvFlattening("everest-malay-sing",
     111             :                                                       6377304.063, 300.8017);
     112          44 :     spheroids[13].SetValuesByEqRadiusAndInvFlattening("everest-pakistan",
     113             :                                                       6377309.613, 300.8017);
     114          44 :     spheroids[14].SetValuesByEqRadiusAndInvFlattening("modified-fisher-1960",
     115             :                                                       6378155, 298.3);
     116          44 :     spheroids[15].SetValuesByEqRadiusAndInvFlattening("helmert-1906", 6378200,
     117             :                                                       298.3);
     118          44 :     spheroids[16].SetValuesByEqRadiusAndInvFlattening("hough-1960", 6378270,
     119             :                                                       297);
     120          44 :     spheroids[17].SetValuesByEqRadiusAndInvFlattening("hughes", 6378273.0,
     121             :                                                       298.279);
     122          44 :     spheroids[18].SetValuesByEqRadiusAndInvFlattening("indonesian-1974",
     123             :                                                       6378160, 298.247);
     124          44 :     spheroids[19].SetValuesByEqRadiusAndInvFlattening("international-1924",
     125             :                                                       6378388, 297);
     126          44 :     spheroids[20].SetValuesByEqRadiusAndInvFlattening("iugc-67", 6378160.0,
     127             :                                                       298.254);
     128          44 :     spheroids[21].SetValuesByEqRadiusAndInvFlattening("iugc-75", 6378140.0,
     129             :                                                       298.25298);
     130          44 :     spheroids[22].SetValuesByEqRadiusAndInvFlattening("krassovsky-1940",
     131             :                                                       6378245, 298.3);
     132          44 :     spheroids[23].SetValuesByEqRadiusAndInvFlattening("kaula", 6378165.0,
     133             :                                                       292.308);
     134          44 :     spheroids[24].SetValuesByEqRadiusAndInvFlattening("grs-80", 6378137,
     135             :                                                       298.257222101);
     136          44 :     spheroids[25].SetValuesByEqRadiusAndInvFlattening("south-american-1969",
     137             :                                                       6378160, 298.25);
     138          44 :     spheroids[26].SetValuesByEqRadiusAndInvFlattening("wgs-72", 6378135,
     139             :                                                       298.26);
     140          44 :     spheroids[27].SetValuesByEqRadiusAndInvFlattening("wgs-84", 6378137,
     141             :                                                       298.257223563);
     142          44 :     spheroids[28].SetValuesByEqRadiusAndInvFlattening("ev-wgs-84", 6378137.0,
     143             :                                                       298.252841);
     144          44 :     spheroids[29].SetValuesByEqRadiusAndInvFlattening("ev-bessel", 6377397.0,
     145             :                                                       299.1976073);
     146             : 
     147          44 :     spheroids[30].SetValuesByEqRadiusAndInvFlattening("airy_1830", 6377563.396,
     148             :                                                       299.3249646);
     149          44 :     spheroids[31].SetValuesByEqRadiusAndInvFlattening("modified_airy",
     150             :                                                       6377340.189, 299.3249646);
     151          44 :     spheroids[32].SetValuesByEqRadiusAndInvFlattening("australian_national",
     152             :                                                       6378160, 298.25);
     153          44 :     spheroids[33].SetValuesByEqRadiusAndInvFlattening("bessel_1841_namibia",
     154             :                                                       6377483.865, 299.1528128);
     155          44 :     spheroids[34].SetValuesByEqRadiusAndInvFlattening("bessel_1841",
     156             :                                                       6377397.155, 299.1528128);
     157          44 :     spheroids[35].SetValuesByEqRadiusAndInvFlattening("clarke_1858", 6378294.0,
     158             :                                                       294.297);
     159          44 :     spheroids[36].SetValuesByEqRadiusAndInvFlattening("clarke_1866", 6378206.4,
     160             :                                                       294.9786982);
     161          44 :     spheroids[37].SetValuesByEqRadiusAndInvFlattening("clarke_1880",
     162             :                                                       6378249.145, 293.465);
     163          44 :     spheroids[38].SetValuesByEqRadiusAndInvFlattening("everest_india_1830",
     164             :                                                       6377276.345, 300.8017);
     165          44 :     spheroids[39].SetValuesByEqRadiusAndInvFlattening("everest_sabah_sarawak",
     166             :                                                       6377298.556, 300.8017);
     167          44 :     spheroids[40].SetValuesByEqRadiusAndInvFlattening("everest_india_1956",
     168             :                                                       6377301.243, 300.8017);
     169          44 :     spheroids[41].SetValuesByEqRadiusAndInvFlattening("everest_malaysia_1969",
     170             :                                                       6377295.664, 300.8017);
     171          44 :     spheroids[42].SetValuesByEqRadiusAndInvFlattening("everest_malay_sing",
     172             :                                                       6377304.063, 300.8017);
     173          44 :     spheroids[43].SetValuesByEqRadiusAndInvFlattening("everest_pakistan",
     174             :                                                       6377309.613, 300.8017);
     175          44 :     spheroids[44].SetValuesByEqRadiusAndInvFlattening("modified_fisher_1960",
     176             :                                                       6378155, 298.3);
     177          44 :     spheroids[45].SetValuesByEqRadiusAndInvFlattening("helmert_1906", 6378200,
     178             :                                                       298.3);
     179          44 :     spheroids[46].SetValuesByEqRadiusAndInvFlattening("hough_1960", 6378270,
     180             :                                                       297);
     181          44 :     spheroids[47].SetValuesByEqRadiusAndInvFlattening("indonesian_1974",
     182             :                                                       6378160, 298.247);
     183          44 :     spheroids[48].SetValuesByEqRadiusAndInvFlattening("international_1924",
     184             :                                                       6378388, 297);
     185          44 :     spheroids[49].SetValuesByEqRadiusAndInvFlattening("iugc_67", 6378160.0,
     186             :                                                       298.254);
     187          44 :     spheroids[50].SetValuesByEqRadiusAndInvFlattening("iugc_75", 6378140.0,
     188             :                                                       298.25298);
     189          44 :     spheroids[51].SetValuesByEqRadiusAndInvFlattening("krassovsky_1940",
     190             :                                                       6378245, 298.3);
     191          44 :     spheroids[52].SetValuesByEqRadiusAndInvFlattening("grs_80", 6378137,
     192             :                                                       298.257222101);
     193          44 :     spheroids[53].SetValuesByEqRadiusAndInvFlattening("south_american_1969",
     194             :                                                       6378160, 298.25);
     195          44 :     spheroids[54].SetValuesByEqRadiusAndInvFlattening("wgs_72", 6378135,
     196             :                                                       298.26);
     197          44 :     spheroids[55].SetValuesByEqRadiusAndInvFlattening("wgs_84", 6378137,
     198             :                                                       298.257223563);
     199          44 :     spheroids[56].SetValuesByEqRadiusAndInvFlattening("ev_wgs_84", 6378137.0,
     200             :                                                       298.252841);
     201          44 :     spheroids[57].SetValuesByEqRadiusAndInvFlattening("ev_bessel", 6377397.0,
     202             :                                                       299.1976073);
     203          44 : }
     204             : 
     205             : CPLErr SaveHKVAttribFile(const char *pszFilenameIn, int nXSize, int nYSize,
     206             :                          int nBands, GDALDataType eType, int bNoDataSet,
     207             :                          double dfNoDataValue);
     208             : 
     209             : /************************************************************************/
     210             : /* ==================================================================== */
     211             : /*                              HKVDataset                              */
     212             : /* ==================================================================== */
     213             : /************************************************************************/
     214             : 
     215             : class HKVDataset final : public RawDataset
     216             : {
     217             :     friend class HKVRasterBand;
     218             : 
     219             :     char *pszPath;
     220             :     VSILFILE *fpBlob;
     221             : 
     222             :     int nGCPCount;
     223             :     GDAL_GCP *pasGCPList;
     224             : 
     225             :     void ProcessGeoref(const char *);
     226             :     void ProcessGeorefGCP(char **, const char *, double, double);
     227             : 
     228          44 :     void SetVersion(float version_number)
     229             :     {
     230             :         // Update stored info.
     231          44 :         MFF2version = version_number;
     232          44 :     }
     233             : 
     234             :     float MFF2version;
     235             : 
     236             :     GDALDataType eRasterType;
     237             : 
     238             :     void SetNoDataValue(double);
     239             : 
     240             :     OGRSpatialReference m_oSRS{};
     241             :     OGRSpatialReference m_oGCPSRS{};
     242             :     double adfGeoTransform[6];
     243             : 
     244             :     char **papszAttrib;
     245             : 
     246             :     bool bGeorefChanged;
     247             :     char **papszGeoref;
     248             : 
     249             :     // NOTE: The MFF2 format goes against GDAL's API in that nodata values are
     250             :     // set per-dataset rather than per-band.  To compromise, for writing out,
     251             :     // the dataset's nodata value will be set to the last value set on any of
     252             :     // the raster bands.
     253             : 
     254             :     bool bNoDataSet;
     255             :     bool bNoDataChanged;
     256             :     double dfNoDataValue;
     257             : 
     258             :     CPL_DISALLOW_COPY_ASSIGN(HKVDataset)
     259             : 
     260             :     CPLErr Close() override;
     261             : 
     262             :   public:
     263             :     HKVDataset();
     264             :     ~HKVDataset() override;
     265             : 
     266           2 :     int GetGCPCount() override /* const */
     267             :     {
     268           2 :         return nGCPCount;
     269             :     }
     270             : 
     271           0 :     const OGRSpatialReference *GetGCPSpatialRef() const override
     272             :     {
     273           0 :         return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
     274             :     }
     275             : 
     276             :     const GDAL_GCP *GetGCPs() override;
     277             : 
     278          15 :     const OGRSpatialReference *GetSpatialRef() const override
     279             :     {
     280          15 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     281             :     }
     282             : 
     283             :     CPLErr GetGeoTransform(double *) override;
     284             : 
     285             :     CPLErr SetGeoTransform(double *) override;
     286             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     287             : 
     288             :     static GDALDataset *Open(GDALOpenInfo *);
     289             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     290             :                                int nBands, GDALDataType eType,
     291             :                                char **papszParamList);
     292             :     static GDALDataset *CreateCopy(const char *pszFilename,
     293             :                                    GDALDataset *poSrcDS, int bStrict,
     294             :                                    char **papszOptions,
     295             :                                    GDALProgressFunc pfnProgress,
     296             :                                    void *pProgressData);
     297             : 
     298             :     static CPLErr Delete(const char *pszName);
     299             : };
     300             : 
     301             : /************************************************************************/
     302             : /* ==================================================================== */
     303             : /*                            HKVRasterBand                             */
     304             : /* ==================================================================== */
     305             : /************************************************************************/
     306             : 
     307             : /************************************************************************/
     308             : /*                           HKVRasterBand()                            */
     309             : /************************************************************************/
     310             : 
     311          94 : HKVRasterBand::HKVRasterBand(HKVDataset *poDSIn, int nBandIn, VSILFILE *fpRawIn,
     312             :                              unsigned int nImgOffsetIn, int nPixelOffsetIn,
     313             :                              int nLineOffsetIn, GDALDataType eDataTypeIn,
     314          94 :                              int bNativeOrderIn)
     315             :     : RawRasterBand(GDALDataset::FromHandle(poDSIn), nBandIn, fpRawIn,
     316             :                     nImgOffsetIn, nPixelOffsetIn, nLineOffsetIn, eDataTypeIn,
     317          94 :                     bNativeOrderIn, RawRasterBand::OwnFP::NO)
     318             : 
     319             : {
     320          94 :     poDS = poDSIn;
     321          94 :     nBand = nBandIn;
     322             : 
     323          94 :     nBlockXSize = poDS->GetRasterXSize();
     324          94 :     nBlockYSize = 1;
     325          94 : }
     326             : 
     327             : /************************************************************************/
     328             : /*                           SetNoDataValue()                           */
     329             : /************************************************************************/
     330             : 
     331           0 : CPLErr HKVRasterBand::SetNoDataValue(double dfNewValue)
     332             : 
     333             : {
     334           0 :     HKVDataset *poHKVDS = reinterpret_cast<HKVDataset *>(poDS);
     335           0 :     RawRasterBand::SetNoDataValue(dfNewValue);
     336           0 :     poHKVDS->SetNoDataValue(dfNewValue);
     337             : 
     338           0 :     return CE_None;
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /* ==================================================================== */
     343             : /*                              HKVDataset                              */
     344             : /* ==================================================================== */
     345             : /************************************************************************/
     346             : 
     347             : /************************************************************************/
     348             : /*                            HKVDataset()                             */
     349             : /************************************************************************/
     350             : 
     351          44 : HKVDataset::HKVDataset()
     352             :     : pszPath(nullptr), fpBlob(nullptr), nGCPCount(0), pasGCPList(nullptr),
     353             :       // Initialize datasets to new version; change if necessary.
     354             :       MFF2version(1.1f), eRasterType(GDT_Unknown), papszAttrib(nullptr),
     355             :       bGeorefChanged(false), papszGeoref(nullptr), bNoDataSet(false),
     356          44 :       bNoDataChanged(false), dfNoDataValue(0.0)
     357             : {
     358          44 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     359          44 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     360          44 :     adfGeoTransform[0] = 0.0;
     361          44 :     adfGeoTransform[1] = 1.0;
     362          44 :     adfGeoTransform[2] = 0.0;
     363          44 :     adfGeoTransform[3] = 0.0;
     364          44 :     adfGeoTransform[4] = 0.0;
     365          44 :     adfGeoTransform[5] = 1.0;
     366          44 : }
     367             : 
     368             : /************************************************************************/
     369             : /*                            ~HKVDataset()                            */
     370             : /************************************************************************/
     371             : 
     372          88 : HKVDataset::~HKVDataset()
     373             : 
     374             : {
     375          44 :     HKVDataset::Close();
     376          88 : }
     377             : 
     378             : /************************************************************************/
     379             : /*                              Close()                                 */
     380             : /************************************************************************/
     381             : 
     382          88 : CPLErr HKVDataset::Close()
     383             : {
     384          88 :     CPLErr eErr = CE_None;
     385          88 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     386             :     {
     387          44 :         if (HKVDataset::FlushCache(true) != CE_None)
     388           0 :             eErr = CE_Failure;
     389             : 
     390          44 :         if (bGeorefChanged)
     391             :         {
     392             :             const char *pszFilename =
     393          26 :                 CPLFormFilename(pszPath, "georef", nullptr);
     394          26 :             CSLSave(papszGeoref, pszFilename);
     395             :         }
     396             : 
     397          44 :         if (bNoDataChanged)
     398             :         {
     399           0 :             SaveHKVAttribFile(pszPath, nRasterXSize, nRasterYSize, nBands,
     400           0 :                               eRasterType, bNoDataSet, dfNoDataValue);
     401             :         }
     402             : 
     403          44 :         if (fpBlob)
     404             :         {
     405          44 :             if (VSIFCloseL(fpBlob) != 0)
     406             :             {
     407           0 :                 eErr = CE_Failure;
     408           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     409             :             }
     410             :         }
     411             : 
     412          44 :         if (nGCPCount > 0)
     413             :         {
     414          14 :             GDALDeinitGCPs(nGCPCount, pasGCPList);
     415          14 :             CPLFree(pasGCPList);
     416             :         }
     417             : 
     418          44 :         CPLFree(pszPath);
     419          44 :         CSLDestroy(papszGeoref);
     420          44 :         CSLDestroy(papszAttrib);
     421             : 
     422          44 :         if (GDALPamDataset::Close() != CE_None)
     423           0 :             eErr = CE_Failure;
     424             :     }
     425          88 :     return eErr;
     426             : }
     427             : 
     428             : /************************************************************************/
     429             : /*                          SetNoDataValue()                            */
     430             : /************************************************************************/
     431             : 
     432           0 : void HKVDataset::SetNoDataValue(double dfNewValue)
     433             : 
     434             : {
     435           0 :     bNoDataSet = true;
     436           0 :     bNoDataChanged = true;
     437           0 :     dfNoDataValue = dfNewValue;
     438           0 : }
     439             : 
     440             : /************************************************************************/
     441             : /*                          SaveHKVAttribFile()                            */
     442             : /************************************************************************/
     443             : 
     444          26 : CPLErr SaveHKVAttribFile(const char *pszFilenameIn, int nXSize, int nYSize,
     445             :                          int nBands, GDALDataType eType, int bNoDataSet,
     446             :                          double dfNoDataValue)
     447             : 
     448             : {
     449          26 :     const char *pszFilename = CPLFormFilename(pszFilenameIn, "attrib", nullptr);
     450             : 
     451          26 :     FILE *fp = VSIFOpen(pszFilename, "wt");
     452          26 :     if (fp == nullptr)
     453             :     {
     454           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Couldn't create %s.",
     455             :                  pszFilename);
     456           0 :         return CE_Failure;
     457             :     }
     458             : 
     459          26 :     fprintf(fp, "channel.enumeration = %d\n", nBands);
     460          26 :     fprintf(fp, "channel.interleave = { *pixel tile sequential }\n");
     461          26 :     fprintf(fp, "extent.cols = %d\n", nXSize);
     462          26 :     fprintf(fp, "extent.rows = %d\n", nYSize);
     463             : 
     464          26 :     switch (eType)
     465             :     {
     466          11 :         case GDT_Byte:
     467          11 :             fprintf(fp, "pixel.encoding = "
     468             :                         "{ *unsigned twos-complement ieee-754 }\n");
     469          11 :             break;
     470             : 
     471           3 :         case GDT_UInt16:
     472           3 :             fprintf(fp, "pixel.encoding = "
     473             :                         "{ *unsigned twos-complement ieee-754 }\n");
     474           3 :             break;
     475             : 
     476           6 :         case GDT_CInt16:
     477             :         case GDT_Int16:
     478           6 :             fprintf(fp, "pixel.encoding = "
     479             :                         "{ unsigned *twos-complement ieee-754 }\n");
     480           6 :             break;
     481             : 
     482           6 :         case GDT_CFloat32:
     483             :         case GDT_Float32:
     484           6 :             fprintf(fp, "pixel.encoding = "
     485             :                         "{ unsigned twos-complement *ieee-754 }\n");
     486           6 :             break;
     487             : 
     488           0 :         default:
     489           0 :             CPLAssert(false);
     490             :     }
     491             : 
     492          26 :     fprintf(fp, "pixel.size = %d\n", GDALGetDataTypeSizeBits(eType));
     493          26 :     if (GDALDataTypeIsComplex(eType))
     494           6 :         fprintf(fp, "pixel.field = { real *complex }\n");
     495             :     else
     496          20 :         fprintf(fp, "pixel.field = { *real complex }\n");
     497             : 
     498             : #ifdef CPL_MSB
     499             :     fprintf(fp, "pixel.order = { lsbf *msbf }\n");
     500             : #else
     501          26 :     fprintf(fp, "pixel.order = { *lsbf msbf }\n");
     502             : #endif
     503             : 
     504          26 :     if (bNoDataSet)
     505           0 :         fprintf(fp, "pixel.no_data = %s\n", CPLSPrintf("%f", dfNoDataValue));
     506             : 
     507             :     // Version information- only create the new style.
     508          26 :     fprintf(fp, "version = 1.1");
     509             : 
     510          26 :     if (VSIFClose(fp) != 0)
     511           0 :         return CE_Failure;
     512          26 :     return CE_None;
     513             : }
     514             : 
     515             : /************************************************************************/
     516             : /*                          GetGeoTransform()                           */
     517             : /************************************************************************/
     518             : 
     519          29 : CPLErr HKVDataset::GetGeoTransform(double *padfTransform)
     520             : 
     521             : {
     522          29 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     523          29 :     return CE_None;
     524             : }
     525             : 
     526             : /************************************************************************/
     527             : /*                          SetGeoTransform()                           */
     528             : /************************************************************************/
     529             : 
     530          26 : CPLErr HKVDataset::SetGeoTransform(double *padfTransform)
     531             : 
     532             : {
     533             :     // NOTE:  Geotransform coordinates must match the current projection
     534             :     // of the dataset being changed (not the geotransform source).
     535             :     // i.e. be in lat/longs for LL projected; UTM for UTM projected.
     536             :     // SET PROJECTION BEFORE SETTING GEOTRANSFORM TO AVOID SYNCHRONIZATION
     537             :     // PROBLEMS.
     538             : 
     539             :     // Update the geotransform itself.
     540          26 :     memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
     541             : 
     542             :     // Clear previous gcps.
     543          26 :     if (nGCPCount > 0)
     544             :     {
     545           0 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
     546           0 :         CPLFree(pasGCPList);
     547             :     }
     548          26 :     nGCPCount = 0;
     549          26 :     pasGCPList = nullptr;
     550             : 
     551             :     // Return if the identity transform is set.
     552          26 :     if (adfGeoTransform[0] == 0.0 && adfGeoTransform[1] == 1.0 &&
     553           0 :         adfGeoTransform[2] == 0.0 && adfGeoTransform[3] == 0.0 &&
     554           0 :         adfGeoTransform[4] == 0.0 && adfGeoTransform[5] == 1.0)
     555           0 :         return CE_None;
     556             : 
     557             :     // Update georef text info for saving later, and update GCPs to match
     558             :     // geotransform.
     559             : 
     560          26 :     OGRCoordinateTransformation *poTransform = nullptr;
     561          26 :     bool bSuccess = true;
     562             : 
     563             :     // Projection parameter checking will have been done in SetProjection.
     564          37 :     if ((CSLFetchNameValue(papszGeoref, "projection.name") != nullptr) &&
     565          11 :         (EQUAL(CSLFetchNameValue(papszGeoref, "projection.name"), "UTM")))
     566             :     {
     567           1 :         auto poLLSRS = m_oSRS.CloneGeogCS();
     568           1 :         if (poLLSRS)
     569             :         {
     570           1 :             poLLSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     571           1 :             poTransform = OGRCreateCoordinateTransformation(&m_oSRS, poLLSRS);
     572           1 :             delete poLLSRS;
     573           1 :             if (poTransform == nullptr)
     574             :             {
     575           0 :                 bSuccess = false;
     576           0 :                 CPLErrorReset();
     577             :             }
     578             :         }
     579             :         else
     580             :         {
     581           0 :             bSuccess = false;
     582             :         }
     583             :     }
     584          25 :     else if (((CSLFetchNameValue(papszGeoref, "projection.name") != nullptr) &&
     585          10 :               (!EQUAL(CSLFetchNameValue(papszGeoref, "projection.name"),
     586          50 :                       "LL"))) ||
     587          25 :              (CSLFetchNameValue(papszGeoref, "projection.name") == nullptr))
     588             :     {
     589          15 :         return CE_Failure;
     590             :     }
     591             : 
     592          11 :     nGCPCount = 0;
     593          11 :     pasGCPList = static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 5));
     594             : 
     595             :     /* -------------------------------------------------------------------- */
     596             :     /*      top left                                                        */
     597             :     /* -------------------------------------------------------------------- */
     598          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     599          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     600          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("top_left");
     601             : 
     602          11 :     double temp_lat = 0.0;
     603          11 :     double temp_long = 0.0;
     604          11 :     if (MFF2version > 1.0)
     605             :     {
     606          11 :         temp_lat = padfTransform[3];
     607          11 :         temp_long = padfTransform[0];
     608          11 :         pasGCPList[nGCPCount].dfGCPPixel = 0.0;
     609          11 :         pasGCPList[nGCPCount].dfGCPLine = 0.0;
     610             :     }
     611             :     else
     612             :     {
     613           0 :         temp_lat =
     614           0 :             padfTransform[3] + 0.5 * padfTransform[4] + 0.5 * padfTransform[5];
     615           0 :         temp_long =
     616           0 :             padfTransform[0] + 0.5 * padfTransform[1] + 0.5 * padfTransform[2];
     617           0 :         pasGCPList[nGCPCount].dfGCPPixel = 0.5;
     618           0 :         pasGCPList[nGCPCount].dfGCPLine = 0.5;
     619             :     }
     620          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     621          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     622          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     623          11 :     nGCPCount++;
     624             : 
     625          11 :     if (poTransform != nullptr)
     626             :     {
     627           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     628           0 :             bSuccess = false;
     629             :     }
     630             : 
     631          11 :     if (bSuccess)
     632             :     {
     633          11 :         char szValue[128] = {'\0'};
     634          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     635          11 :         papszGeoref =
     636          11 :             CSLSetNameValue(papszGeoref, "top_left.latitude", szValue);
     637             : 
     638          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     639          11 :         papszGeoref =
     640          11 :             CSLSetNameValue(papszGeoref, "top_left.longitude", szValue);
     641             :     }
     642             : 
     643             :     /* -------------------------------------------------------------------- */
     644             :     /*      top_right                                                       */
     645             :     /* -------------------------------------------------------------------- */
     646          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     647          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     648          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("top_right");
     649             : 
     650          11 :     if (MFF2version > 1.0)
     651             :     {
     652          11 :         temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4];
     653          11 :         temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1];
     654          11 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize();
     655          11 :         pasGCPList[nGCPCount].dfGCPLine = 0.0;
     656             :     }
     657             :     else
     658             :     {
     659           0 :         temp_lat = padfTransform[3] +
     660           0 :                    (GetRasterXSize() - 0.5) * padfTransform[4] +
     661           0 :                    0.5 * padfTransform[5];
     662           0 :         temp_long = padfTransform[0] +
     663           0 :                     (GetRasterXSize() - 0.5) * padfTransform[1] +
     664           0 :                     0.5 * padfTransform[2];
     665           0 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() - 0.5;
     666           0 :         pasGCPList[nGCPCount].dfGCPLine = 0.5;
     667             :     }
     668          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     669          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     670          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     671          11 :     nGCPCount++;
     672             : 
     673          11 :     if (poTransform != nullptr)
     674             :     {
     675           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     676           0 :             bSuccess = false;
     677             :     }
     678             : 
     679          11 :     if (bSuccess)
     680             :     {
     681          11 :         char szValue[128] = {'\0'};
     682          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     683          11 :         papszGeoref =
     684          11 :             CSLSetNameValue(papszGeoref, "top_right.latitude", szValue);
     685             : 
     686          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     687          11 :         papszGeoref =
     688          11 :             CSLSetNameValue(papszGeoref, "top_right.longitude", szValue);
     689             :     }
     690             : 
     691             :     /* -------------------------------------------------------------------- */
     692             :     /*      bottom_left                                                     */
     693             :     /* -------------------------------------------------------------------- */
     694          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     695          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     696          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("bottom_left");
     697             : 
     698          11 :     if (MFF2version > 1.0)
     699             :     {
     700          11 :         temp_lat = padfTransform[3] + GetRasterYSize() * padfTransform[5];
     701          11 :         temp_long = padfTransform[0] + GetRasterYSize() * padfTransform[2];
     702          11 :         pasGCPList[nGCPCount].dfGCPPixel = 0.0;
     703          11 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize();
     704             :     }
     705             :     else
     706             :     {
     707           0 :         temp_lat = padfTransform[3] + 0.5 * padfTransform[4] +
     708           0 :                    (GetRasterYSize() - 0.5) * padfTransform[5];
     709           0 :         temp_long = padfTransform[0] + 0.5 * padfTransform[1] +
     710           0 :                     (GetRasterYSize() - 0.5) * padfTransform[2];
     711           0 :         pasGCPList[nGCPCount].dfGCPPixel = 0.5;
     712           0 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize() - 0.5;
     713             :     }
     714          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     715          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     716          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     717          11 :     nGCPCount++;
     718             : 
     719          11 :     if (poTransform != nullptr)
     720             :     {
     721           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     722           0 :             bSuccess = false;
     723             :     }
     724             : 
     725          11 :     if (bSuccess)
     726             :     {
     727          11 :         char szValue[128] = {'\0'};
     728          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     729          11 :         papszGeoref =
     730          11 :             CSLSetNameValue(papszGeoref, "bottom_left.latitude", szValue);
     731             : 
     732          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     733          11 :         papszGeoref =
     734          11 :             CSLSetNameValue(papszGeoref, "bottom_left.longitude", szValue);
     735             :     }
     736             : 
     737             :     /* -------------------------------------------------------------------- */
     738             :     /*      bottom_right                                                    */
     739             :     /* -------------------------------------------------------------------- */
     740          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     741          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     742          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("bottom_right");
     743             : 
     744          11 :     if (MFF2version > 1.0)
     745             :     {
     746          11 :         temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4] +
     747          11 :                    GetRasterYSize() * padfTransform[5];
     748          11 :         temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1] +
     749          11 :                     GetRasterYSize() * padfTransform[2];
     750          11 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize();
     751          11 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize();
     752             :     }
     753             :     else
     754             :     {
     755           0 :         temp_lat = padfTransform[3] +
     756           0 :                    (GetRasterXSize() - 0.5) * padfTransform[4] +
     757           0 :                    (GetRasterYSize() - 0.5) * padfTransform[5];
     758           0 :         temp_long = padfTransform[0] +
     759           0 :                     (GetRasterXSize() - 0.5) * padfTransform[1] +
     760           0 :                     (GetRasterYSize() - 0.5) * padfTransform[2];
     761           0 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() - 0.5;
     762           0 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize() - 0.5;
     763             :     }
     764          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     765          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     766          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     767          11 :     nGCPCount++;
     768             : 
     769          11 :     if (poTransform != nullptr)
     770             :     {
     771           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     772           0 :             bSuccess = false;
     773             :     }
     774             : 
     775          11 :     if (bSuccess)
     776             :     {
     777          11 :         char szValue[128] = {'\0'};
     778          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     779          11 :         papszGeoref =
     780          11 :             CSLSetNameValue(papszGeoref, "bottom_right.latitude", szValue);
     781             : 
     782          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     783          11 :         papszGeoref =
     784          11 :             CSLSetNameValue(papszGeoref, "bottom_right.longitude", szValue);
     785             :     }
     786             : 
     787             :     /* -------------------------------------------------------------------- */
     788             :     /*      Center                                                          */
     789             :     /* -------------------------------------------------------------------- */
     790          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     791          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     792          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("centre");
     793             : 
     794          11 :     temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4] * 0.5 +
     795          11 :                GetRasterYSize() * padfTransform[5] * 0.5;
     796          11 :     temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1] * 0.5 +
     797          11 :                 GetRasterYSize() * padfTransform[2] * 0.5;
     798          11 :     pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() / 2.0;
     799          11 :     pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize() / 2.0;
     800             : 
     801          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     802          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     803          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     804          11 :     nGCPCount++;
     805             : 
     806          11 :     if (poTransform != nullptr)
     807             :     {
     808           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     809           0 :             bSuccess = false;
     810             :     }
     811             : 
     812          11 :     if (bSuccess)
     813             :     {
     814          11 :         char szValue[128] = {'\0'};
     815          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     816          11 :         papszGeoref = CSLSetNameValue(papszGeoref, "centre.latitude", szValue);
     817             : 
     818          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     819          11 :         papszGeoref = CSLSetNameValue(papszGeoref, "centre.longitude", szValue);
     820             :     }
     821             : 
     822          11 :     if (!bSuccess)
     823             :     {
     824           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     825             :                  "Error setting header info in SetGeoTransform. "
     826             :                  "Changes may not be saved properly.");
     827             :     }
     828             : 
     829          11 :     if (poTransform != nullptr)
     830           1 :         delete poTransform;
     831             : 
     832          11 :     bGeorefChanged = true;
     833             : 
     834          11 :     return CE_None;
     835             : }
     836             : 
     837             : /************************************************************************/
     838             : /*                           SetSpatialRef()                            */
     839             : /*                                                                      */
     840             : /*      We provide very limited support for setting the projection.     */
     841             : /************************************************************************/
     842             : 
     843          26 : CPLErr HKVDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     844             : 
     845             : {
     846             :     // Update a georef file.
     847          26 :     if (poSRS == nullptr)
     848             :     {
     849           0 :         m_oSRS.Clear();
     850           0 :         return CE_None;
     851             :     }
     852          26 :     m_oSRS = *poSRS;
     853             : 
     854          27 :     if ((m_oSRS.GetAttrValue("PROJECTION") != nullptr) &&
     855           1 :         (EQUAL(m_oSRS.GetAttrValue("PROJECTION"), SRS_PT_TRANSVERSE_MERCATOR)))
     856             :     {
     857           1 :         papszGeoref = CSLSetNameValue(papszGeoref, "projection.name", "utm");
     858           1 :         OGRErr ogrerrorOl = OGRERR_NONE;
     859           1 :         papszGeoref = CSLSetNameValue(
     860             :             papszGeoref, "projection.origin_longitude",
     861             :             CPLSPrintf("%f", m_oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0,
     862             :                                                 &ogrerrorOl)));
     863             :     }
     864          50 :     else if (m_oSRS.GetAttrValue("PROJECTION") == nullptr &&
     865          25 :              m_oSRS.IsGeographic())
     866             :     {
     867          25 :         papszGeoref = CSLSetNameValue(papszGeoref, "projection.name", "LL");
     868             :     }
     869             :     else
     870             :     {
     871           0 :         CPLError(CE_Warning, CPLE_AppDefined, "Unrecognized projection.");
     872           0 :         return CE_Failure;
     873             :     }
     874             : 
     875          26 :     OGRErr ogrerrorEq = OGRERR_NONE;
     876          26 :     const double eq_radius = m_oSRS.GetSemiMajor(&ogrerrorEq);
     877             : 
     878          26 :     OGRErr ogrerrorInvf = OGRERR_NONE;
     879          26 :     const double inv_flattening = m_oSRS.GetInvFlattening(&ogrerrorInvf);
     880             : 
     881          26 :     if ((ogrerrorEq == OGRERR_NONE) && (ogrerrorInvf == OGRERR_NONE))
     882             :     {
     883          26 :         HKVSpheroidList *hkvEllipsoids = new HKVSpheroidList;
     884             :         char *spheroid_name =
     885          26 :             hkvEllipsoids->GetSpheroidNameByEqRadiusAndInvFlattening(
     886             :                 eq_radius, inv_flattening);
     887          26 :         if (spheroid_name != nullptr)
     888             :         {
     889          26 :             papszGeoref =
     890          26 :                 CSLSetNameValue(papszGeoref, "spheroid.name", spheroid_name);
     891             :         }
     892          26 :         CPLFree(spheroid_name);
     893          26 :         delete hkvEllipsoids;
     894             :     }
     895             :     else
     896             :     {
     897             :         // Default to previous behavior if spheroid not found by radius and
     898             :         // inverse flattening.
     899           0 :         char *pszProjection = nullptr;
     900           0 :         m_oSRS.exportToWkt(&pszProjection);
     901           0 :         if (pszProjection && strstr(pszProjection, "Bessel") != nullptr)
     902             :         {
     903           0 :             papszGeoref =
     904           0 :                 CSLSetNameValue(papszGeoref, "spheroid.name", "ev-bessel");
     905             :         }
     906             :         else
     907             :         {
     908           0 :             papszGeoref =
     909           0 :                 CSLSetNameValue(papszGeoref, "spheroid.name", "ev-wgs-84");
     910             :         }
     911           0 :         CPLFree(pszProjection);
     912             :     }
     913          26 :     bGeorefChanged = true;
     914          26 :     return CE_None;
     915             : }
     916             : 
     917             : /************************************************************************/
     918             : /*                               GetGCP()                               */
     919             : /************************************************************************/
     920             : 
     921           0 : const GDAL_GCP *HKVDataset::GetGCPs()
     922             : 
     923             : {
     924           0 :     return pasGCPList;
     925             : }
     926             : 
     927             : /************************************************************************/
     928             : /*                          ProcessGeorefGCP()                          */
     929             : /************************************************************************/
     930             : 
     931          90 : void HKVDataset::ProcessGeorefGCP(char **papszGeorefIn, const char *pszBase,
     932             :                                   double dfRasterX, double dfRasterY)
     933             : 
     934             : {
     935             :     /* -------------------------------------------------------------------- */
     936             :     /*      Fetch the GCP from the string list.                             */
     937             :     /* -------------------------------------------------------------------- */
     938          90 :     char szFieldName[128] = {'\0'};
     939          90 :     snprintf(szFieldName, sizeof(szFieldName), "%s.latitude", pszBase);
     940          90 :     double dfLat = 0.0;
     941          90 :     if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
     942          75 :         return;
     943             :     else
     944          15 :         dfLat = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
     945             : 
     946          15 :     snprintf(szFieldName, sizeof(szFieldName), "%s.longitude", pszBase);
     947          15 :     double dfLong = 0.0;
     948          15 :     if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
     949           0 :         return;
     950             :     else
     951          15 :         dfLong = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
     952             : 
     953             :     /* -------------------------------------------------------------------- */
     954             :     /*      Add the gcp to the internal list.                               */
     955             :     /* -------------------------------------------------------------------- */
     956          15 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     957             : 
     958          15 :     CPLFree(pasGCPList[nGCPCount].pszId);
     959             : 
     960          15 :     pasGCPList[nGCPCount].pszId = CPLStrdup(pszBase);
     961             : 
     962          15 :     pasGCPList[nGCPCount].dfGCPX = dfLong;
     963          15 :     pasGCPList[nGCPCount].dfGCPY = dfLat;
     964          15 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     965             : 
     966          15 :     pasGCPList[nGCPCount].dfGCPPixel = dfRasterX;
     967          15 :     pasGCPList[nGCPCount].dfGCPLine = dfRasterY;
     968             : 
     969          15 :     nGCPCount++;
     970             : }
     971             : 
     972             : /************************************************************************/
     973             : /*                           ProcessGeoref()                            */
     974             : /************************************************************************/
     975             : 
     976          18 : void HKVDataset::ProcessGeoref(const char *pszFilename)
     977             : 
     978             : {
     979             :     /* -------------------------------------------------------------------- */
     980             :     /*      Load the georef file, and boil white space away from around     */
     981             :     /*      the equal sign.                                                 */
     982             :     /* -------------------------------------------------------------------- */
     983          18 :     CSLDestroy(papszGeoref);
     984          18 :     papszGeoref = CSLLoad(pszFilename);
     985          18 :     if (papszGeoref == nullptr)
     986           0 :         return;
     987             : 
     988          18 :     HKVSpheroidList *hkvEllipsoids = new HKVSpheroidList;
     989             : 
     990          87 :     for (int i = 0; papszGeoref[i] != nullptr; i++)
     991             :     {
     992          69 :         int iDst = 0;
     993          69 :         char *pszLine = papszGeoref[i];
     994             : 
     995        1899 :         for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
     996             :         {
     997        1830 :             if (pszLine[iSrc] != ' ')
     998             :             {
     999        1830 :                 pszLine[iDst++] = pszLine[iSrc];
    1000             :             }
    1001             :         }
    1002          69 :         pszLine[iDst] = '\0';
    1003             :     }
    1004             : 
    1005             :     /* -------------------------------------------------------------------- */
    1006             :     /*      Try to get GCPs, in lat/longs                     .             */
    1007             :     /* -------------------------------------------------------------------- */
    1008          18 :     nGCPCount = 0;
    1009          18 :     pasGCPList = reinterpret_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 5));
    1010             : 
    1011          18 :     if (MFF2version > 1.0)
    1012             :     {
    1013          18 :         ProcessGeorefGCP(papszGeoref, "top_left", 0, 0);
    1014          18 :         ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize(), 0);
    1015          18 :         ProcessGeorefGCP(papszGeoref, "bottom_left", 0, GetRasterYSize());
    1016          18 :         ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize(),
    1017          18 :                          GetRasterYSize());
    1018          18 :         ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
    1019          18 :                          GetRasterYSize() / 2.0);
    1020             :     }
    1021             :     else
    1022             :     {
    1023           0 :         ProcessGeorefGCP(papszGeoref, "top_left", 0.5, 0.5);
    1024           0 :         ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize() - 0.5, 0.5);
    1025           0 :         ProcessGeorefGCP(papszGeoref, "bottom_left", 0.5,
    1026           0 :                          GetRasterYSize() - 0.5);
    1027           0 :         ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize() - 0.5,
    1028           0 :                          GetRasterYSize() - 0.5);
    1029           0 :         ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
    1030           0 :                          GetRasterYSize() / 2.0);
    1031             :     }
    1032             : 
    1033          18 :     if (nGCPCount == 0)
    1034             :     {
    1035          15 :         CPLFree(pasGCPList);
    1036          15 :         pasGCPList = nullptr;
    1037             :     }
    1038             : 
    1039             :     /* -------------------------------------------------------------------- */
    1040             :     /*      Do we have a recognised projection?                             */
    1041             :     /* -------------------------------------------------------------------- */
    1042          18 :     const char *pszProjName = CSLFetchNameValue(papszGeoref, "projection.name");
    1043             :     const char *pszOriginLong =
    1044          18 :         CSLFetchNameValue(papszGeoref, "projection.origin_longitude");
    1045             :     const char *pszSpheroidName =
    1046          18 :         CSLFetchNameValue(papszGeoref, "spheroid.name");
    1047             : 
    1048          36 :     if (pszSpheroidName != nullptr &&
    1049          18 :         hkvEllipsoids->SpheroidInList(pszSpheroidName))
    1050             :     {
    1051             : #if 0
    1052             :       // TODO(schwehr): Enable in trunk after 2.1 branch and fix.
    1053             :       // Breaks tests on some platforms.
    1054             :       CPLError( CE_Failure, CPLE_AppDefined,
    1055             :                 "Unrecognized ellipsoid.  Not handled.  "
    1056             :                 "Spheroid name not in spheroid list: '%s'",
    1057             :                 pszSpheroidName );
    1058             : #endif
    1059             :         // Why were eq_radius and inv_flattening never used?
    1060             :         // eq_radius = hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName);
    1061             :         // inv_flattening =
    1062             :         //     hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName);
    1063             :     }
    1064           0 :     else if (pszProjName != nullptr)
    1065             :     {
    1066           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1067             :                  "Unrecognized ellipsoid.  Not handled.");
    1068             :         // TODO(schwehr): This error is was never what was happening.
    1069             :         // CPLError( CE_Warning, CPLE_AppDefined,
    1070             :         //           "Unrecognized ellipsoid.  Using wgs-84 parameters.");
    1071             :         // eq_radius=hkvEllipsoids->GetSpheroidEqRadius("wgs-84"); */
    1072             :         // inv_flattening=hkvEllipsoids->GetSpheroidInverseFlattening("wgs-84");
    1073             :     }
    1074             : 
    1075          18 :     if (pszProjName != nullptr && EQUAL(pszProjName, "utm") && nGCPCount == 5)
    1076             :     {
    1077             :         // int nZone = (int)((CPLAtof(pszOriginLong)+184.5) / 6.0);
    1078           3 :         int nZone = 31;  // TODO(schwehr): Where does 31 come from?
    1079             : 
    1080           3 :         if (pszOriginLong == nullptr)
    1081             :         {
    1082             :             // If origin not specified, assume 0.0.
    1083           0 :             CPLError(
    1084             :                 CE_Warning, CPLE_AppDefined,
    1085             :                 "No projection origin longitude specified.  Assuming 0.0.");
    1086             :         }
    1087             :         else
    1088             :         {
    1089           3 :             nZone = 31 + static_cast<int>(floor(CPLAtof(pszOriginLong) / 6.0));
    1090             :         }
    1091             : 
    1092           6 :         OGRSpatialReference oUTM;
    1093             : 
    1094           3 :         if (pasGCPList[4].dfGCPY < 0)
    1095           0 :             oUTM.SetUTM(nZone, 0);
    1096             :         else
    1097           3 :             oUTM.SetUTM(nZone, 1);
    1098             : 
    1099           6 :         OGRSpatialReference oLL;
    1100           3 :         oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1101           3 :         if (pszOriginLong != nullptr)
    1102             :         {
    1103           3 :             oUTM.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, CPLAtof(pszOriginLong));
    1104           3 :             oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
    1105             :         }
    1106             : 
    1107           3 :         if ((pszSpheroidName == nullptr) ||
    1108           3 :             (EQUAL(pszSpheroidName, "wgs-84")) ||
    1109           3 :             (EQUAL(pszSpheroidName, "wgs_84")))
    1110             :         {
    1111           0 :             oUTM.SetWellKnownGeogCS("WGS84");
    1112           0 :             oLL.SetWellKnownGeogCS("WGS84");
    1113             :         }
    1114             :         else
    1115             :         {
    1116           3 :             if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
    1117             :             {
    1118           3 :                 oUTM.SetGeogCS(
    1119             :                     "unknown", "unknown", pszSpheroidName,
    1120             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
    1121             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
    1122             :                         pszSpheroidName));
    1123           3 :                 oLL.SetGeogCS(
    1124             :                     "unknown", "unknown", pszSpheroidName,
    1125             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
    1126             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
    1127             :                         pszSpheroidName));
    1128             :             }
    1129             :             else
    1130             :             {
    1131           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1132             :                          "Unrecognized ellipsoid.  Using wgs-84 parameters.");
    1133           0 :                 oUTM.SetWellKnownGeogCS("WGS84");
    1134           0 :                 oLL.SetWellKnownGeogCS("WGS84");
    1135             :             }
    1136             :         }
    1137             : 
    1138             :         OGRCoordinateTransformation *poTransform =
    1139           3 :             OGRCreateCoordinateTransformation(&oLL, &oUTM);
    1140             : 
    1141           3 :         bool bSuccess = true;
    1142           3 :         if (poTransform == nullptr)
    1143             :         {
    1144           0 :             CPLErrorReset();
    1145           0 :             bSuccess = false;
    1146             :         }
    1147             : 
    1148           3 :         double dfUtmX[5] = {0.0};
    1149           3 :         double dfUtmY[5] = {0.0};
    1150             : 
    1151           3 :         if (poTransform != nullptr)
    1152             :         {
    1153          18 :             for (int gcp_index = 0; gcp_index < 5; gcp_index++)
    1154             :             {
    1155          15 :                 dfUtmX[gcp_index] = pasGCPList[gcp_index].dfGCPX;
    1156          15 :                 dfUtmY[gcp_index] = pasGCPList[gcp_index].dfGCPY;
    1157             : 
    1158          15 :                 if (bSuccess && !poTransform->Transform(1, &(dfUtmX[gcp_index]),
    1159             :                                                         &(dfUtmY[gcp_index])))
    1160           0 :                     bSuccess = false;
    1161             :             }
    1162             :         }
    1163             : 
    1164           3 :         if (bSuccess)
    1165             :         {
    1166             :             // Update GCPS to proper projection.
    1167          18 :             for (int gcp_index = 0; gcp_index < 5; gcp_index++)
    1168             :             {
    1169          15 :                 pasGCPList[gcp_index].dfGCPX = dfUtmX[gcp_index];
    1170          15 :                 pasGCPList[gcp_index].dfGCPY = dfUtmY[gcp_index];
    1171             :             }
    1172             : 
    1173           3 :             m_oGCPSRS = oUTM;
    1174             : 
    1175           3 :             bool transform_ok = CPL_TO_BOOL(
    1176           3 :                 GDALGCPsToGeoTransform(5, pasGCPList, adfGeoTransform, 0));
    1177             : 
    1178           3 :             if (!transform_ok)
    1179             :             {
    1180             :                 // Transform may not be sufficient in all cases (slant range
    1181             :                 // projection).
    1182           0 :                 adfGeoTransform[0] = 0.0;
    1183           0 :                 adfGeoTransform[1] = 1.0;
    1184           0 :                 adfGeoTransform[2] = 0.0;
    1185           0 :                 adfGeoTransform[3] = 0.0;
    1186           0 :                 adfGeoTransform[4] = 0.0;
    1187           0 :                 adfGeoTransform[5] = 1.0;
    1188           0 :                 m_oGCPSRS.Clear();
    1189             :             }
    1190             :             else
    1191             :             {
    1192           3 :                 m_oSRS = std::move(oUTM);
    1193             :             }
    1194             :         }
    1195             : 
    1196           3 :         if (poTransform != nullptr)
    1197           6 :             delete poTransform;
    1198             :     }
    1199          15 :     else if (pszProjName != nullptr && nGCPCount == 5)
    1200             :     {
    1201           0 :         OGRSpatialReference oLL;
    1202           0 :         oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1203             : 
    1204           0 :         if (pszOriginLong != nullptr)
    1205             :         {
    1206           0 :             oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
    1207             :         }
    1208             : 
    1209           0 :         if (pszSpheroidName == nullptr ||
    1210           0 :             EQUAL(pszSpheroidName, "wgs-84") ||  // Dash.
    1211           0 :             EQUAL(pszSpheroidName, "wgs_84"))    // Underscore.
    1212             :         {
    1213           0 :             oLL.SetWellKnownGeogCS("WGS84");
    1214             :         }
    1215             :         else
    1216             :         {
    1217           0 :             if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
    1218             :             {
    1219           0 :                 oLL.SetGeogCS(
    1220             :                     "", "", pszSpheroidName,
    1221             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
    1222             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
    1223             :                         pszSpheroidName));
    1224             :             }
    1225             :             else
    1226             :             {
    1227           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1228             :                          "Unrecognized ellipsoid.  "
    1229             :                          "Using wgs-84 parameters.");
    1230           0 :                 oLL.SetWellKnownGeogCS("WGS84");
    1231             :             }
    1232             :         }
    1233             : 
    1234           0 :         const bool transform_ok = CPL_TO_BOOL(
    1235           0 :             GDALGCPsToGeoTransform(5, pasGCPList, adfGeoTransform, 0));
    1236             : 
    1237           0 :         m_oSRS.Clear();
    1238             : 
    1239           0 :         if (!transform_ok)
    1240             :         {
    1241           0 :             adfGeoTransform[0] = 0.0;
    1242           0 :             adfGeoTransform[1] = 1.0;
    1243           0 :             adfGeoTransform[2] = 0.0;
    1244           0 :             adfGeoTransform[3] = 0.0;
    1245           0 :             adfGeoTransform[4] = 0.0;
    1246           0 :             adfGeoTransform[5] = 1.0;
    1247             :         }
    1248             :         else
    1249             :         {
    1250           0 :             m_oSRS = oLL;
    1251             :         }
    1252             : 
    1253           0 :         m_oGCPSRS = std::move(oLL);
    1254             :     }
    1255             : 
    1256          18 :     delete hkvEllipsoids;
    1257             : }
    1258             : 
    1259             : /************************************************************************/
    1260             : /*                                Open()                                */
    1261             : /************************************************************************/
    1262             : 
    1263       29263 : GDALDataset *HKVDataset::Open(GDALOpenInfo *poOpenInfo)
    1264             : 
    1265             : {
    1266             :     /* -------------------------------------------------------------------- */
    1267             :     /*      We assume the dataset is passed as a directory.  Check for      */
    1268             :     /*      an attrib and blob file as a minimum.                           */
    1269             :     /* -------------------------------------------------------------------- */
    1270       29263 :     if (!poOpenInfo->bIsDirectory)
    1271       29002 :         return nullptr;
    1272             : 
    1273             :     const char *pszFilename =
    1274         261 :         CPLFormFilename(poOpenInfo->pszFilename, "image_data", nullptr);
    1275             :     VSIStatBuf sStat;
    1276         261 :     if (VSIStat(pszFilename, &sStat) != 0)
    1277         217 :         pszFilename = CPLFormFilename(poOpenInfo->pszFilename, "blob", nullptr);
    1278         261 :     if (VSIStat(pszFilename, &sStat) != 0)
    1279         217 :         return nullptr;
    1280             : 
    1281          44 :     pszFilename = CPLFormFilename(poOpenInfo->pszFilename, "attrib", nullptr);
    1282          44 :     if (VSIStat(pszFilename, &sStat) != 0)
    1283           0 :         return nullptr;
    1284             : 
    1285             :     /* -------------------------------------------------------------------- */
    1286             :     /*      Load the attrib file, and boil white space away from around     */
    1287             :     /*      the equal sign.                                                 */
    1288             :     /* -------------------------------------------------------------------- */
    1289          44 :     char **papszAttrib = CSLLoad(pszFilename);
    1290          44 :     if (papszAttrib == nullptr)
    1291           0 :         return nullptr;
    1292             : 
    1293         440 :     for (int i = 0; papszAttrib[i] != nullptr; i++)
    1294             :     {
    1295         396 :         int iDst = 0;
    1296         396 :         char *pszLine = papszAttrib[i];
    1297             : 
    1298       11173 :         for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
    1299             :         {
    1300       10777 :             if (pszLine[iSrc] != ' ')
    1301             :             {
    1302        9369 :                 pszLine[iDst++] = pszLine[iSrc];
    1303             :             }
    1304             :         }
    1305         396 :         pszLine[iDst] = '\0';
    1306             :     }
    1307             : 
    1308             :     /* -------------------------------------------------------------------- */
    1309             :     /*      Create a corresponding GDALDataset.                             */
    1310             :     /* -------------------------------------------------------------------- */
    1311          88 :     auto poDS = std::make_unique<HKVDataset>();
    1312             : 
    1313          44 :     poDS->pszPath = CPLStrdup(poOpenInfo->pszFilename);
    1314          44 :     poDS->papszAttrib = papszAttrib;
    1315             : 
    1316          44 :     poDS->eAccess = poOpenInfo->eAccess;
    1317             : 
    1318             :     /* -------------------------------------------------------------------- */
    1319             :     /*      Set some dataset wide information.                              */
    1320             :     /* -------------------------------------------------------------------- */
    1321          44 :     bool bNative = false;
    1322          44 :     bool bComplex = false;
    1323          44 :     int nRawBands = 0;
    1324             : 
    1325          88 :     if (CSLFetchNameValue(papszAttrib, "extent.cols") == nullptr ||
    1326          44 :         CSLFetchNameValue(papszAttrib, "extent.rows") == nullptr)
    1327             :     {
    1328           0 :         return nullptr;
    1329             :     }
    1330             : 
    1331          44 :     poDS->nRasterXSize = atoi(CSLFetchNameValue(papszAttrib, "extent.cols"));
    1332          44 :     poDS->nRasterYSize = atoi(CSLFetchNameValue(papszAttrib, "extent.rows"));
    1333             : 
    1334          44 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
    1335             :     {
    1336           0 :         return nullptr;
    1337             :     }
    1338             : 
    1339          44 :     const char *pszValue = CSLFetchNameValue(papszAttrib, "pixel.order");
    1340          44 :     if (pszValue == nullptr)
    1341           0 :         bNative = true;
    1342             :     else
    1343             :     {
    1344             : #ifdef CPL_MSB
    1345             :         bNative = strstr(pszValue, "*msbf") != NULL;
    1346             : #else
    1347          44 :         bNative = strstr(pszValue, "*lsbf") != nullptr;
    1348             : #endif
    1349             :     }
    1350             : 
    1351          44 :     bool bNoDataSet = false;
    1352          44 :     double dfNoDataValue = 0.0;
    1353          44 :     pszValue = CSLFetchNameValue(papszAttrib, "pixel.no_data");
    1354          44 :     if (pszValue != nullptr)
    1355             :     {
    1356           0 :         bNoDataSet = true;
    1357           0 :         dfNoDataValue = CPLAtof(pszValue);
    1358             :     }
    1359             : 
    1360          44 :     pszValue = CSLFetchNameValue(papszAttrib, "channel.enumeration");
    1361          44 :     if (pszValue != nullptr)
    1362          44 :         nRawBands = atoi(pszValue);
    1363             :     else
    1364           0 :         nRawBands = 1;
    1365             : 
    1366          44 :     if (!GDALCheckBandCount(nRawBands, TRUE))
    1367             :     {
    1368           0 :         return nullptr;
    1369             :     }
    1370             : 
    1371          44 :     pszValue = CSLFetchNameValue(papszAttrib, "pixel.field");
    1372          44 :     if (pszValue != nullptr && strstr(pszValue, "*complex") != nullptr)
    1373          10 :         bComplex = true;
    1374             :     else
    1375          34 :         bComplex = false;
    1376             : 
    1377             :     /* Get the version number, if present (if not, assume old version. */
    1378             :     /* Versions differ in their interpretation of corner coordinates.  */
    1379             : 
    1380          44 :     if (CSLFetchNameValue(papszAttrib, "version") != nullptr)
    1381          44 :         poDS->SetVersion(static_cast<float>(
    1382          44 :             CPLAtof(CSLFetchNameValue(papszAttrib, "version"))));
    1383             :     else
    1384           0 :         poDS->SetVersion(1.0);
    1385             : 
    1386             :     /* -------------------------------------------------------------------- */
    1387             :     /*      Figure out the datatype                                         */
    1388             :     /* -------------------------------------------------------------------- */
    1389          44 :     const char *pszEncoding = CSLFetchNameValue(papszAttrib, "pixel.encoding");
    1390          44 :     if (pszEncoding == nullptr)
    1391           0 :         pszEncoding = "{ *unsigned }";
    1392             : 
    1393          44 :     int nSize = 1;
    1394          44 :     if (CSLFetchNameValue(papszAttrib, "pixel.size") != nullptr)
    1395          44 :         nSize = atoi(CSLFetchNameValue(papszAttrib, "pixel.size")) / 8;
    1396             : #if 0
    1397             :     int nPseudoBands;
    1398             :     if( bComplex )
    1399             :         nPseudoBands = 2;
    1400             :     else
    1401             :         nPseudoBands = 1;
    1402             : #endif
    1403             : 
    1404             :     GDALDataType eType;
    1405          44 :     if (nSize == 1)
    1406          19 :         eType = GDT_Byte;
    1407          25 :     else if (nSize == 2 && strstr(pszEncoding, "*unsigned") != nullptr)
    1408           5 :         eType = GDT_UInt16;
    1409          20 :     else if (nSize == 4 && bComplex)
    1410           5 :         eType = GDT_CInt16;
    1411          15 :     else if (nSize == 2)
    1412           5 :         eType = GDT_Int16;
    1413          10 :     else if (nSize == 4 && strstr(pszEncoding, "*unsigned") != nullptr)
    1414           0 :         eType = GDT_UInt32;
    1415          10 :     else if (nSize == 8 && strstr(pszEncoding, "*two") != nullptr && bComplex)
    1416           0 :         eType = GDT_CInt32;
    1417          10 :     else if (nSize == 4 && strstr(pszEncoding, "*two") != nullptr)
    1418           0 :         eType = GDT_Int32;
    1419          10 :     else if (nSize == 8 && bComplex)
    1420           5 :         eType = GDT_CFloat32;
    1421           5 :     else if (nSize == 4)
    1422           5 :         eType = GDT_Float32;
    1423           0 :     else if (nSize == 16 && bComplex)
    1424           0 :         eType = GDT_CFloat64;
    1425           0 :     else if (nSize == 8)
    1426           0 :         eType = GDT_Float64;
    1427             :     else
    1428             :     {
    1429           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1430             :                  "Unsupported pixel data type in %s.\n"
    1431             :                  "pixel.size=%d pixel.encoding=%s",
    1432           0 :                  poDS->pszPath, nSize, pszEncoding);
    1433           0 :         return nullptr;
    1434             :     }
    1435             : 
    1436             :     /* -------------------------------------------------------------------- */
    1437             :     /*      Open the blob file.                                             */
    1438             :     /* -------------------------------------------------------------------- */
    1439          44 :     pszFilename = CPLFormFilename(poDS->pszPath, "image_data", nullptr);
    1440          44 :     if (VSIStat(pszFilename, &sStat) != 0)
    1441           0 :         pszFilename = CPLFormFilename(poDS->pszPath, "blob", nullptr);
    1442          44 :     if (poOpenInfo->eAccess == GA_ReadOnly)
    1443             :     {
    1444          18 :         poDS->fpBlob = VSIFOpenL(pszFilename, "rb");
    1445          18 :         if (poDS->fpBlob == nullptr)
    1446             :         {
    1447           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1448             :                      "Unable to open file %s for read access.", pszFilename);
    1449           0 :             return nullptr;
    1450             :         }
    1451             :     }
    1452             :     else
    1453             :     {
    1454          26 :         poDS->fpBlob = VSIFOpenL(pszFilename, "rb+");
    1455          26 :         if (poDS->fpBlob == nullptr)
    1456             :         {
    1457           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1458             :                      "Unable to open file %s for update access.", pszFilename);
    1459           0 :             return nullptr;
    1460             :         }
    1461             :     }
    1462             : 
    1463             :     /* -------------------------------------------------------------------- */
    1464             :     /*      Build the overview filename, as blob file = "_ovr".             */
    1465             :     /* -------------------------------------------------------------------- */
    1466          88 :     std::string osOvrFilename(pszFilename);
    1467          44 :     osOvrFilename += "_ovr";
    1468             : 
    1469             :     /* -------------------------------------------------------------------- */
    1470             :     /*      Define the bands.                                               */
    1471             :     /* -------------------------------------------------------------------- */
    1472          44 :     const int nPixelOffset = nRawBands * nSize;
    1473          44 :     const int nLineOffset = nPixelOffset * poDS->GetRasterXSize();
    1474          44 :     int nOffset = 0;
    1475             : 
    1476         138 :     for (int iRawBand = 0; iRawBand < nRawBands; iRawBand++)
    1477             :     {
    1478             :         auto poBand = std::make_unique<HKVRasterBand>(
    1479          94 :             poDS.get(), poDS->GetRasterCount() + 1, poDS->fpBlob, nOffset,
    1480          94 :             nPixelOffset, nLineOffset, eType, bNative);
    1481          94 :         if (!poBand->IsValid())
    1482           0 :             return nullptr;
    1483             : 
    1484          94 :         if (bNoDataSet)
    1485           0 :             poBand->SetNoDataValue(dfNoDataValue);
    1486          94 :         poDS->SetBand(poDS->GetRasterCount() + 1, std::move(poBand));
    1487          94 :         nOffset += GDALGetDataTypeSizeBytes(eType);
    1488             :     }
    1489             : 
    1490          44 :     poDS->eRasterType = eType;
    1491             : 
    1492             :     /* -------------------------------------------------------------------- */
    1493             :     /*      Process the georef file if there is one.                        */
    1494             :     /* -------------------------------------------------------------------- */
    1495          44 :     pszFilename = CPLFormFilename(poDS->pszPath, "georef", nullptr);
    1496          44 :     if (VSIStat(pszFilename, &sStat) == 0)
    1497          18 :         poDS->ProcessGeoref(pszFilename);
    1498             : 
    1499             :     /* -------------------------------------------------------------------- */
    1500             :     /*      Initialize any PAM information.                                 */
    1501             :     /* -------------------------------------------------------------------- */
    1502          44 :     poDS->SetDescription(osOvrFilename.c_str());
    1503          44 :     poDS->TryLoadXML();
    1504             : 
    1505             :     /* -------------------------------------------------------------------- */
    1506             :     /*      Handle overviews.                                               */
    1507             :     /* -------------------------------------------------------------------- */
    1508          44 :     poDS->oOvManager.Initialize(poDS.get(), osOvrFilename.c_str(), nullptr,
    1509             :                                 TRUE);
    1510             : 
    1511          44 :     return poDS.release();
    1512             : }
    1513             : 
    1514             : /************************************************************************/
    1515             : /*                               Create()                               */
    1516             : /************************************************************************/
    1517             : 
    1518          51 : GDALDataset *HKVDataset::Create(const char *pszFilenameIn, int nXSize,
    1519             :                                 int nYSize, int nBandsIn, GDALDataType eType,
    1520             :                                 char ** /* papszParamList */)
    1521             : 
    1522             : {
    1523             :     /* -------------------------------------------------------------------- */
    1524             :     /*      Verify input options.                                           */
    1525             :     /* -------------------------------------------------------------------- */
    1526          51 :     if (nBandsIn <= 0)
    1527             :     {
    1528           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1529             :                  "HKV driver does not support %d bands.", nBandsIn);
    1530           1 :         return nullptr;
    1531             :     }
    1532             : 
    1533          50 :     if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 &&
    1534          27 :         eType != GDT_CInt16 && eType != GDT_Float32 && eType != GDT_CFloat32)
    1535             :     {
    1536          21 :         CPLError(CE_Failure, CPLE_AppDefined,
    1537             :                  "Attempt to create HKV file with currently unsupported\n"
    1538             :                  "data type (%s).",
    1539             :                  GDALGetDataTypeName(eType));
    1540             : 
    1541          21 :         return nullptr;
    1542             :     }
    1543             : 
    1544             :     /* -------------------------------------------------------------------- */
    1545             :     /*      Establish the name of the directory we will be creating the     */
    1546             :     /*      new HKV directory in.  Verify that this is a directory.         */
    1547             :     /* -------------------------------------------------------------------- */
    1548          29 :     char *pszBaseDir = nullptr;
    1549             : 
    1550          29 :     if (strlen(CPLGetPath(pszFilenameIn)) == 0)
    1551           0 :         pszBaseDir = CPLStrdup(".");
    1552             :     else
    1553          29 :         pszBaseDir = CPLStrdup(CPLGetPath(pszFilenameIn));
    1554             : 
    1555             :     VSIStatBuf sStat;
    1556          29 :     if (CPLStat(pszBaseDir, &sStat) != 0 || !VSI_ISDIR(sStat.st_mode))
    1557             :     {
    1558           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1559             :                  "Attempt to create HKV dataset under %s,\n"
    1560             :                  "but this is not a valid directory.",
    1561             :                  pszBaseDir);
    1562           3 :         CPLFree(pszBaseDir);
    1563           3 :         return nullptr;
    1564             :     }
    1565             : 
    1566          26 :     CPLFree(pszBaseDir);
    1567          26 :     pszBaseDir = nullptr;
    1568             : 
    1569          26 :     if (VSIMkdir(pszFilenameIn, 0755) != 0)
    1570             :     {
    1571           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to create directory %s.",
    1572             :                  pszFilenameIn);
    1573           0 :         return nullptr;
    1574             :     }
    1575             : 
    1576             :     /* -------------------------------------------------------------------- */
    1577             :     /*      Create the header file.                                         */
    1578             :     /* -------------------------------------------------------------------- */
    1579          26 :     CPLErr CEHeaderCreated = SaveHKVAttribFile(pszFilenameIn, nXSize, nYSize,
    1580             :                                                nBandsIn, eType, FALSE, 0.0);
    1581             : 
    1582          26 :     if (CEHeaderCreated != CE_None)
    1583           0 :         return nullptr;
    1584             : 
    1585             :     /* -------------------------------------------------------------------- */
    1586             :     /*      Create the blob file.                                           */
    1587             :     /* -------------------------------------------------------------------- */
    1588             : 
    1589             :     const char *pszFilename =
    1590          26 :         CPLFormFilename(pszFilenameIn, "image_data", nullptr);
    1591          26 :     FILE *fp = VSIFOpen(pszFilename, "wb");
    1592          26 :     if (fp == nullptr)
    1593             :     {
    1594           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Couldn't create %s.\n",
    1595             :                  pszFilename);
    1596           0 :         return nullptr;
    1597             :     }
    1598             : 
    1599          26 :     bool bOK = VSIFWrite(reinterpret_cast<void *>(const_cast<char *>("")), 1, 1,
    1600          26 :                          fp) == 1;
    1601          26 :     if (VSIFClose(fp) != 0)
    1602           0 :         bOK &= false;
    1603             : 
    1604          26 :     if (!bOK)
    1605           0 :         return nullptr;
    1606             :     /* -------------------------------------------------------------------- */
    1607             :     /*      Open the dataset normally.                                      */
    1608             :     /* -------------------------------------------------------------------- */
    1609          26 :     return GDALDataset::FromHandle(GDALOpen(pszFilenameIn, GA_Update));
    1610             : }
    1611             : 
    1612             : /************************************************************************/
    1613             : /*                               Delete()                               */
    1614             : /*                                                                      */
    1615             : /*      An HKV Blob dataset consists of a bunch of files in a           */
    1616             : /*      directory.  Try to delete all the files, then the               */
    1617             : /*      directory.                                                      */
    1618             : /************************************************************************/
    1619             : 
    1620           1 : CPLErr HKVDataset::Delete(const char *pszName)
    1621             : 
    1622             : {
    1623             :     VSIStatBuf sStat;
    1624           1 :     if (CPLStat(pszName, &sStat) != 0 || !VSI_ISDIR(sStat.st_mode))
    1625             :     {
    1626           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1627             :                  "%s does not appear to be an HKV Dataset, as it is not "
    1628             :                  "a path to a directory.",
    1629             :                  pszName);
    1630           0 :         return CE_Failure;
    1631             :     }
    1632             : 
    1633           1 :     char **papszFiles = VSIReadDir(pszName);
    1634           6 :     for (int i = 0; i < CSLCount(papszFiles); i++)
    1635             :     {
    1636           5 :         if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
    1637           2 :             continue;
    1638             : 
    1639             :         const char *pszTarget =
    1640           3 :             CPLFormFilename(pszName, papszFiles[i], nullptr);
    1641           3 :         if (VSIUnlink(pszTarget) != 0)
    1642             :         {
    1643           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1644             :                      "Unable to delete file %s,"
    1645             :                      "HKVDataset Delete(%s) failed.",
    1646             :                      pszTarget, pszName);
    1647           0 :             CSLDestroy(papszFiles);
    1648           0 :             return CE_Failure;
    1649             :         }
    1650             :     }
    1651             : 
    1652           1 :     CSLDestroy(papszFiles);
    1653             : 
    1654           1 :     if (VSIRmdir(pszName) != 0)
    1655             :     {
    1656           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1657             :                  "Unable to delete directory %s,"
    1658             :                  "HKVDataset Delete() failed.",
    1659             :                  pszName);
    1660           0 :         return CE_Failure;
    1661             :     }
    1662             : 
    1663           1 :     return CE_None;
    1664             : }
    1665             : 
    1666             : /************************************************************************/
    1667             : /*                             CreateCopy()                             */
    1668             : /************************************************************************/
    1669             : 
    1670          20 : GDALDataset *HKVDataset::CreateCopy(const char *pszFilename,
    1671             :                                     GDALDataset *poSrcDS,
    1672             :                                     CPL_UNUSED int bStrict, char **papszOptions,
    1673             :                                     GDALProgressFunc pfnProgress,
    1674             :                                     void *pProgressData)
    1675             : {
    1676          20 :     int nBands = poSrcDS->GetRasterCount();
    1677          20 :     if (nBands == 0)
    1678             :     {
    1679           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1680             :                  "HKV driver does not support source dataset with zero band.");
    1681           1 :         return nullptr;
    1682             :     }
    1683             : 
    1684          19 :     GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    1685             : 
    1686          19 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    1687           0 :         return nullptr;
    1688             : 
    1689             :     /* check that other bands match type- sets type */
    1690             :     /* to unknown if they differ.                  */
    1691          29 :     for (int iBand = 1; iBand < poSrcDS->GetRasterCount(); iBand++)
    1692             :     {
    1693          10 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
    1694          10 :         eType = GDALDataTypeUnion(eType, poBand->GetRasterDataType());
    1695             :     }
    1696             : 
    1697          19 :     HKVDataset *poDS = reinterpret_cast<HKVDataset *>(Create(
    1698             :         pszFilename, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
    1699             :         poSrcDS->GetRasterCount(), eType, papszOptions));
    1700             : 
    1701             :     /* Check that Create worked- return Null if it didn't */
    1702          19 :     if (poDS == nullptr)
    1703           8 :         return nullptr;
    1704             : 
    1705             :     /* -------------------------------------------------------------------- */
    1706             :     /*      Copy the image data.                                            */
    1707             :     /* -------------------------------------------------------------------- */
    1708          11 :     const int nXSize = poDS->GetRasterXSize();
    1709          11 :     const int nYSize = poDS->GetRasterYSize();
    1710             : 
    1711             :     int nBlockXSize, nBlockYSize;
    1712          11 :     poDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1713             : 
    1714          11 :     const int nBlockTotal = ((nXSize + nBlockXSize - 1) / nBlockXSize) *
    1715          11 :                             ((nYSize + nBlockYSize - 1) / nBlockYSize) *
    1716          11 :                             poSrcDS->GetRasterCount();
    1717             : 
    1718          11 :     int nBlocksDone = 0;
    1719          32 :     for (int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++)
    1720             :     {
    1721          21 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
    1722          21 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(iBand + 1);
    1723             : 
    1724             :         /* Get nodata value, if relevant */
    1725          21 :         int pbSuccess = FALSE;
    1726          21 :         double dfSrcNoDataValue = poSrcBand->GetNoDataValue(&pbSuccess);
    1727          21 :         if (pbSuccess)
    1728           0 :             poDS->SetNoDataValue(dfSrcNoDataValue);
    1729             : 
    1730          63 :         void *pData = CPLMalloc(nBlockXSize * nBlockYSize *
    1731          21 :                                 GDALGetDataTypeSize(eType) / 8);
    1732             : 
    1733         241 :         for (int iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize)
    1734             :         {
    1735         440 :             for (int iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize)
    1736             :             {
    1737         220 :                 if (!pfnProgress((nBlocksDone++) /
    1738         220 :                                      static_cast<float>(nBlockTotal),
    1739             :                                  nullptr, pProgressData))
    1740             :                 {
    1741           0 :                     CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1742           0 :                     delete poDS;
    1743           0 :                     CPLFree(pData);
    1744             : 
    1745             :                     GDALDriver *poHKVDriver = reinterpret_cast<GDALDriver *>(
    1746           0 :                         GDALGetDriverByName("MFF2"));
    1747           0 :                     poHKVDriver->Delete(pszFilename);
    1748           0 :                     return nullptr;
    1749             :                 }
    1750             : 
    1751         220 :                 const int nTBXSize = std::min(nBlockXSize, nXSize - iXOffset);
    1752         220 :                 const int nTBYSize = std::min(nBlockYSize, nYSize - iYOffset);
    1753             : 
    1754         220 :                 CPLErr eErr = poSrcBand->RasterIO(
    1755             :                     GF_Read, iXOffset, iYOffset, nTBXSize, nTBYSize, pData,
    1756             :                     nTBXSize, nTBYSize, eType, 0, 0, nullptr);
    1757         220 :                 if (eErr != CE_None)
    1758             :                 {
    1759           0 :                     delete poDS;
    1760           0 :                     CPLFree(pData);
    1761           0 :                     return nullptr;
    1762             :                 }
    1763             : 
    1764         220 :                 eErr = poDstBand->RasterIO(GF_Write, iXOffset, iYOffset,
    1765             :                                            nTBXSize, nTBYSize, pData, nTBXSize,
    1766             :                                            nTBYSize, eType, 0, 0, nullptr);
    1767             : 
    1768         220 :                 if (eErr != CE_None)
    1769             :                 {
    1770           0 :                     delete poDS;
    1771           0 :                     CPLFree(pData);
    1772           0 :                     return nullptr;
    1773             :                 }
    1774             :             }
    1775             :         }
    1776             : 
    1777          21 :         CPLFree(pData);
    1778             :     }
    1779             : 
    1780             :     /* -------------------------------------------------------------------- */
    1781             :     /*      Copy georeferencing information, if enough is available.        */
    1782             :     /*      Only copy geotransform-style info (won't work for slant range). */
    1783             :     /* -------------------------------------------------------------------- */
    1784             : 
    1785             :     double *tempGeoTransform =
    1786          11 :         static_cast<double *>(CPLMalloc(6 * sizeof(double)));
    1787             : 
    1788          22 :     if ((poSrcDS->GetGeoTransform(tempGeoTransform) == CE_None) &&
    1789          11 :         (tempGeoTransform[0] != 0.0 || tempGeoTransform[1] != 1.0 ||
    1790           0 :          tempGeoTransform[2] != 0.0 || tempGeoTransform[3] != 0.0 ||
    1791           0 :          tempGeoTransform[4] != 0.0 || std::abs(tempGeoTransform[5]) != 1.0))
    1792             :     {
    1793          11 :         auto poSrcSRS = poSrcDS->GetSpatialRef();
    1794          11 :         if (poSrcSRS)
    1795             :         {
    1796          11 :             poDS->SetSpatialRef(poSrcSRS);
    1797          11 :             poDS->m_oGCPSRS = *poSrcSRS;
    1798             :         }
    1799          11 :         poDS->SetGeoTransform(tempGeoTransform);
    1800             : 
    1801          11 :         CPLFree(tempGeoTransform);
    1802             : 
    1803             :         // georef file will be saved automatically when dataset is deleted
    1804             :         // because SetProjection sets a flag to indicate it is necessary.
    1805             :     }
    1806             :     else
    1807             :     {
    1808           0 :         CPLFree(tempGeoTransform);
    1809             :     }
    1810             : 
    1811             :     // Make sure image data gets flushed.
    1812          32 :     for (int iBand = 0; iBand < poDS->GetRasterCount(); iBand++)
    1813             :     {
    1814             :         RawRasterBand *poDstBand =
    1815          21 :             reinterpret_cast<RawRasterBand *>(poDS->GetRasterBand(iBand + 1));
    1816          21 :         poDstBand->FlushCache(false);
    1817             :     }
    1818             : 
    1819          11 :     if (!pfnProgress(1.0, nullptr, pProgressData))
    1820             :     {
    1821           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1822           0 :         delete poDS;
    1823             : 
    1824             :         GDALDriver *poHKVDriver =
    1825           0 :             reinterpret_cast<GDALDriver *>(GDALGetDriverByName("MFF2"));
    1826           0 :         poHKVDriver->Delete(pszFilename);
    1827           0 :         return nullptr;
    1828             :     }
    1829             : 
    1830          11 :     poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    1831             : 
    1832          11 :     return poDS;
    1833             : }
    1834             : 
    1835             : /************************************************************************/
    1836             : /*                         GDALRegister_HKV()                           */
    1837             : /************************************************************************/
    1838             : 
    1839        1520 : void GDALRegister_HKV()
    1840             : 
    1841             : {
    1842        1520 :     if (GDALGetDriverByName("MFF2") != nullptr)
    1843         301 :         return;
    1844             : 
    1845        1219 :     GDALDriver *poDriver = new GDALDriver();
    1846             : 
    1847        1219 :     poDriver->SetDescription("MFF2");
    1848        1219 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1849        1219 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Vexcel MFF2 (HKV) Raster");
    1850        1219 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/mff2.html");
    1851        1219 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    1852             :                               "Byte Int16 UInt16 Int32 UInt32 CInt16 "
    1853        1219 :                               "CInt32 Float32 Float64 CFloat32 CFloat64");
    1854             : 
    1855        1219 :     poDriver->pfnOpen = HKVDataset::Open;
    1856        1219 :     poDriver->pfnCreate = HKVDataset::Create;
    1857        1219 :     poDriver->pfnDelete = HKVDataset::Delete;
    1858        1219 :     poDriver->pfnCreateCopy = HKVDataset::CreateCopy;
    1859             : 
    1860        1219 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1861             : }

Generated by: LCOV version 1.14