LCOV - code coverage report
Current view: top level - frmts/raw - hkvdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 610 808 75.5 %
Date: 2024-11-21 22:18:42 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 char *pszFilename =
     377          26 :                 CPLFormFilename(pszPath, "georef", nullptr);
     378          26 :             CSLSave(papszGeoref, pszFilename);
     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          26 :     const char *pszFilename = CPLFormFilename(pszFilenameIn, "attrib", nullptr);
     434             : 
     435          26 :     FILE *fp = VSIFOpen(pszFilename, "wt");
     436          26 :     if (fp == nullptr)
     437             :     {
     438           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Couldn't create %s.",
     439             :                  pszFilename);
     440           0 :         return CE_Failure;
     441             :     }
     442             : 
     443          26 :     fprintf(fp, "channel.enumeration = %d\n", nBands);
     444          26 :     fprintf(fp, "channel.interleave = { *pixel tile sequential }\n");
     445          26 :     fprintf(fp, "extent.cols = %d\n", nXSize);
     446          26 :     fprintf(fp, "extent.rows = %d\n", nYSize);
     447             : 
     448          26 :     switch (eType)
     449             :     {
     450          11 :         case GDT_Byte:
     451          11 :             fprintf(fp, "pixel.encoding = "
     452             :                         "{ *unsigned twos-complement ieee-754 }\n");
     453          11 :             break;
     454             : 
     455           3 :         case GDT_UInt16:
     456           3 :             fprintf(fp, "pixel.encoding = "
     457             :                         "{ *unsigned twos-complement ieee-754 }\n");
     458           3 :             break;
     459             : 
     460           6 :         case GDT_CInt16:
     461             :         case GDT_Int16:
     462           6 :             fprintf(fp, "pixel.encoding = "
     463             :                         "{ unsigned *twos-complement ieee-754 }\n");
     464           6 :             break;
     465             : 
     466           6 :         case GDT_CFloat32:
     467             :         case GDT_Float32:
     468           6 :             fprintf(fp, "pixel.encoding = "
     469             :                         "{ unsigned twos-complement *ieee-754 }\n");
     470           6 :             break;
     471             : 
     472           0 :         default:
     473           0 :             CPLAssert(false);
     474             :     }
     475             : 
     476          26 :     fprintf(fp, "pixel.size = %d\n", GDALGetDataTypeSizeBits(eType));
     477          26 :     if (GDALDataTypeIsComplex(eType))
     478           6 :         fprintf(fp, "pixel.field = { real *complex }\n");
     479             :     else
     480          20 :         fprintf(fp, "pixel.field = { *real complex }\n");
     481             : 
     482             : #ifdef CPL_MSB
     483             :     fprintf(fp, "pixel.order = { lsbf *msbf }\n");
     484             : #else
     485          26 :     fprintf(fp, "pixel.order = { *lsbf msbf }\n");
     486             : #endif
     487             : 
     488          26 :     if (bNoDataSet)
     489           0 :         fprintf(fp, "pixel.no_data = %s\n", CPLSPrintf("%f", dfNoDataValue));
     490             : 
     491             :     // Version information- only create the new style.
     492          26 :     fprintf(fp, "version = 1.1");
     493             : 
     494          26 :     if (VSIFClose(fp) != 0)
     495           0 :         return CE_Failure;
     496          26 :     return CE_None;
     497             : }
     498             : 
     499             : /************************************************************************/
     500             : /*                          GetGeoTransform()                           */
     501             : /************************************************************************/
     502             : 
     503          29 : CPLErr HKVDataset::GetGeoTransform(double *padfTransform)
     504             : 
     505             : {
     506          29 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     507          29 :     return CE_None;
     508             : }
     509             : 
     510             : /************************************************************************/
     511             : /*                          SetGeoTransform()                           */
     512             : /************************************************************************/
     513             : 
     514          26 : CPLErr HKVDataset::SetGeoTransform(double *padfTransform)
     515             : 
     516             : {
     517             :     // NOTE:  Geotransform coordinates must match the current projection
     518             :     // of the dataset being changed (not the geotransform source).
     519             :     // i.e. be in lat/longs for LL projected; UTM for UTM projected.
     520             :     // SET PROJECTION BEFORE SETTING GEOTRANSFORM TO AVOID SYNCHRONIZATION
     521             :     // PROBLEMS.
     522             : 
     523             :     // Update the geotransform itself.
     524          26 :     memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
     525             : 
     526             :     // Clear previous gcps.
     527          26 :     if (nGCPCount > 0)
     528             :     {
     529           0 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
     530           0 :         CPLFree(pasGCPList);
     531             :     }
     532          26 :     nGCPCount = 0;
     533          26 :     pasGCPList = nullptr;
     534             : 
     535             :     // Return if the identity transform is set.
     536          26 :     if (adfGeoTransform[0] == 0.0 && adfGeoTransform[1] == 1.0 &&
     537           0 :         adfGeoTransform[2] == 0.0 && adfGeoTransform[3] == 0.0 &&
     538           0 :         adfGeoTransform[4] == 0.0 && adfGeoTransform[5] == 1.0)
     539           0 :         return CE_None;
     540             : 
     541             :     // Update georef text info for saving later, and update GCPs to match
     542             :     // geotransform.
     543             : 
     544          26 :     OGRCoordinateTransformation *poTransform = nullptr;
     545          26 :     bool bSuccess = true;
     546             : 
     547             :     // Projection parameter checking will have been done in SetProjection.
     548          37 :     if ((CSLFetchNameValue(papszGeoref, "projection.name") != nullptr) &&
     549          11 :         (EQUAL(CSLFetchNameValue(papszGeoref, "projection.name"), "UTM")))
     550             :     {
     551           1 :         auto poLLSRS = m_oSRS.CloneGeogCS();
     552           1 :         if (poLLSRS)
     553             :         {
     554           1 :             poLLSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     555           1 :             poTransform = OGRCreateCoordinateTransformation(&m_oSRS, poLLSRS);
     556           1 :             delete poLLSRS;
     557           1 :             if (poTransform == nullptr)
     558             :             {
     559           0 :                 bSuccess = false;
     560           0 :                 CPLErrorReset();
     561             :             }
     562             :         }
     563             :         else
     564             :         {
     565           0 :             bSuccess = false;
     566             :         }
     567             :     }
     568          25 :     else if (((CSLFetchNameValue(papszGeoref, "projection.name") != nullptr) &&
     569          10 :               (!EQUAL(CSLFetchNameValue(papszGeoref, "projection.name"),
     570          50 :                       "LL"))) ||
     571          25 :              (CSLFetchNameValue(papszGeoref, "projection.name") == nullptr))
     572             :     {
     573          15 :         return CE_Failure;
     574             :     }
     575             : 
     576          11 :     nGCPCount = 0;
     577          11 :     pasGCPList = static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 5));
     578             : 
     579             :     /* -------------------------------------------------------------------- */
     580             :     /*      top left                                                        */
     581             :     /* -------------------------------------------------------------------- */
     582          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     583          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     584          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("top_left");
     585             : 
     586          11 :     double temp_lat = 0.0;
     587          11 :     double temp_long = 0.0;
     588          11 :     if (MFF2version > 1.0)
     589             :     {
     590          11 :         temp_lat = padfTransform[3];
     591          11 :         temp_long = padfTransform[0];
     592          11 :         pasGCPList[nGCPCount].dfGCPPixel = 0.0;
     593          11 :         pasGCPList[nGCPCount].dfGCPLine = 0.0;
     594             :     }
     595             :     else
     596             :     {
     597           0 :         temp_lat =
     598           0 :             padfTransform[3] + 0.5 * padfTransform[4] + 0.5 * padfTransform[5];
     599           0 :         temp_long =
     600           0 :             padfTransform[0] + 0.5 * padfTransform[1] + 0.5 * padfTransform[2];
     601           0 :         pasGCPList[nGCPCount].dfGCPPixel = 0.5;
     602           0 :         pasGCPList[nGCPCount].dfGCPLine = 0.5;
     603             :     }
     604          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     605          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     606          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     607          11 :     nGCPCount++;
     608             : 
     609          11 :     if (poTransform != nullptr)
     610             :     {
     611           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     612           0 :             bSuccess = false;
     613             :     }
     614             : 
     615          11 :     if (bSuccess)
     616             :     {
     617          11 :         char szValue[128] = {'\0'};
     618          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     619          11 :         papszGeoref =
     620          11 :             CSLSetNameValue(papszGeoref, "top_left.latitude", szValue);
     621             : 
     622          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     623          11 :         papszGeoref =
     624          11 :             CSLSetNameValue(papszGeoref, "top_left.longitude", szValue);
     625             :     }
     626             : 
     627             :     /* -------------------------------------------------------------------- */
     628             :     /*      top_right                                                       */
     629             :     /* -------------------------------------------------------------------- */
     630          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     631          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     632          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("top_right");
     633             : 
     634          11 :     if (MFF2version > 1.0)
     635             :     {
     636          11 :         temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4];
     637          11 :         temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1];
     638          11 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize();
     639          11 :         pasGCPList[nGCPCount].dfGCPLine = 0.0;
     640             :     }
     641             :     else
     642             :     {
     643           0 :         temp_lat = padfTransform[3] +
     644           0 :                    (GetRasterXSize() - 0.5) * padfTransform[4] +
     645           0 :                    0.5 * padfTransform[5];
     646           0 :         temp_long = padfTransform[0] +
     647           0 :                     (GetRasterXSize() - 0.5) * padfTransform[1] +
     648           0 :                     0.5 * padfTransform[2];
     649           0 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() - 0.5;
     650           0 :         pasGCPList[nGCPCount].dfGCPLine = 0.5;
     651             :     }
     652          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     653          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     654          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     655          11 :     nGCPCount++;
     656             : 
     657          11 :     if (poTransform != nullptr)
     658             :     {
     659           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     660           0 :             bSuccess = false;
     661             :     }
     662             : 
     663          11 :     if (bSuccess)
     664             :     {
     665          11 :         char szValue[128] = {'\0'};
     666          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     667          11 :         papszGeoref =
     668          11 :             CSLSetNameValue(papszGeoref, "top_right.latitude", szValue);
     669             : 
     670          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     671          11 :         papszGeoref =
     672          11 :             CSLSetNameValue(papszGeoref, "top_right.longitude", szValue);
     673             :     }
     674             : 
     675             :     /* -------------------------------------------------------------------- */
     676             :     /*      bottom_left                                                     */
     677             :     /* -------------------------------------------------------------------- */
     678          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     679          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     680          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("bottom_left");
     681             : 
     682          11 :     if (MFF2version > 1.0)
     683             :     {
     684          11 :         temp_lat = padfTransform[3] + GetRasterYSize() * padfTransform[5];
     685          11 :         temp_long = padfTransform[0] + GetRasterYSize() * padfTransform[2];
     686          11 :         pasGCPList[nGCPCount].dfGCPPixel = 0.0;
     687          11 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize();
     688             :     }
     689             :     else
     690             :     {
     691           0 :         temp_lat = padfTransform[3] + 0.5 * padfTransform[4] +
     692           0 :                    (GetRasterYSize() - 0.5) * padfTransform[5];
     693           0 :         temp_long = padfTransform[0] + 0.5 * padfTransform[1] +
     694           0 :                     (GetRasterYSize() - 0.5) * padfTransform[2];
     695           0 :         pasGCPList[nGCPCount].dfGCPPixel = 0.5;
     696           0 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize() - 0.5;
     697             :     }
     698          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     699          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     700          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     701          11 :     nGCPCount++;
     702             : 
     703          11 :     if (poTransform != nullptr)
     704             :     {
     705           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     706           0 :             bSuccess = false;
     707             :     }
     708             : 
     709          11 :     if (bSuccess)
     710             :     {
     711          11 :         char szValue[128] = {'\0'};
     712          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     713          11 :         papszGeoref =
     714          11 :             CSLSetNameValue(papszGeoref, "bottom_left.latitude", szValue);
     715             : 
     716          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     717          11 :         papszGeoref =
     718          11 :             CSLSetNameValue(papszGeoref, "bottom_left.longitude", szValue);
     719             :     }
     720             : 
     721             :     /* -------------------------------------------------------------------- */
     722             :     /*      bottom_right                                                    */
     723             :     /* -------------------------------------------------------------------- */
     724          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     725          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     726          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("bottom_right");
     727             : 
     728          11 :     if (MFF2version > 1.0)
     729             :     {
     730          11 :         temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4] +
     731          11 :                    GetRasterYSize() * padfTransform[5];
     732          11 :         temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1] +
     733          11 :                     GetRasterYSize() * padfTransform[2];
     734          11 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize();
     735          11 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize();
     736             :     }
     737             :     else
     738             :     {
     739           0 :         temp_lat = padfTransform[3] +
     740           0 :                    (GetRasterXSize() - 0.5) * padfTransform[4] +
     741           0 :                    (GetRasterYSize() - 0.5) * padfTransform[5];
     742           0 :         temp_long = padfTransform[0] +
     743           0 :                     (GetRasterXSize() - 0.5) * padfTransform[1] +
     744           0 :                     (GetRasterYSize() - 0.5) * padfTransform[2];
     745           0 :         pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() - 0.5;
     746           0 :         pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize() - 0.5;
     747             :     }
     748          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     749          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     750          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     751          11 :     nGCPCount++;
     752             : 
     753          11 :     if (poTransform != nullptr)
     754             :     {
     755           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     756           0 :             bSuccess = false;
     757             :     }
     758             : 
     759          11 :     if (bSuccess)
     760             :     {
     761          11 :         char szValue[128] = {'\0'};
     762          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     763          11 :         papszGeoref =
     764          11 :             CSLSetNameValue(papszGeoref, "bottom_right.latitude", szValue);
     765             : 
     766          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     767          11 :         papszGeoref =
     768          11 :             CSLSetNameValue(papszGeoref, "bottom_right.longitude", szValue);
     769             :     }
     770             : 
     771             :     /* -------------------------------------------------------------------- */
     772             :     /*      Center                                                          */
     773             :     /* -------------------------------------------------------------------- */
     774          11 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     775          11 :     CPLFree(pasGCPList[nGCPCount].pszId);
     776          11 :     pasGCPList[nGCPCount].pszId = CPLStrdup("centre");
     777             : 
     778          11 :     temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4] * 0.5 +
     779          11 :                GetRasterYSize() * padfTransform[5] * 0.5;
     780          11 :     temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1] * 0.5 +
     781          11 :                 GetRasterYSize() * padfTransform[2] * 0.5;
     782          11 :     pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() / 2.0;
     783          11 :     pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize() / 2.0;
     784             : 
     785          11 :     pasGCPList[nGCPCount].dfGCPX = temp_long;
     786          11 :     pasGCPList[nGCPCount].dfGCPY = temp_lat;
     787          11 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     788          11 :     nGCPCount++;
     789             : 
     790          11 :     if (poTransform != nullptr)
     791             :     {
     792           1 :         if (!bSuccess || !poTransform->Transform(1, &temp_long, &temp_lat))
     793           0 :             bSuccess = false;
     794             :     }
     795             : 
     796          11 :     if (bSuccess)
     797             :     {
     798          11 :         char szValue[128] = {'\0'};
     799          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_lat);
     800          11 :         papszGeoref = CSLSetNameValue(papszGeoref, "centre.latitude", szValue);
     801             : 
     802          11 :         CPLsnprintf(szValue, sizeof(szValue), "%.10f", temp_long);
     803          11 :         papszGeoref = CSLSetNameValue(papszGeoref, "centre.longitude", szValue);
     804             :     }
     805             : 
     806          11 :     if (!bSuccess)
     807             :     {
     808           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     809             :                  "Error setting header info in SetGeoTransform. "
     810             :                  "Changes may not be saved properly.");
     811             :     }
     812             : 
     813          11 :     if (poTransform != nullptr)
     814           1 :         delete poTransform;
     815             : 
     816          11 :     bGeorefChanged = true;
     817             : 
     818          11 :     return CE_None;
     819             : }
     820             : 
     821             : /************************************************************************/
     822             : /*                           SetSpatialRef()                            */
     823             : /*                                                                      */
     824             : /*      We provide very limited support for setting the projection.     */
     825             : /************************************************************************/
     826             : 
     827          26 : CPLErr HKVDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     828             : 
     829             : {
     830             :     // Update a georef file.
     831          26 :     if (poSRS == nullptr)
     832             :     {
     833           0 :         m_oSRS.Clear();
     834           0 :         return CE_None;
     835             :     }
     836          26 :     m_oSRS = *poSRS;
     837             : 
     838          27 :     if ((m_oSRS.GetAttrValue("PROJECTION") != nullptr) &&
     839           1 :         (EQUAL(m_oSRS.GetAttrValue("PROJECTION"), SRS_PT_TRANSVERSE_MERCATOR)))
     840             :     {
     841           1 :         papszGeoref = CSLSetNameValue(papszGeoref, "projection.name", "utm");
     842           1 :         OGRErr ogrerrorOl = OGRERR_NONE;
     843           1 :         papszGeoref = CSLSetNameValue(
     844             :             papszGeoref, "projection.origin_longitude",
     845             :             CPLSPrintf("%f", m_oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0,
     846             :                                                 &ogrerrorOl)));
     847             :     }
     848          50 :     else if (m_oSRS.GetAttrValue("PROJECTION") == nullptr &&
     849          25 :              m_oSRS.IsGeographic())
     850             :     {
     851          25 :         papszGeoref = CSLSetNameValue(papszGeoref, "projection.name", "LL");
     852             :     }
     853             :     else
     854             :     {
     855           0 :         CPLError(CE_Warning, CPLE_AppDefined, "Unrecognized projection.");
     856           0 :         return CE_Failure;
     857             :     }
     858             : 
     859          26 :     OGRErr ogrerrorEq = OGRERR_NONE;
     860          26 :     const double eq_radius = m_oSRS.GetSemiMajor(&ogrerrorEq);
     861             : 
     862          26 :     OGRErr ogrerrorInvf = OGRERR_NONE;
     863          26 :     const double inv_flattening = m_oSRS.GetInvFlattening(&ogrerrorInvf);
     864             : 
     865          26 :     if ((ogrerrorEq == OGRERR_NONE) && (ogrerrorInvf == OGRERR_NONE))
     866             :     {
     867          26 :         HKVSpheroidList *hkvEllipsoids = new HKVSpheroidList;
     868             :         char *spheroid_name =
     869          26 :             hkvEllipsoids->GetSpheroidNameByEqRadiusAndInvFlattening(
     870             :                 eq_radius, inv_flattening);
     871          26 :         if (spheroid_name != nullptr)
     872             :         {
     873          26 :             papszGeoref =
     874          26 :                 CSLSetNameValue(papszGeoref, "spheroid.name", spheroid_name);
     875             :         }
     876          26 :         CPLFree(spheroid_name);
     877          26 :         delete hkvEllipsoids;
     878             :     }
     879             :     else
     880             :     {
     881             :         // Default to previous behavior if spheroid not found by radius and
     882             :         // inverse flattening.
     883           0 :         char *pszProjection = nullptr;
     884           0 :         m_oSRS.exportToWkt(&pszProjection);
     885           0 :         if (pszProjection && strstr(pszProjection, "Bessel") != nullptr)
     886             :         {
     887           0 :             papszGeoref =
     888           0 :                 CSLSetNameValue(papszGeoref, "spheroid.name", "ev-bessel");
     889             :         }
     890             :         else
     891             :         {
     892           0 :             papszGeoref =
     893           0 :                 CSLSetNameValue(papszGeoref, "spheroid.name", "ev-wgs-84");
     894             :         }
     895           0 :         CPLFree(pszProjection);
     896             :     }
     897          26 :     bGeorefChanged = true;
     898          26 :     return CE_None;
     899             : }
     900             : 
     901             : /************************************************************************/
     902             : /*                               GetGCP()                               */
     903             : /************************************************************************/
     904             : 
     905           0 : const GDAL_GCP *HKVDataset::GetGCPs()
     906             : 
     907             : {
     908           0 :     return pasGCPList;
     909             : }
     910             : 
     911             : /************************************************************************/
     912             : /*                          ProcessGeorefGCP()                          */
     913             : /************************************************************************/
     914             : 
     915          90 : void HKVDataset::ProcessGeorefGCP(char **papszGeorefIn, const char *pszBase,
     916             :                                   double dfRasterX, double dfRasterY)
     917             : 
     918             : {
     919             :     /* -------------------------------------------------------------------- */
     920             :     /*      Fetch the GCP from the string list.                             */
     921             :     /* -------------------------------------------------------------------- */
     922          90 :     char szFieldName[128] = {'\0'};
     923          90 :     snprintf(szFieldName, sizeof(szFieldName), "%s.latitude", pszBase);
     924          90 :     double dfLat = 0.0;
     925          90 :     if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
     926          75 :         return;
     927             :     else
     928          15 :         dfLat = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
     929             : 
     930          15 :     snprintf(szFieldName, sizeof(szFieldName), "%s.longitude", pszBase);
     931          15 :     double dfLong = 0.0;
     932          15 :     if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
     933           0 :         return;
     934             :     else
     935          15 :         dfLong = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
     936             : 
     937             :     /* -------------------------------------------------------------------- */
     938             :     /*      Add the gcp to the internal list.                               */
     939             :     /* -------------------------------------------------------------------- */
     940          15 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     941             : 
     942          15 :     CPLFree(pasGCPList[nGCPCount].pszId);
     943             : 
     944          15 :     pasGCPList[nGCPCount].pszId = CPLStrdup(pszBase);
     945             : 
     946          15 :     pasGCPList[nGCPCount].dfGCPX = dfLong;
     947          15 :     pasGCPList[nGCPCount].dfGCPY = dfLat;
     948          15 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     949             : 
     950          15 :     pasGCPList[nGCPCount].dfGCPPixel = dfRasterX;
     951          15 :     pasGCPList[nGCPCount].dfGCPLine = dfRasterY;
     952             : 
     953          15 :     nGCPCount++;
     954             : }
     955             : 
     956             : /************************************************************************/
     957             : /*                           ProcessGeoref()                            */
     958             : /************************************************************************/
     959             : 
     960          18 : void HKVDataset::ProcessGeoref(const char *pszFilename)
     961             : 
     962             : {
     963             :     /* -------------------------------------------------------------------- */
     964             :     /*      Load the georef file, and boil white space away from around     */
     965             :     /*      the equal sign.                                                 */
     966             :     /* -------------------------------------------------------------------- */
     967          18 :     CSLDestroy(papszGeoref);
     968          18 :     papszGeoref = CSLLoad(pszFilename);
     969          18 :     if (papszGeoref == nullptr)
     970           0 :         return;
     971             : 
     972          18 :     HKVSpheroidList *hkvEllipsoids = new HKVSpheroidList;
     973             : 
     974          87 :     for (int i = 0; papszGeoref[i] != nullptr; i++)
     975             :     {
     976          69 :         int iDst = 0;
     977          69 :         char *pszLine = papszGeoref[i];
     978             : 
     979        1899 :         for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
     980             :         {
     981        1830 :             if (pszLine[iSrc] != ' ')
     982             :             {
     983        1830 :                 pszLine[iDst++] = pszLine[iSrc];
     984             :             }
     985             :         }
     986          69 :         pszLine[iDst] = '\0';
     987             :     }
     988             : 
     989             :     /* -------------------------------------------------------------------- */
     990             :     /*      Try to get GCPs, in lat/longs                     .             */
     991             :     /* -------------------------------------------------------------------- */
     992          18 :     nGCPCount = 0;
     993          18 :     pasGCPList = reinterpret_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 5));
     994             : 
     995          18 :     if (MFF2version > 1.0)
     996             :     {
     997          18 :         ProcessGeorefGCP(papszGeoref, "top_left", 0, 0);
     998          18 :         ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize(), 0);
     999          18 :         ProcessGeorefGCP(papszGeoref, "bottom_left", 0, GetRasterYSize());
    1000          18 :         ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize(),
    1001          18 :                          GetRasterYSize());
    1002          18 :         ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
    1003          18 :                          GetRasterYSize() / 2.0);
    1004             :     }
    1005             :     else
    1006             :     {
    1007           0 :         ProcessGeorefGCP(papszGeoref, "top_left", 0.5, 0.5);
    1008           0 :         ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize() - 0.5, 0.5);
    1009           0 :         ProcessGeorefGCP(papszGeoref, "bottom_left", 0.5,
    1010           0 :                          GetRasterYSize() - 0.5);
    1011           0 :         ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize() - 0.5,
    1012           0 :                          GetRasterYSize() - 0.5);
    1013           0 :         ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
    1014           0 :                          GetRasterYSize() / 2.0);
    1015             :     }
    1016             : 
    1017          18 :     if (nGCPCount == 0)
    1018             :     {
    1019          15 :         CPLFree(pasGCPList);
    1020          15 :         pasGCPList = nullptr;
    1021             :     }
    1022             : 
    1023             :     /* -------------------------------------------------------------------- */
    1024             :     /*      Do we have a recognised projection?                             */
    1025             :     /* -------------------------------------------------------------------- */
    1026          18 :     const char *pszProjName = CSLFetchNameValue(papszGeoref, "projection.name");
    1027             :     const char *pszOriginLong =
    1028          18 :         CSLFetchNameValue(papszGeoref, "projection.origin_longitude");
    1029             :     const char *pszSpheroidName =
    1030          18 :         CSLFetchNameValue(papszGeoref, "spheroid.name");
    1031             : 
    1032          36 :     if (pszSpheroidName != nullptr &&
    1033          18 :         hkvEllipsoids->SpheroidInList(pszSpheroidName))
    1034             :     {
    1035             : #if 0
    1036             :       // TODO(schwehr): Enable in trunk after 2.1 branch and fix.
    1037             :       // Breaks tests on some platforms.
    1038             :       CPLError( CE_Failure, CPLE_AppDefined,
    1039             :                 "Unrecognized ellipsoid.  Not handled.  "
    1040             :                 "Spheroid name not in spheroid list: '%s'",
    1041             :                 pszSpheroidName );
    1042             : #endif
    1043             :         // Why were eq_radius and inv_flattening never used?
    1044             :         // eq_radius = hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName);
    1045             :         // inv_flattening =
    1046             :         //     hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName);
    1047             :     }
    1048           0 :     else if (pszProjName != nullptr)
    1049             :     {
    1050           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1051             :                  "Unrecognized ellipsoid.  Not handled.");
    1052             :         // TODO(schwehr): This error is was never what was happening.
    1053             :         // CPLError( CE_Warning, CPLE_AppDefined,
    1054             :         //           "Unrecognized ellipsoid.  Using wgs-84 parameters.");
    1055             :         // eq_radius=hkvEllipsoids->GetSpheroidEqRadius("wgs-84"); */
    1056             :         // inv_flattening=hkvEllipsoids->GetSpheroidInverseFlattening("wgs-84");
    1057             :     }
    1058             : 
    1059          18 :     if (pszProjName != nullptr && EQUAL(pszProjName, "utm") && nGCPCount == 5)
    1060             :     {
    1061             :         // int nZone = (int)((CPLAtof(pszOriginLong)+184.5) / 6.0);
    1062           3 :         int nZone = 31;  // TODO(schwehr): Where does 31 come from?
    1063             : 
    1064           3 :         if (pszOriginLong == nullptr)
    1065             :         {
    1066             :             // If origin not specified, assume 0.0.
    1067           0 :             CPLError(
    1068             :                 CE_Warning, CPLE_AppDefined,
    1069             :                 "No projection origin longitude specified.  Assuming 0.0.");
    1070             :         }
    1071             :         else
    1072             :         {
    1073           3 :             nZone = 31 + static_cast<int>(floor(CPLAtof(pszOriginLong) / 6.0));
    1074             :         }
    1075             : 
    1076           6 :         OGRSpatialReference oUTM;
    1077             : 
    1078           3 :         if (pasGCPList[4].dfGCPY < 0)
    1079           0 :             oUTM.SetUTM(nZone, 0);
    1080             :         else
    1081           3 :             oUTM.SetUTM(nZone, 1);
    1082             : 
    1083           6 :         OGRSpatialReference oLL;
    1084           3 :         oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1085           3 :         if (pszOriginLong != nullptr)
    1086             :         {
    1087           3 :             oUTM.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, CPLAtof(pszOriginLong));
    1088           3 :             oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
    1089             :         }
    1090             : 
    1091           3 :         if ((pszSpheroidName == nullptr) ||
    1092           3 :             (EQUAL(pszSpheroidName, "wgs-84")) ||
    1093           3 :             (EQUAL(pszSpheroidName, "wgs_84")))
    1094             :         {
    1095           0 :             oUTM.SetWellKnownGeogCS("WGS84");
    1096           0 :             oLL.SetWellKnownGeogCS("WGS84");
    1097             :         }
    1098             :         else
    1099             :         {
    1100           3 :             if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
    1101             :             {
    1102           3 :                 oUTM.SetGeogCS(
    1103             :                     "unknown", "unknown", pszSpheroidName,
    1104             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
    1105             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
    1106             :                         pszSpheroidName));
    1107           3 :                 oLL.SetGeogCS(
    1108             :                     "unknown", "unknown", pszSpheroidName,
    1109             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
    1110             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
    1111             :                         pszSpheroidName));
    1112             :             }
    1113             :             else
    1114             :             {
    1115           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1116             :                          "Unrecognized ellipsoid.  Using wgs-84 parameters.");
    1117           0 :                 oUTM.SetWellKnownGeogCS("WGS84");
    1118           0 :                 oLL.SetWellKnownGeogCS("WGS84");
    1119             :             }
    1120             :         }
    1121             : 
    1122             :         OGRCoordinateTransformation *poTransform =
    1123           3 :             OGRCreateCoordinateTransformation(&oLL, &oUTM);
    1124             : 
    1125           3 :         bool bSuccess = true;
    1126           3 :         if (poTransform == nullptr)
    1127             :         {
    1128           0 :             CPLErrorReset();
    1129           0 :             bSuccess = false;
    1130             :         }
    1131             : 
    1132           3 :         double dfUtmX[5] = {0.0};
    1133           3 :         double dfUtmY[5] = {0.0};
    1134             : 
    1135           3 :         if (poTransform != nullptr)
    1136             :         {
    1137          18 :             for (int gcp_index = 0; gcp_index < 5; gcp_index++)
    1138             :             {
    1139          15 :                 dfUtmX[gcp_index] = pasGCPList[gcp_index].dfGCPX;
    1140          15 :                 dfUtmY[gcp_index] = pasGCPList[gcp_index].dfGCPY;
    1141             : 
    1142          15 :                 if (bSuccess && !poTransform->Transform(1, &(dfUtmX[gcp_index]),
    1143             :                                                         &(dfUtmY[gcp_index])))
    1144           0 :                     bSuccess = false;
    1145             :             }
    1146             :         }
    1147             : 
    1148           3 :         if (bSuccess)
    1149             :         {
    1150             :             // Update GCPS to proper projection.
    1151          18 :             for (int gcp_index = 0; gcp_index < 5; gcp_index++)
    1152             :             {
    1153          15 :                 pasGCPList[gcp_index].dfGCPX = dfUtmX[gcp_index];
    1154          15 :                 pasGCPList[gcp_index].dfGCPY = dfUtmY[gcp_index];
    1155             :             }
    1156             : 
    1157           3 :             m_oGCPSRS = oUTM;
    1158             : 
    1159           3 :             bool transform_ok = CPL_TO_BOOL(
    1160           3 :                 GDALGCPsToGeoTransform(5, pasGCPList, adfGeoTransform, 0));
    1161             : 
    1162           3 :             if (!transform_ok)
    1163             :             {
    1164             :                 // Transform may not be sufficient in all cases (slant range
    1165             :                 // projection).
    1166           0 :                 adfGeoTransform[0] = 0.0;
    1167           0 :                 adfGeoTransform[1] = 1.0;
    1168           0 :                 adfGeoTransform[2] = 0.0;
    1169           0 :                 adfGeoTransform[3] = 0.0;
    1170           0 :                 adfGeoTransform[4] = 0.0;
    1171           0 :                 adfGeoTransform[5] = 1.0;
    1172           0 :                 m_oGCPSRS.Clear();
    1173             :             }
    1174             :             else
    1175             :             {
    1176           3 :                 m_oSRS = std::move(oUTM);
    1177             :             }
    1178             :         }
    1179             : 
    1180           3 :         if (poTransform != nullptr)
    1181           6 :             delete poTransform;
    1182             :     }
    1183          15 :     else if (pszProjName != nullptr && nGCPCount == 5)
    1184             :     {
    1185           0 :         OGRSpatialReference oLL;
    1186           0 :         oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1187             : 
    1188           0 :         if (pszOriginLong != nullptr)
    1189             :         {
    1190           0 :             oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
    1191             :         }
    1192             : 
    1193           0 :         if (pszSpheroidName == nullptr ||
    1194           0 :             EQUAL(pszSpheroidName, "wgs-84") ||  // Dash.
    1195           0 :             EQUAL(pszSpheroidName, "wgs_84"))    // Underscore.
    1196             :         {
    1197           0 :             oLL.SetWellKnownGeogCS("WGS84");
    1198             :         }
    1199             :         else
    1200             :         {
    1201           0 :             if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
    1202             :             {
    1203           0 :                 oLL.SetGeogCS(
    1204             :                     "", "", pszSpheroidName,
    1205             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
    1206             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
    1207             :                         pszSpheroidName));
    1208             :             }
    1209             :             else
    1210             :             {
    1211           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1212             :                          "Unrecognized ellipsoid.  "
    1213             :                          "Using wgs-84 parameters.");
    1214           0 :                 oLL.SetWellKnownGeogCS("WGS84");
    1215             :             }
    1216             :         }
    1217             : 
    1218           0 :         const bool transform_ok = CPL_TO_BOOL(
    1219           0 :             GDALGCPsToGeoTransform(5, pasGCPList, adfGeoTransform, 0));
    1220             : 
    1221           0 :         m_oSRS.Clear();
    1222             : 
    1223           0 :         if (!transform_ok)
    1224             :         {
    1225           0 :             adfGeoTransform[0] = 0.0;
    1226           0 :             adfGeoTransform[1] = 1.0;
    1227           0 :             adfGeoTransform[2] = 0.0;
    1228           0 :             adfGeoTransform[3] = 0.0;
    1229           0 :             adfGeoTransform[4] = 0.0;
    1230           0 :             adfGeoTransform[5] = 1.0;
    1231             :         }
    1232             :         else
    1233             :         {
    1234           0 :             m_oSRS = oLL;
    1235             :         }
    1236             : 
    1237           0 :         m_oGCPSRS = std::move(oLL);
    1238             :     }
    1239             : 
    1240          18 :     delete hkvEllipsoids;
    1241             : }
    1242             : 
    1243             : /************************************************************************/
    1244             : /*                                Open()                                */
    1245             : /************************************************************************/
    1246             : 
    1247       30698 : GDALDataset *HKVDataset::Open(GDALOpenInfo *poOpenInfo)
    1248             : 
    1249             : {
    1250             :     /* -------------------------------------------------------------------- */
    1251             :     /*      We assume the dataset is passed as a directory.  Check for      */
    1252             :     /*      an attrib and blob file as a minimum.                           */
    1253             :     /* -------------------------------------------------------------------- */
    1254       30698 :     if (!poOpenInfo->bIsDirectory)
    1255       30431 :         return nullptr;
    1256             : 
    1257             :     const char *pszFilename =
    1258         267 :         CPLFormFilename(poOpenInfo->pszFilename, "image_data", nullptr);
    1259             :     VSIStatBuf sStat;
    1260         267 :     if (VSIStat(pszFilename, &sStat) != 0)
    1261         223 :         pszFilename = CPLFormFilename(poOpenInfo->pszFilename, "blob", nullptr);
    1262         267 :     if (VSIStat(pszFilename, &sStat) != 0)
    1263         223 :         return nullptr;
    1264             : 
    1265          44 :     pszFilename = CPLFormFilename(poOpenInfo->pszFilename, "attrib", nullptr);
    1266          44 :     if (VSIStat(pszFilename, &sStat) != 0)
    1267           0 :         return nullptr;
    1268             : 
    1269             :     /* -------------------------------------------------------------------- */
    1270             :     /*      Load the attrib file, and boil white space away from around     */
    1271             :     /*      the equal sign.                                                 */
    1272             :     /* -------------------------------------------------------------------- */
    1273          44 :     char **papszAttrib = CSLLoad(pszFilename);
    1274          44 :     if (papszAttrib == nullptr)
    1275           0 :         return nullptr;
    1276             : 
    1277         440 :     for (int i = 0; papszAttrib[i] != nullptr; i++)
    1278             :     {
    1279         396 :         int iDst = 0;
    1280         396 :         char *pszLine = papszAttrib[i];
    1281             : 
    1282       11173 :         for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
    1283             :         {
    1284       10777 :             if (pszLine[iSrc] != ' ')
    1285             :             {
    1286        9369 :                 pszLine[iDst++] = pszLine[iSrc];
    1287             :             }
    1288             :         }
    1289         396 :         pszLine[iDst] = '\0';
    1290             :     }
    1291             : 
    1292             :     /* -------------------------------------------------------------------- */
    1293             :     /*      Create a corresponding GDALDataset.                             */
    1294             :     /* -------------------------------------------------------------------- */
    1295          88 :     auto poDS = std::make_unique<HKVDataset>();
    1296             : 
    1297          44 :     poDS->pszPath = CPLStrdup(poOpenInfo->pszFilename);
    1298          44 :     poDS->papszAttrib = papszAttrib;
    1299             : 
    1300          44 :     poDS->eAccess = poOpenInfo->eAccess;
    1301             : 
    1302             :     /* -------------------------------------------------------------------- */
    1303             :     /*      Set some dataset wide information.                              */
    1304             :     /* -------------------------------------------------------------------- */
    1305          44 :     bool bNative = false;
    1306          44 :     bool bComplex = false;
    1307          44 :     int nRawBands = 0;
    1308             : 
    1309          88 :     if (CSLFetchNameValue(papszAttrib, "extent.cols") == nullptr ||
    1310          44 :         CSLFetchNameValue(papszAttrib, "extent.rows") == nullptr)
    1311             :     {
    1312           0 :         return nullptr;
    1313             :     }
    1314             : 
    1315          44 :     poDS->nRasterXSize = atoi(CSLFetchNameValue(papszAttrib, "extent.cols"));
    1316          44 :     poDS->nRasterYSize = atoi(CSLFetchNameValue(papszAttrib, "extent.rows"));
    1317             : 
    1318          44 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
    1319             :     {
    1320           0 :         return nullptr;
    1321             :     }
    1322             : 
    1323          44 :     const char *pszValue = CSLFetchNameValue(papszAttrib, "pixel.order");
    1324          44 :     if (pszValue == nullptr)
    1325           0 :         bNative = true;
    1326             :     else
    1327             :     {
    1328             : #ifdef CPL_MSB
    1329             :         bNative = strstr(pszValue, "*msbf") != NULL;
    1330             : #else
    1331          44 :         bNative = strstr(pszValue, "*lsbf") != nullptr;
    1332             : #endif
    1333             :     }
    1334             : 
    1335          44 :     bool bNoDataSet = false;
    1336          44 :     double dfNoDataValue = 0.0;
    1337          44 :     pszValue = CSLFetchNameValue(papszAttrib, "pixel.no_data");
    1338          44 :     if (pszValue != nullptr)
    1339             :     {
    1340           0 :         bNoDataSet = true;
    1341           0 :         dfNoDataValue = CPLAtof(pszValue);
    1342             :     }
    1343             : 
    1344          44 :     pszValue = CSLFetchNameValue(papszAttrib, "channel.enumeration");
    1345          44 :     if (pszValue != nullptr)
    1346          44 :         nRawBands = atoi(pszValue);
    1347             :     else
    1348           0 :         nRawBands = 1;
    1349             : 
    1350          44 :     if (!GDALCheckBandCount(nRawBands, TRUE))
    1351             :     {
    1352           0 :         return nullptr;
    1353             :     }
    1354             : 
    1355          44 :     pszValue = CSLFetchNameValue(papszAttrib, "pixel.field");
    1356          44 :     if (pszValue != nullptr && strstr(pszValue, "*complex") != nullptr)
    1357          10 :         bComplex = true;
    1358             :     else
    1359          34 :         bComplex = false;
    1360             : 
    1361             :     /* Get the version number, if present (if not, assume old version. */
    1362             :     /* Versions differ in their interpretation of corner coordinates.  */
    1363             : 
    1364          44 :     if (CSLFetchNameValue(papszAttrib, "version") != nullptr)
    1365          44 :         poDS->SetVersion(static_cast<float>(
    1366          44 :             CPLAtof(CSLFetchNameValue(papszAttrib, "version"))));
    1367             :     else
    1368           0 :         poDS->SetVersion(1.0);
    1369             : 
    1370             :     /* -------------------------------------------------------------------- */
    1371             :     /*      Figure out the datatype                                         */
    1372             :     /* -------------------------------------------------------------------- */
    1373          44 :     const char *pszEncoding = CSLFetchNameValue(papszAttrib, "pixel.encoding");
    1374          44 :     if (pszEncoding == nullptr)
    1375           0 :         pszEncoding = "{ *unsigned }";
    1376             : 
    1377          44 :     int nSize = 1;
    1378          44 :     if (CSLFetchNameValue(papszAttrib, "pixel.size") != nullptr)
    1379          44 :         nSize = atoi(CSLFetchNameValue(papszAttrib, "pixel.size")) / 8;
    1380             : #if 0
    1381             :     int nPseudoBands;
    1382             :     if( bComplex )
    1383             :         nPseudoBands = 2;
    1384             :     else
    1385             :         nPseudoBands = 1;
    1386             : #endif
    1387             : 
    1388             :     GDALDataType eType;
    1389          44 :     if (nSize == 1)
    1390          19 :         eType = GDT_Byte;
    1391          25 :     else if (nSize == 2 && strstr(pszEncoding, "*unsigned") != nullptr)
    1392           5 :         eType = GDT_UInt16;
    1393          20 :     else if (nSize == 4 && bComplex)
    1394           5 :         eType = GDT_CInt16;
    1395          15 :     else if (nSize == 2)
    1396           5 :         eType = GDT_Int16;
    1397          10 :     else if (nSize == 4 && strstr(pszEncoding, "*unsigned") != nullptr)
    1398           0 :         eType = GDT_UInt32;
    1399          10 :     else if (nSize == 8 && strstr(pszEncoding, "*two") != nullptr && bComplex)
    1400           0 :         eType = GDT_CInt32;
    1401          10 :     else if (nSize == 4 && strstr(pszEncoding, "*two") != nullptr)
    1402           0 :         eType = GDT_Int32;
    1403          10 :     else if (nSize == 8 && bComplex)
    1404           5 :         eType = GDT_CFloat32;
    1405           5 :     else if (nSize == 4)
    1406           5 :         eType = GDT_Float32;
    1407           0 :     else if (nSize == 16 && bComplex)
    1408           0 :         eType = GDT_CFloat64;
    1409           0 :     else if (nSize == 8)
    1410           0 :         eType = GDT_Float64;
    1411             :     else
    1412             :     {
    1413           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1414             :                  "Unsupported pixel data type in %s.\n"
    1415             :                  "pixel.size=%d pixel.encoding=%s",
    1416           0 :                  poDS->pszPath, nSize, pszEncoding);
    1417           0 :         return nullptr;
    1418             :     }
    1419             : 
    1420             :     /* -------------------------------------------------------------------- */
    1421             :     /*      Open the blob file.                                             */
    1422             :     /* -------------------------------------------------------------------- */
    1423          44 :     pszFilename = CPLFormFilename(poDS->pszPath, "image_data", nullptr);
    1424          44 :     if (VSIStat(pszFilename, &sStat) != 0)
    1425           0 :         pszFilename = CPLFormFilename(poDS->pszPath, "blob", nullptr);
    1426          44 :     if (poOpenInfo->eAccess == GA_ReadOnly)
    1427             :     {
    1428          18 :         poDS->fpBlob = VSIFOpenL(pszFilename, "rb");
    1429          18 :         if (poDS->fpBlob == nullptr)
    1430             :         {
    1431           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1432             :                      "Unable to open file %s for read access.", pszFilename);
    1433           0 :             return nullptr;
    1434             :         }
    1435             :     }
    1436             :     else
    1437             :     {
    1438          26 :         poDS->fpBlob = VSIFOpenL(pszFilename, "rb+");
    1439          26 :         if (poDS->fpBlob == nullptr)
    1440             :         {
    1441           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1442             :                      "Unable to open file %s for update access.", pszFilename);
    1443           0 :             return nullptr;
    1444             :         }
    1445             :     }
    1446             : 
    1447             :     /* -------------------------------------------------------------------- */
    1448             :     /*      Build the overview filename, as blob file = "_ovr".             */
    1449             :     /* -------------------------------------------------------------------- */
    1450          88 :     std::string osOvrFilename(pszFilename);
    1451          44 :     osOvrFilename += "_ovr";
    1452             : 
    1453             :     /* -------------------------------------------------------------------- */
    1454             :     /*      Define the bands.                                               */
    1455             :     /* -------------------------------------------------------------------- */
    1456          44 :     const int nPixelOffset = nRawBands * nSize;
    1457          44 :     const int nLineOffset = nPixelOffset * poDS->GetRasterXSize();
    1458          44 :     int nOffset = 0;
    1459             : 
    1460         138 :     for (int iRawBand = 0; iRawBand < nRawBands; iRawBand++)
    1461             :     {
    1462             :         auto poBand = std::make_unique<HKVRasterBand>(
    1463          94 :             poDS.get(), poDS->GetRasterCount() + 1, poDS->fpBlob, nOffset,
    1464          94 :             nPixelOffset, nLineOffset, eType, bNative);
    1465          94 :         if (!poBand->IsValid())
    1466           0 :             return nullptr;
    1467             : 
    1468          94 :         if (bNoDataSet)
    1469           0 :             poBand->SetNoDataValue(dfNoDataValue);
    1470          94 :         poDS->SetBand(poDS->GetRasterCount() + 1, std::move(poBand));
    1471          94 :         nOffset += GDALGetDataTypeSizeBytes(eType);
    1472             :     }
    1473             : 
    1474          44 :     poDS->eRasterType = eType;
    1475             : 
    1476             :     /* -------------------------------------------------------------------- */
    1477             :     /*      Process the georef file if there is one.                        */
    1478             :     /* -------------------------------------------------------------------- */
    1479          44 :     pszFilename = CPLFormFilename(poDS->pszPath, "georef", nullptr);
    1480          44 :     if (VSIStat(pszFilename, &sStat) == 0)
    1481          18 :         poDS->ProcessGeoref(pszFilename);
    1482             : 
    1483             :     /* -------------------------------------------------------------------- */
    1484             :     /*      Initialize any PAM information.                                 */
    1485             :     /* -------------------------------------------------------------------- */
    1486          44 :     poDS->SetDescription(osOvrFilename.c_str());
    1487          44 :     poDS->TryLoadXML();
    1488             : 
    1489             :     /* -------------------------------------------------------------------- */
    1490             :     /*      Handle overviews.                                               */
    1491             :     /* -------------------------------------------------------------------- */
    1492          44 :     poDS->oOvManager.Initialize(poDS.get(), osOvrFilename.c_str(), nullptr,
    1493             :                                 TRUE);
    1494             : 
    1495          44 :     return poDS.release();
    1496             : }
    1497             : 
    1498             : /************************************************************************/
    1499             : /*                               Create()                               */
    1500             : /************************************************************************/
    1501             : 
    1502          51 : GDALDataset *HKVDataset::Create(const char *pszFilenameIn, int nXSize,
    1503             :                                 int nYSize, int nBandsIn, GDALDataType eType,
    1504             :                                 char ** /* papszParamList */)
    1505             : 
    1506             : {
    1507             :     /* -------------------------------------------------------------------- */
    1508             :     /*      Verify input options.                                           */
    1509             :     /* -------------------------------------------------------------------- */
    1510          51 :     if (nBandsIn <= 0)
    1511             :     {
    1512           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1513             :                  "HKV driver does not support %d bands.", nBandsIn);
    1514           1 :         return nullptr;
    1515             :     }
    1516             : 
    1517          50 :     if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 &&
    1518          27 :         eType != GDT_CInt16 && eType != GDT_Float32 && eType != GDT_CFloat32)
    1519             :     {
    1520          21 :         CPLError(CE_Failure, CPLE_AppDefined,
    1521             :                  "Attempt to create HKV file with currently unsupported\n"
    1522             :                  "data type (%s).",
    1523             :                  GDALGetDataTypeName(eType));
    1524             : 
    1525          21 :         return nullptr;
    1526             :     }
    1527             : 
    1528             :     /* -------------------------------------------------------------------- */
    1529             :     /*      Establish the name of the directory we will be creating the     */
    1530             :     /*      new HKV directory in.  Verify that this is a directory.         */
    1531             :     /* -------------------------------------------------------------------- */
    1532          29 :     char *pszBaseDir = nullptr;
    1533             : 
    1534          29 :     if (strlen(CPLGetPath(pszFilenameIn)) == 0)
    1535           0 :         pszBaseDir = CPLStrdup(".");
    1536             :     else
    1537          29 :         pszBaseDir = CPLStrdup(CPLGetPath(pszFilenameIn));
    1538             : 
    1539             :     VSIStatBuf sStat;
    1540          29 :     if (CPLStat(pszBaseDir, &sStat) != 0 || !VSI_ISDIR(sStat.st_mode))
    1541             :     {
    1542           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1543             :                  "Attempt to create HKV dataset under %s,\n"
    1544             :                  "but this is not a valid directory.",
    1545             :                  pszBaseDir);
    1546           3 :         CPLFree(pszBaseDir);
    1547           3 :         return nullptr;
    1548             :     }
    1549             : 
    1550          26 :     CPLFree(pszBaseDir);
    1551          26 :     pszBaseDir = nullptr;
    1552             : 
    1553          26 :     if (VSIMkdir(pszFilenameIn, 0755) != 0)
    1554             :     {
    1555           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to create directory %s.",
    1556             :                  pszFilenameIn);
    1557           0 :         return nullptr;
    1558             :     }
    1559             : 
    1560             :     /* -------------------------------------------------------------------- */
    1561             :     /*      Create the header file.                                         */
    1562             :     /* -------------------------------------------------------------------- */
    1563          26 :     CPLErr CEHeaderCreated = SaveHKVAttribFile(pszFilenameIn, nXSize, nYSize,
    1564             :                                                nBandsIn, eType, FALSE, 0.0);
    1565             : 
    1566          26 :     if (CEHeaderCreated != CE_None)
    1567           0 :         return nullptr;
    1568             : 
    1569             :     /* -------------------------------------------------------------------- */
    1570             :     /*      Create the blob file.                                           */
    1571             :     /* -------------------------------------------------------------------- */
    1572             : 
    1573             :     const char *pszFilename =
    1574          26 :         CPLFormFilename(pszFilenameIn, "image_data", nullptr);
    1575          26 :     FILE *fp = VSIFOpen(pszFilename, "wb");
    1576          26 :     if (fp == nullptr)
    1577             :     {
    1578           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Couldn't create %s.\n",
    1579             :                  pszFilename);
    1580           0 :         return nullptr;
    1581             :     }
    1582             : 
    1583          26 :     bool bOK = VSIFWrite(reinterpret_cast<void *>(const_cast<char *>("")), 1, 1,
    1584          26 :                          fp) == 1;
    1585          26 :     if (VSIFClose(fp) != 0)
    1586           0 :         bOK &= false;
    1587             : 
    1588          26 :     if (!bOK)
    1589           0 :         return nullptr;
    1590             :     /* -------------------------------------------------------------------- */
    1591             :     /*      Open the dataset normally.                                      */
    1592             :     /* -------------------------------------------------------------------- */
    1593          26 :     return GDALDataset::FromHandle(GDALOpen(pszFilenameIn, GA_Update));
    1594             : }
    1595             : 
    1596             : /************************************************************************/
    1597             : /*                               Delete()                               */
    1598             : /*                                                                      */
    1599             : /*      An HKV Blob dataset consists of a bunch of files in a           */
    1600             : /*      directory.  Try to delete all the files, then the               */
    1601             : /*      directory.                                                      */
    1602             : /************************************************************************/
    1603             : 
    1604           1 : CPLErr HKVDataset::Delete(const char *pszName)
    1605             : 
    1606             : {
    1607             :     VSIStatBuf sStat;
    1608           1 :     if (CPLStat(pszName, &sStat) != 0 || !VSI_ISDIR(sStat.st_mode))
    1609             :     {
    1610           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1611             :                  "%s does not appear to be an HKV Dataset, as it is not "
    1612             :                  "a path to a directory.",
    1613             :                  pszName);
    1614           0 :         return CE_Failure;
    1615             :     }
    1616             : 
    1617           1 :     char **papszFiles = VSIReadDir(pszName);
    1618           6 :     for (int i = 0; i < CSLCount(papszFiles); i++)
    1619             :     {
    1620           5 :         if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
    1621           2 :             continue;
    1622             : 
    1623             :         const char *pszTarget =
    1624           3 :             CPLFormFilename(pszName, papszFiles[i], nullptr);
    1625           3 :         if (VSIUnlink(pszTarget) != 0)
    1626             :         {
    1627           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1628             :                      "Unable to delete file %s,"
    1629             :                      "HKVDataset Delete(%s) failed.",
    1630             :                      pszTarget, pszName);
    1631           0 :             CSLDestroy(papszFiles);
    1632           0 :             return CE_Failure;
    1633             :         }
    1634             :     }
    1635             : 
    1636           1 :     CSLDestroy(papszFiles);
    1637             : 
    1638           1 :     if (VSIRmdir(pszName) != 0)
    1639             :     {
    1640           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1641             :                  "Unable to delete directory %s,"
    1642             :                  "HKVDataset Delete() failed.",
    1643             :                  pszName);
    1644           0 :         return CE_Failure;
    1645             :     }
    1646             : 
    1647           1 :     return CE_None;
    1648             : }
    1649             : 
    1650             : /************************************************************************/
    1651             : /*                             CreateCopy()                             */
    1652             : /************************************************************************/
    1653             : 
    1654          20 : GDALDataset *HKVDataset::CreateCopy(const char *pszFilename,
    1655             :                                     GDALDataset *poSrcDS,
    1656             :                                     CPL_UNUSED int bStrict, char **papszOptions,
    1657             :                                     GDALProgressFunc pfnProgress,
    1658             :                                     void *pProgressData)
    1659             : {
    1660          20 :     int nBands = poSrcDS->GetRasterCount();
    1661          20 :     if (nBands == 0)
    1662             :     {
    1663           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1664             :                  "HKV driver does not support source dataset with zero band.");
    1665           1 :         return nullptr;
    1666             :     }
    1667             : 
    1668          19 :     GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    1669             : 
    1670          19 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    1671           0 :         return nullptr;
    1672             : 
    1673             :     /* check that other bands match type- sets type */
    1674             :     /* to unknown if they differ.                  */
    1675          29 :     for (int iBand = 1; iBand < poSrcDS->GetRasterCount(); iBand++)
    1676             :     {
    1677          10 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
    1678          10 :         eType = GDALDataTypeUnion(eType, poBand->GetRasterDataType());
    1679             :     }
    1680             : 
    1681          19 :     HKVDataset *poDS = reinterpret_cast<HKVDataset *>(Create(
    1682             :         pszFilename, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
    1683             :         poSrcDS->GetRasterCount(), eType, papszOptions));
    1684             : 
    1685             :     /* Check that Create worked- return Null if it didn't */
    1686          19 :     if (poDS == nullptr)
    1687           8 :         return nullptr;
    1688             : 
    1689             :     /* -------------------------------------------------------------------- */
    1690             :     /*      Copy the image data.                                            */
    1691             :     /* -------------------------------------------------------------------- */
    1692          11 :     const int nXSize = poDS->GetRasterXSize();
    1693          11 :     const int nYSize = poDS->GetRasterYSize();
    1694             : 
    1695             :     int nBlockXSize, nBlockYSize;
    1696          11 :     poDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1697             : 
    1698          11 :     const int nBlockTotal = ((nXSize + nBlockXSize - 1) / nBlockXSize) *
    1699          11 :                             ((nYSize + nBlockYSize - 1) / nBlockYSize) *
    1700          11 :                             poSrcDS->GetRasterCount();
    1701             : 
    1702          11 :     int nBlocksDone = 0;
    1703          32 :     for (int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++)
    1704             :     {
    1705          21 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
    1706          21 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(iBand + 1);
    1707             : 
    1708             :         /* Get nodata value, if relevant */
    1709          21 :         int pbSuccess = FALSE;
    1710          21 :         double dfSrcNoDataValue = poSrcBand->GetNoDataValue(&pbSuccess);
    1711          21 :         if (pbSuccess)
    1712           0 :             poDS->SetNoDataValue(dfSrcNoDataValue);
    1713             : 
    1714          63 :         void *pData = CPLMalloc(nBlockXSize * nBlockYSize *
    1715          21 :                                 GDALGetDataTypeSize(eType) / 8);
    1716             : 
    1717         241 :         for (int iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize)
    1718             :         {
    1719         440 :             for (int iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize)
    1720             :             {
    1721         220 :                 if (!pfnProgress((nBlocksDone++) /
    1722         220 :                                      static_cast<float>(nBlockTotal),
    1723             :                                  nullptr, pProgressData))
    1724             :                 {
    1725           0 :                     CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1726           0 :                     delete poDS;
    1727           0 :                     CPLFree(pData);
    1728             : 
    1729             :                     GDALDriver *poHKVDriver = reinterpret_cast<GDALDriver *>(
    1730           0 :                         GDALGetDriverByName("MFF2"));
    1731           0 :                     poHKVDriver->Delete(pszFilename);
    1732           0 :                     return nullptr;
    1733             :                 }
    1734             : 
    1735         220 :                 const int nTBXSize = std::min(nBlockXSize, nXSize - iXOffset);
    1736         220 :                 const int nTBYSize = std::min(nBlockYSize, nYSize - iYOffset);
    1737             : 
    1738         220 :                 CPLErr eErr = poSrcBand->RasterIO(
    1739             :                     GF_Read, iXOffset, iYOffset, nTBXSize, nTBYSize, pData,
    1740             :                     nTBXSize, nTBYSize, eType, 0, 0, nullptr);
    1741         220 :                 if (eErr != CE_None)
    1742             :                 {
    1743           0 :                     delete poDS;
    1744           0 :                     CPLFree(pData);
    1745           0 :                     return nullptr;
    1746             :                 }
    1747             : 
    1748         220 :                 eErr = poDstBand->RasterIO(GF_Write, iXOffset, iYOffset,
    1749             :                                            nTBXSize, nTBYSize, pData, nTBXSize,
    1750             :                                            nTBYSize, eType, 0, 0, nullptr);
    1751             : 
    1752         220 :                 if (eErr != CE_None)
    1753             :                 {
    1754           0 :                     delete poDS;
    1755           0 :                     CPLFree(pData);
    1756           0 :                     return nullptr;
    1757             :                 }
    1758             :             }
    1759             :         }
    1760             : 
    1761          21 :         CPLFree(pData);
    1762             :     }
    1763             : 
    1764             :     /* -------------------------------------------------------------------- */
    1765             :     /*      Copy georeferencing information, if enough is available.        */
    1766             :     /*      Only copy geotransform-style info (won't work for slant range). */
    1767             :     /* -------------------------------------------------------------------- */
    1768             : 
    1769             :     double *tempGeoTransform =
    1770          11 :         static_cast<double *>(CPLMalloc(6 * sizeof(double)));
    1771             : 
    1772          22 :     if ((poSrcDS->GetGeoTransform(tempGeoTransform) == CE_None) &&
    1773          11 :         (tempGeoTransform[0] != 0.0 || tempGeoTransform[1] != 1.0 ||
    1774           0 :          tempGeoTransform[2] != 0.0 || tempGeoTransform[3] != 0.0 ||
    1775           0 :          tempGeoTransform[4] != 0.0 || std::abs(tempGeoTransform[5]) != 1.0))
    1776             :     {
    1777          11 :         auto poSrcSRS = poSrcDS->GetSpatialRef();
    1778          11 :         if (poSrcSRS)
    1779             :         {
    1780          11 :             poDS->SetSpatialRef(poSrcSRS);
    1781          11 :             poDS->m_oGCPSRS = *poSrcSRS;
    1782             :         }
    1783          11 :         poDS->SetGeoTransform(tempGeoTransform);
    1784             : 
    1785          11 :         CPLFree(tempGeoTransform);
    1786             : 
    1787             :         // georef file will be saved automatically when dataset is deleted
    1788             :         // because SetProjection sets a flag to indicate it is necessary.
    1789             :     }
    1790             :     else
    1791             :     {
    1792           0 :         CPLFree(tempGeoTransform);
    1793             :     }
    1794             : 
    1795             :     // Make sure image data gets flushed.
    1796          32 :     for (int iBand = 0; iBand < poDS->GetRasterCount(); iBand++)
    1797             :     {
    1798             :         RawRasterBand *poDstBand =
    1799          21 :             reinterpret_cast<RawRasterBand *>(poDS->GetRasterBand(iBand + 1));
    1800          21 :         poDstBand->FlushCache(false);
    1801             :     }
    1802             : 
    1803          11 :     if (!pfnProgress(1.0, nullptr, pProgressData))
    1804             :     {
    1805           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    1806           0 :         delete poDS;
    1807             : 
    1808             :         GDALDriver *poHKVDriver =
    1809           0 :             reinterpret_cast<GDALDriver *>(GDALGetDriverByName("MFF2"));
    1810           0 :         poHKVDriver->Delete(pszFilename);
    1811           0 :         return nullptr;
    1812             :     }
    1813             : 
    1814          11 :     poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    1815             : 
    1816          11 :     return poDS;
    1817             : }
    1818             : 
    1819             : /************************************************************************/
    1820             : /*                         GDALRegister_HKV()                           */
    1821             : /************************************************************************/
    1822             : 
    1823        1595 : void GDALRegister_HKV()
    1824             : 
    1825             : {
    1826        1595 :     if (GDALGetDriverByName("MFF2") != nullptr)
    1827         302 :         return;
    1828             : 
    1829        1293 :     GDALDriver *poDriver = new GDALDriver();
    1830             : 
    1831        1293 :     poDriver->SetDescription("MFF2");
    1832        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1833        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Vexcel MFF2 (HKV) Raster");
    1834        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/mff2.html");
    1835        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    1836             :                               "Byte Int16 UInt16 Int32 UInt32 CInt16 "
    1837        1293 :                               "CInt32 Float32 Float64 CFloat32 CFloat64");
    1838             : 
    1839        1293 :     poDriver->pfnOpen = HKVDataset::Open;
    1840        1293 :     poDriver->pfnCreate = HKVDataset::Create;
    1841        1293 :     poDriver->pfnDelete = HKVDataset::Delete;
    1842        1293 :     poDriver->pfnCreateCopy = HKVDataset::CreateCopy;
    1843             : 
    1844        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1845             : }

Generated by: LCOV version 1.14