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

Generated by: LCOV version 1.14