LCOV - code coverage report
Current view: top level - frmts/raw - hkvdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 285 401 71.1 %
Date: 2025-03-28 21:34:50 Functions: 14 19 73.7 %

          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           2 :     ~HKVRasterBand() override
      44           1 :     {
      45           2 :     }
      46             : };
      47             : 
      48             : /************************************************************************/
      49             : /*                      HKV Spheroids                                   */
      50             : /************************************************************************/
      51             : 
      52             : class HKVSpheroidList : public SpheroidList
      53             : {
      54             :   public:
      55             :     HKVSpheroidList();
      56             : 
      57           1 :     ~HKVSpheroidList()
      58           1 :     {
      59           1 :     }
      60             : };
      61             : 
      62           1 : HKVSpheroidList ::HKVSpheroidList()
      63             : {
      64           1 :     num_spheroids = 58;
      65           1 :     epsilonR = 0.1;
      66           1 :     epsilonI = 0.000001;
      67             : 
      68           1 :     spheroids[0].SetValuesByEqRadiusAndInvFlattening("airy-1830", 6377563.396,
      69             :                                                      299.3249646);
      70           1 :     spheroids[1].SetValuesByEqRadiusAndInvFlattening("modified-airy",
      71             :                                                      6377340.189, 299.3249646);
      72           1 :     spheroids[2].SetValuesByEqRadiusAndInvFlattening("australian-national",
      73             :                                                      6378160, 298.25);
      74           1 :     spheroids[3].SetValuesByEqRadiusAndInvFlattening("bessel-1841-namibia",
      75             :                                                      6377483.865, 299.1528128);
      76           1 :     spheroids[4].SetValuesByEqRadiusAndInvFlattening("bessel-1841", 6377397.155,
      77             :                                                      299.1528128);
      78           1 :     spheroids[5].SetValuesByEqRadiusAndInvFlattening("clarke-1858", 6378294.0,
      79             :                                                      294.297);
      80           1 :     spheroids[6].SetValuesByEqRadiusAndInvFlattening("clarke-1866", 6378206.4,
      81             :                                                      294.9786982);
      82           1 :     spheroids[7].SetValuesByEqRadiusAndInvFlattening("clarke-1880", 6378249.145,
      83             :                                                      293.465);
      84           1 :     spheroids[8].SetValuesByEqRadiusAndInvFlattening("everest-india-1830",
      85             :                                                      6377276.345, 300.8017);
      86           1 :     spheroids[9].SetValuesByEqRadiusAndInvFlattening("everest-sabah-sarawak",
      87             :                                                      6377298.556, 300.8017);
      88           1 :     spheroids[10].SetValuesByEqRadiusAndInvFlattening("everest-india-1956",
      89             :                                                       6377301.243, 300.8017);
      90           1 :     spheroids[11].SetValuesByEqRadiusAndInvFlattening("everest-malaysia-1969",
      91             :                                                       6377295.664, 300.8017);
      92           1 :     spheroids[12].SetValuesByEqRadiusAndInvFlattening("everest-malay-sing",
      93             :                                                       6377304.063, 300.8017);
      94           1 :     spheroids[13].SetValuesByEqRadiusAndInvFlattening("everest-pakistan",
      95             :                                                       6377309.613, 300.8017);
      96           1 :     spheroids[14].SetValuesByEqRadiusAndInvFlattening("modified-fisher-1960",
      97             :                                                       6378155, 298.3);
      98           1 :     spheroids[15].SetValuesByEqRadiusAndInvFlattening("helmert-1906", 6378200,
      99             :                                                       298.3);
     100           1 :     spheroids[16].SetValuesByEqRadiusAndInvFlattening("hough-1960", 6378270,
     101             :                                                       297);
     102           1 :     spheroids[17].SetValuesByEqRadiusAndInvFlattening("hughes", 6378273.0,
     103             :                                                       298.279);
     104           1 :     spheroids[18].SetValuesByEqRadiusAndInvFlattening("indonesian-1974",
     105             :                                                       6378160, 298.247);
     106           1 :     spheroids[19].SetValuesByEqRadiusAndInvFlattening("international-1924",
     107             :                                                       6378388, 297);
     108           1 :     spheroids[20].SetValuesByEqRadiusAndInvFlattening("iugc-67", 6378160.0,
     109             :                                                       298.254);
     110           1 :     spheroids[21].SetValuesByEqRadiusAndInvFlattening("iugc-75", 6378140.0,
     111             :                                                       298.25298);
     112           1 :     spheroids[22].SetValuesByEqRadiusAndInvFlattening("krassovsky-1940",
     113             :                                                       6378245, 298.3);
     114           1 :     spheroids[23].SetValuesByEqRadiusAndInvFlattening("kaula", 6378165.0,
     115             :                                                       292.308);
     116           1 :     spheroids[24].SetValuesByEqRadiusAndInvFlattening("grs-80", 6378137,
     117             :                                                       298.257222101);
     118           1 :     spheroids[25].SetValuesByEqRadiusAndInvFlattening("south-american-1969",
     119             :                                                       6378160, 298.25);
     120           1 :     spheroids[26].SetValuesByEqRadiusAndInvFlattening("wgs-72", 6378135,
     121             :                                                       298.26);
     122           1 :     spheroids[27].SetValuesByEqRadiusAndInvFlattening("wgs-84", 6378137,
     123             :                                                       298.257223563);
     124           1 :     spheroids[28].SetValuesByEqRadiusAndInvFlattening("ev-wgs-84", 6378137.0,
     125             :                                                       298.252841);
     126           1 :     spheroids[29].SetValuesByEqRadiusAndInvFlattening("ev-bessel", 6377397.0,
     127             :                                                       299.1976073);
     128             : 
     129           1 :     spheroids[30].SetValuesByEqRadiusAndInvFlattening("airy_1830", 6377563.396,
     130             :                                                       299.3249646);
     131           1 :     spheroids[31].SetValuesByEqRadiusAndInvFlattening("modified_airy",
     132             :                                                       6377340.189, 299.3249646);
     133           1 :     spheroids[32].SetValuesByEqRadiusAndInvFlattening("australian_national",
     134             :                                                       6378160, 298.25);
     135           1 :     spheroids[33].SetValuesByEqRadiusAndInvFlattening("bessel_1841_namibia",
     136             :                                                       6377483.865, 299.1528128);
     137           1 :     spheroids[34].SetValuesByEqRadiusAndInvFlattening("bessel_1841",
     138             :                                                       6377397.155, 299.1528128);
     139           1 :     spheroids[35].SetValuesByEqRadiusAndInvFlattening("clarke_1858", 6378294.0,
     140             :                                                       294.297);
     141           1 :     spheroids[36].SetValuesByEqRadiusAndInvFlattening("clarke_1866", 6378206.4,
     142             :                                                       294.9786982);
     143           1 :     spheroids[37].SetValuesByEqRadiusAndInvFlattening("clarke_1880",
     144             :                                                       6378249.145, 293.465);
     145           1 :     spheroids[38].SetValuesByEqRadiusAndInvFlattening("everest_india_1830",
     146             :                                                       6377276.345, 300.8017);
     147           1 :     spheroids[39].SetValuesByEqRadiusAndInvFlattening("everest_sabah_sarawak",
     148             :                                                       6377298.556, 300.8017);
     149           1 :     spheroids[40].SetValuesByEqRadiusAndInvFlattening("everest_india_1956",
     150             :                                                       6377301.243, 300.8017);
     151           1 :     spheroids[41].SetValuesByEqRadiusAndInvFlattening("everest_malaysia_1969",
     152             :                                                       6377295.664, 300.8017);
     153           1 :     spheroids[42].SetValuesByEqRadiusAndInvFlattening("everest_malay_sing",
     154             :                                                       6377304.063, 300.8017);
     155           1 :     spheroids[43].SetValuesByEqRadiusAndInvFlattening("everest_pakistan",
     156             :                                                       6377309.613, 300.8017);
     157           1 :     spheroids[44].SetValuesByEqRadiusAndInvFlattening("modified_fisher_1960",
     158             :                                                       6378155, 298.3);
     159           1 :     spheroids[45].SetValuesByEqRadiusAndInvFlattening("helmert_1906", 6378200,
     160             :                                                       298.3);
     161           1 :     spheroids[46].SetValuesByEqRadiusAndInvFlattening("hough_1960", 6378270,
     162             :                                                       297);
     163           1 :     spheroids[47].SetValuesByEqRadiusAndInvFlattening("indonesian_1974",
     164             :                                                       6378160, 298.247);
     165           1 :     spheroids[48].SetValuesByEqRadiusAndInvFlattening("international_1924",
     166             :                                                       6378388, 297);
     167           1 :     spheroids[49].SetValuesByEqRadiusAndInvFlattening("iugc_67", 6378160.0,
     168             :                                                       298.254);
     169           1 :     spheroids[50].SetValuesByEqRadiusAndInvFlattening("iugc_75", 6378140.0,
     170             :                                                       298.25298);
     171           1 :     spheroids[51].SetValuesByEqRadiusAndInvFlattening("krassovsky_1940",
     172             :                                                       6378245, 298.3);
     173           1 :     spheroids[52].SetValuesByEqRadiusAndInvFlattening("grs_80", 6378137,
     174             :                                                       298.257222101);
     175           1 :     spheroids[53].SetValuesByEqRadiusAndInvFlattening("south_american_1969",
     176             :                                                       6378160, 298.25);
     177           1 :     spheroids[54].SetValuesByEqRadiusAndInvFlattening("wgs_72", 6378135,
     178             :                                                       298.26);
     179           1 :     spheroids[55].SetValuesByEqRadiusAndInvFlattening("wgs_84", 6378137,
     180             :                                                       298.257223563);
     181           1 :     spheroids[56].SetValuesByEqRadiusAndInvFlattening("ev_wgs_84", 6378137.0,
     182             :                                                       298.252841);
     183           1 :     spheroids[57].SetValuesByEqRadiusAndInvFlattening("ev_bessel", 6377397.0,
     184             :                                                       299.1976073);
     185           1 : }
     186             : 
     187             : /************************************************************************/
     188             : /* ==================================================================== */
     189             : /*                              HKVDataset                              */
     190             : /* ==================================================================== */
     191             : /************************************************************************/
     192             : 
     193             : class HKVDataset final : public RawDataset
     194             : {
     195             :     friend class HKVRasterBand;
     196             : 
     197             :     char *pszPath;
     198             :     VSILFILE *fpBlob;
     199             : 
     200             :     int nGCPCount;
     201             :     GDAL_GCP *pasGCPList;
     202             : 
     203             :     void ProcessGeoref(const char *);
     204             :     void ProcessGeorefGCP(char **, const char *, double, double);
     205             : 
     206           1 :     void SetVersion(float version_number)
     207             :     {
     208             :         // Update stored info.
     209           1 :         MFF2version = version_number;
     210           1 :     }
     211             : 
     212             :     float MFF2version;
     213             : 
     214             :     GDALDataType eRasterType;
     215             : 
     216             :     void SetNoDataValue(double);
     217             : 
     218             :     OGRSpatialReference m_oSRS{};
     219             :     OGRSpatialReference m_oGCPSRS{};
     220             :     double adfGeoTransform[6];
     221             : 
     222             :     char **papszAttrib;
     223             : 
     224             :     char **papszGeoref;
     225             : 
     226             :     // NOTE: The MFF2 format goes against GDAL's API in that nodata values are
     227             :     // set per-dataset rather than per-band.  To compromise, for writing out,
     228             :     // the dataset's nodata value will be set to the last value set on any of
     229             :     // the raster bands.
     230             : 
     231             :     bool bNoDataSet;
     232             :     double dfNoDataValue;
     233             : 
     234             :     CPL_DISALLOW_COPY_ASSIGN(HKVDataset)
     235             : 
     236             :     CPLErr Close() override;
     237             : 
     238             :   public:
     239             :     HKVDataset();
     240             :     ~HKVDataset() override;
     241             : 
     242           0 :     int GetGCPCount() override /* const */
     243             :     {
     244           0 :         return nGCPCount;
     245             :     }
     246             : 
     247           0 :     const OGRSpatialReference *GetGCPSpatialRef() const override
     248             :     {
     249           0 :         return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
     250             :     }
     251             : 
     252             :     const GDAL_GCP *GetGCPs() override;
     253             : 
     254           0 :     const OGRSpatialReference *GetSpatialRef() const override
     255             :     {
     256           0 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     257             :     }
     258             : 
     259             :     CPLErr GetGeoTransform(double *) override;
     260             : 
     261             :     static GDALDataset *Open(GDALOpenInfo *);
     262             : };
     263             : 
     264             : /************************************************************************/
     265             : /* ==================================================================== */
     266             : /*                            HKVRasterBand                             */
     267             : /* ==================================================================== */
     268             : /************************************************************************/
     269             : 
     270             : /************************************************************************/
     271             : /*                           HKVRasterBand()                            */
     272             : /************************************************************************/
     273             : 
     274           1 : HKVRasterBand::HKVRasterBand(HKVDataset *poDSIn, int nBandIn, VSILFILE *fpRawIn,
     275             :                              unsigned int nImgOffsetIn, int nPixelOffsetIn,
     276             :                              int nLineOffsetIn, GDALDataType eDataTypeIn,
     277           1 :                              int bNativeOrderIn)
     278             :     : RawRasterBand(GDALDataset::FromHandle(poDSIn), nBandIn, fpRawIn,
     279             :                     nImgOffsetIn, nPixelOffsetIn, nLineOffsetIn, eDataTypeIn,
     280           1 :                     bNativeOrderIn, RawRasterBand::OwnFP::NO)
     281             : 
     282             : {
     283           1 :     poDS = poDSIn;
     284           1 :     nBand = nBandIn;
     285             : 
     286           1 :     nBlockXSize = poDS->GetRasterXSize();
     287           1 :     nBlockYSize = 1;
     288           1 : }
     289             : 
     290             : /************************************************************************/
     291             : /* ==================================================================== */
     292             : /*                              HKVDataset                              */
     293             : /* ==================================================================== */
     294             : /************************************************************************/
     295             : 
     296             : /************************************************************************/
     297             : /*                            HKVDataset()                             */
     298             : /************************************************************************/
     299             : 
     300           1 : HKVDataset::HKVDataset()
     301             :     : pszPath(nullptr), fpBlob(nullptr), nGCPCount(0), pasGCPList(nullptr),
     302             :       // Initialize datasets to new version; change if necessary.
     303             :       MFF2version(1.1f), eRasterType(GDT_Unknown), papszAttrib(nullptr),
     304           1 :       papszGeoref(nullptr), bNoDataSet(false), dfNoDataValue(0.0)
     305             : {
     306           1 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     307           1 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     308           1 :     adfGeoTransform[0] = 0.0;
     309           1 :     adfGeoTransform[1] = 1.0;
     310           1 :     adfGeoTransform[2] = 0.0;
     311           1 :     adfGeoTransform[3] = 0.0;
     312           1 :     adfGeoTransform[4] = 0.0;
     313           1 :     adfGeoTransform[5] = 1.0;
     314           1 : }
     315             : 
     316             : /************************************************************************/
     317             : /*                            ~HKVDataset()                            */
     318             : /************************************************************************/
     319             : 
     320           2 : HKVDataset::~HKVDataset()
     321             : 
     322             : {
     323           1 :     HKVDataset::Close();
     324           2 : }
     325             : 
     326             : /************************************************************************/
     327             : /*                              Close()                                 */
     328             : /************************************************************************/
     329             : 
     330           2 : CPLErr HKVDataset::Close()
     331             : {
     332           2 :     CPLErr eErr = CE_None;
     333           2 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     334             :     {
     335           1 :         if (HKVDataset::FlushCache(true) != CE_None)
     336           0 :             eErr = CE_Failure;
     337             : 
     338           1 :         if (fpBlob)
     339             :         {
     340           1 :             if (VSIFCloseL(fpBlob) != 0)
     341             :             {
     342           0 :                 eErr = CE_Failure;
     343           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     344             :             }
     345             :         }
     346             : 
     347           1 :         if (nGCPCount > 0)
     348             :         {
     349           1 :             GDALDeinitGCPs(nGCPCount, pasGCPList);
     350           1 :             CPLFree(pasGCPList);
     351             :         }
     352             : 
     353           1 :         CPLFree(pszPath);
     354           1 :         CSLDestroy(papszGeoref);
     355           1 :         CSLDestroy(papszAttrib);
     356             : 
     357           1 :         if (GDALPamDataset::Close() != CE_None)
     358           0 :             eErr = CE_Failure;
     359             :     }
     360           2 :     return eErr;
     361             : }
     362             : 
     363             : /************************************************************************/
     364             : /*                          GetGeoTransform()                           */
     365             : /************************************************************************/
     366             : 
     367           0 : CPLErr HKVDataset::GetGeoTransform(double *padfTransform)
     368             : 
     369             : {
     370           0 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     371           0 :     return CE_None;
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*                               GetGCP()                               */
     376             : /************************************************************************/
     377             : 
     378           0 : const GDAL_GCP *HKVDataset::GetGCPs()
     379             : 
     380             : {
     381           0 :     return pasGCPList;
     382             : }
     383             : 
     384             : /************************************************************************/
     385             : /*                          ProcessGeorefGCP()                          */
     386             : /************************************************************************/
     387             : 
     388           5 : void HKVDataset::ProcessGeorefGCP(char **papszGeorefIn, const char *pszBase,
     389             :                                   double dfRasterX, double dfRasterY)
     390             : 
     391             : {
     392             :     /* -------------------------------------------------------------------- */
     393             :     /*      Fetch the GCP from the string list.                             */
     394             :     /* -------------------------------------------------------------------- */
     395           5 :     char szFieldName[128] = {'\0'};
     396           5 :     snprintf(szFieldName, sizeof(szFieldName), "%s.latitude", pszBase);
     397           5 :     double dfLat = 0.0;
     398           5 :     if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
     399           0 :         return;
     400             :     else
     401           5 :         dfLat = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
     402             : 
     403           5 :     snprintf(szFieldName, sizeof(szFieldName), "%s.longitude", pszBase);
     404           5 :     double dfLong = 0.0;
     405           5 :     if (CSLFetchNameValue(papszGeorefIn, szFieldName) == nullptr)
     406           0 :         return;
     407             :     else
     408           5 :         dfLong = CPLAtof(CSLFetchNameValue(papszGeorefIn, szFieldName));
     409             : 
     410             :     /* -------------------------------------------------------------------- */
     411             :     /*      Add the gcp to the internal list.                               */
     412             :     /* -------------------------------------------------------------------- */
     413           5 :     GDALInitGCPs(1, pasGCPList + nGCPCount);
     414             : 
     415           5 :     CPLFree(pasGCPList[nGCPCount].pszId);
     416             : 
     417           5 :     pasGCPList[nGCPCount].pszId = CPLStrdup(pszBase);
     418             : 
     419           5 :     pasGCPList[nGCPCount].dfGCPX = dfLong;
     420           5 :     pasGCPList[nGCPCount].dfGCPY = dfLat;
     421           5 :     pasGCPList[nGCPCount].dfGCPZ = 0.0;
     422             : 
     423           5 :     pasGCPList[nGCPCount].dfGCPPixel = dfRasterX;
     424           5 :     pasGCPList[nGCPCount].dfGCPLine = dfRasterY;
     425             : 
     426           5 :     nGCPCount++;
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                           ProcessGeoref()                            */
     431             : /************************************************************************/
     432             : 
     433           1 : void HKVDataset::ProcessGeoref(const char *pszFilename)
     434             : 
     435             : {
     436             :     /* -------------------------------------------------------------------- */
     437             :     /*      Load the georef file, and boil white space away from around     */
     438             :     /*      the equal sign.                                                 */
     439             :     /* -------------------------------------------------------------------- */
     440           1 :     CSLDestroy(papszGeoref);
     441           1 :     papszGeoref = CSLLoad(pszFilename);
     442           1 :     if (papszGeoref == nullptr)
     443           0 :         return;
     444             : 
     445           1 :     HKVSpheroidList *hkvEllipsoids = new HKVSpheroidList;
     446             : 
     447          14 :     for (int i = 0; papszGeoref[i] != nullptr; i++)
     448             :     {
     449          13 :         int iDst = 0;
     450          13 :         char *pszLine = papszGeoref[i];
     451             : 
     452         433 :         for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
     453             :         {
     454         420 :             if (pszLine[iSrc] != ' ')
     455             :             {
     456         420 :                 pszLine[iDst++] = pszLine[iSrc];
     457             :             }
     458             :         }
     459          13 :         pszLine[iDst] = '\0';
     460             :     }
     461             : 
     462             :     /* -------------------------------------------------------------------- */
     463             :     /*      Try to get GCPs, in lat/longs                     .             */
     464             :     /* -------------------------------------------------------------------- */
     465           1 :     nGCPCount = 0;
     466           1 :     pasGCPList = reinterpret_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 5));
     467             : 
     468           1 :     if (MFF2version > 1.0)
     469             :     {
     470           1 :         ProcessGeorefGCP(papszGeoref, "top_left", 0, 0);
     471           1 :         ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize(), 0);
     472           1 :         ProcessGeorefGCP(papszGeoref, "bottom_left", 0, GetRasterYSize());
     473           1 :         ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize(),
     474           1 :                          GetRasterYSize());
     475           1 :         ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
     476           1 :                          GetRasterYSize() / 2.0);
     477             :     }
     478             :     else
     479             :     {
     480           0 :         ProcessGeorefGCP(papszGeoref, "top_left", 0.5, 0.5);
     481           0 :         ProcessGeorefGCP(papszGeoref, "top_right", GetRasterXSize() - 0.5, 0.5);
     482           0 :         ProcessGeorefGCP(papszGeoref, "bottom_left", 0.5,
     483           0 :                          GetRasterYSize() - 0.5);
     484           0 :         ProcessGeorefGCP(papszGeoref, "bottom_right", GetRasterXSize() - 0.5,
     485           0 :                          GetRasterYSize() - 0.5);
     486           0 :         ProcessGeorefGCP(papszGeoref, "centre", GetRasterXSize() / 2.0,
     487           0 :                          GetRasterYSize() / 2.0);
     488             :     }
     489             : 
     490           1 :     if (nGCPCount == 0)
     491             :     {
     492           0 :         CPLFree(pasGCPList);
     493           0 :         pasGCPList = nullptr;
     494             :     }
     495             : 
     496             :     /* -------------------------------------------------------------------- */
     497             :     /*      Do we have a recognised projection?                             */
     498             :     /* -------------------------------------------------------------------- */
     499           1 :     const char *pszProjName = CSLFetchNameValue(papszGeoref, "projection.name");
     500             :     const char *pszOriginLong =
     501           1 :         CSLFetchNameValue(papszGeoref, "projection.origin_longitude");
     502             :     const char *pszSpheroidName =
     503           1 :         CSLFetchNameValue(papszGeoref, "spheroid.name");
     504             : 
     505           2 :     if (pszSpheroidName != nullptr &&
     506           1 :         hkvEllipsoids->SpheroidInList(pszSpheroidName))
     507             :     {
     508             : #if 0
     509             :       // TODO(schwehr): Enable in trunk after 2.1 branch and fix.
     510             :       // Breaks tests on some platforms.
     511             :       CPLError( CE_Failure, CPLE_AppDefined,
     512             :                 "Unrecognized ellipsoid.  Not handled.  "
     513             :                 "Spheroid name not in spheroid list: '%s'",
     514             :                 pszSpheroidName );
     515             : #endif
     516             :         // Why were eq_radius and inv_flattening never used?
     517             :         // eq_radius = hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName);
     518             :         // inv_flattening =
     519             :         //     hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName);
     520             :     }
     521           0 :     else if (pszProjName != nullptr)
     522             :     {
     523           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     524             :                  "Unrecognized ellipsoid.  Not handled.");
     525             :         // TODO(schwehr): This error is was never what was happening.
     526             :         // CPLError( CE_Warning, CPLE_AppDefined,
     527             :         //           "Unrecognized ellipsoid.  Using wgs-84 parameters.");
     528             :         // eq_radius=hkvEllipsoids->GetSpheroidEqRadius("wgs-84"); */
     529             :         // inv_flattening=hkvEllipsoids->GetSpheroidInverseFlattening("wgs-84");
     530             :     }
     531             : 
     532           1 :     if (pszProjName != nullptr && EQUAL(pszProjName, "utm") && nGCPCount == 5)
     533             :     {
     534             :         // int nZone = (int)((CPLAtof(pszOriginLong)+184.5) / 6.0);
     535           1 :         int nZone = 31;  // TODO(schwehr): Where does 31 come from?
     536             : 
     537           1 :         if (pszOriginLong == nullptr)
     538             :         {
     539             :             // If origin not specified, assume 0.0.
     540           0 :             CPLError(
     541             :                 CE_Warning, CPLE_AppDefined,
     542             :                 "No projection origin longitude specified.  Assuming 0.0.");
     543             :         }
     544             :         else
     545             :         {
     546           1 :             nZone = 31 + static_cast<int>(floor(CPLAtof(pszOriginLong) / 6.0));
     547             :         }
     548             : 
     549           2 :         OGRSpatialReference oUTM;
     550             : 
     551           1 :         if (pasGCPList[4].dfGCPY < 0)
     552           0 :             oUTM.SetUTM(nZone, 0);
     553             :         else
     554           1 :             oUTM.SetUTM(nZone, 1);
     555             : 
     556           2 :         OGRSpatialReference oLL;
     557           1 :         oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     558           1 :         if (pszOriginLong != nullptr)
     559             :         {
     560           1 :             oUTM.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, CPLAtof(pszOriginLong));
     561           1 :             oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
     562             :         }
     563             : 
     564           1 :         if ((pszSpheroidName == nullptr) ||
     565           1 :             (EQUAL(pszSpheroidName, "wgs-84")) ||
     566           1 :             (EQUAL(pszSpheroidName, "wgs_84")))
     567             :         {
     568           0 :             oUTM.SetWellKnownGeogCS("WGS84");
     569           0 :             oLL.SetWellKnownGeogCS("WGS84");
     570             :         }
     571             :         else
     572             :         {
     573           1 :             if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
     574             :             {
     575           1 :                 oUTM.SetGeogCS(
     576             :                     "unknown", "unknown", pszSpheroidName,
     577             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
     578             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
     579             :                         pszSpheroidName));
     580           1 :                 oLL.SetGeogCS(
     581             :                     "unknown", "unknown", pszSpheroidName,
     582             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
     583             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
     584             :                         pszSpheroidName));
     585             :             }
     586             :             else
     587             :             {
     588           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     589             :                          "Unrecognized ellipsoid.  Using wgs-84 parameters.");
     590           0 :                 oUTM.SetWellKnownGeogCS("WGS84");
     591           0 :                 oLL.SetWellKnownGeogCS("WGS84");
     592             :             }
     593             :         }
     594             : 
     595             :         OGRCoordinateTransformation *poTransform =
     596           1 :             OGRCreateCoordinateTransformation(&oLL, &oUTM);
     597             : 
     598           1 :         bool bSuccess = true;
     599           1 :         if (poTransform == nullptr)
     600             :         {
     601           0 :             CPLErrorReset();
     602           0 :             bSuccess = false;
     603             :         }
     604             : 
     605           1 :         double dfUtmX[5] = {0.0};
     606           1 :         double dfUtmY[5] = {0.0};
     607             : 
     608           1 :         if (poTransform != nullptr)
     609             :         {
     610           6 :             for (int gcp_index = 0; gcp_index < 5; gcp_index++)
     611             :             {
     612           5 :                 dfUtmX[gcp_index] = pasGCPList[gcp_index].dfGCPX;
     613           5 :                 dfUtmY[gcp_index] = pasGCPList[gcp_index].dfGCPY;
     614             : 
     615           5 :                 if (bSuccess && !poTransform->Transform(1, &(dfUtmX[gcp_index]),
     616             :                                                         &(dfUtmY[gcp_index])))
     617           0 :                     bSuccess = false;
     618             :             }
     619             :         }
     620             : 
     621           1 :         if (bSuccess)
     622             :         {
     623             :             // Update GCPS to proper projection.
     624           6 :             for (int gcp_index = 0; gcp_index < 5; gcp_index++)
     625             :             {
     626           5 :                 pasGCPList[gcp_index].dfGCPX = dfUtmX[gcp_index];
     627           5 :                 pasGCPList[gcp_index].dfGCPY = dfUtmY[gcp_index];
     628             :             }
     629             : 
     630           1 :             m_oGCPSRS = oUTM;
     631             : 
     632           1 :             bool transform_ok = CPL_TO_BOOL(
     633           1 :                 GDALGCPsToGeoTransform(5, pasGCPList, adfGeoTransform, 0));
     634             : 
     635           1 :             if (!transform_ok)
     636             :             {
     637             :                 // Transform may not be sufficient in all cases (slant range
     638             :                 // projection).
     639           0 :                 adfGeoTransform[0] = 0.0;
     640           0 :                 adfGeoTransform[1] = 1.0;
     641           0 :                 adfGeoTransform[2] = 0.0;
     642           0 :                 adfGeoTransform[3] = 0.0;
     643           0 :                 adfGeoTransform[4] = 0.0;
     644           0 :                 adfGeoTransform[5] = 1.0;
     645           0 :                 m_oGCPSRS.Clear();
     646             :             }
     647             :             else
     648             :             {
     649           1 :                 m_oSRS = std::move(oUTM);
     650             :             }
     651             :         }
     652             : 
     653           1 :         if (poTransform != nullptr)
     654           2 :             delete poTransform;
     655             :     }
     656           0 :     else if (pszProjName != nullptr && nGCPCount == 5)
     657             :     {
     658           0 :         OGRSpatialReference oLL;
     659           0 :         oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     660             : 
     661           0 :         if (pszOriginLong != nullptr)
     662             :         {
     663           0 :             oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, CPLAtof(pszOriginLong));
     664             :         }
     665             : 
     666           0 :         if (pszSpheroidName == nullptr ||
     667           0 :             EQUAL(pszSpheroidName, "wgs-84") ||  // Dash.
     668           0 :             EQUAL(pszSpheroidName, "wgs_84"))    // Underscore.
     669             :         {
     670           0 :             oLL.SetWellKnownGeogCS("WGS84");
     671             :         }
     672             :         else
     673             :         {
     674           0 :             if (hkvEllipsoids->SpheroidInList(pszSpheroidName))
     675             :             {
     676           0 :                 oLL.SetGeogCS(
     677             :                     "", "", pszSpheroidName,
     678             :                     hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName),
     679             :                     hkvEllipsoids->GetSpheroidInverseFlattening(
     680             :                         pszSpheroidName));
     681             :             }
     682             :             else
     683             :             {
     684           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     685             :                          "Unrecognized ellipsoid.  "
     686             :                          "Using wgs-84 parameters.");
     687           0 :                 oLL.SetWellKnownGeogCS("WGS84");
     688             :             }
     689             :         }
     690             : 
     691           0 :         const bool transform_ok = CPL_TO_BOOL(
     692           0 :             GDALGCPsToGeoTransform(5, pasGCPList, adfGeoTransform, 0));
     693             : 
     694           0 :         m_oSRS.Clear();
     695             : 
     696           0 :         if (!transform_ok)
     697             :         {
     698           0 :             adfGeoTransform[0] = 0.0;
     699           0 :             adfGeoTransform[1] = 1.0;
     700           0 :             adfGeoTransform[2] = 0.0;
     701           0 :             adfGeoTransform[3] = 0.0;
     702           0 :             adfGeoTransform[4] = 0.0;
     703           0 :             adfGeoTransform[5] = 1.0;
     704             :         }
     705             :         else
     706             :         {
     707           0 :             m_oSRS = oLL;
     708             :         }
     709             : 
     710           0 :         m_oGCPSRS = std::move(oLL);
     711             :     }
     712             : 
     713           1 :     delete hkvEllipsoids;
     714             : }
     715             : 
     716             : /************************************************************************/
     717             : /*                                Open()                                */
     718             : /************************************************************************/
     719             : 
     720       29531 : GDALDataset *HKVDataset::Open(GDALOpenInfo *poOpenInfo)
     721             : 
     722             : {
     723             :     /* -------------------------------------------------------------------- */
     724             :     /*      We assume the dataset is passed as a directory.  Check for      */
     725             :     /*      an attrib and blob file as a minimum.                           */
     726             :     /* -------------------------------------------------------------------- */
     727       29531 :     if (!poOpenInfo->bIsDirectory)
     728       29297 :         return nullptr;
     729             : 
     730             :     std::string osFilename =
     731         468 :         CPLFormFilenameSafe(poOpenInfo->pszFilename, "image_data", nullptr);
     732             :     VSIStatBuf sStat;
     733         234 :     if (VSIStat(osFilename.c_str(), &sStat) != 0)
     734             :         osFilename =
     735         233 :             CPLFormFilenameSafe(poOpenInfo->pszFilename, "blob", nullptr);
     736         234 :     if (VSIStat(osFilename.c_str(), &sStat) != 0)
     737         233 :         return nullptr;
     738             : 
     739             :     osFilename =
     740           1 :         CPLFormFilenameSafe(poOpenInfo->pszFilename, "attrib", nullptr);
     741           1 :     if (VSIStat(osFilename.c_str(), &sStat) != 0)
     742           0 :         return nullptr;
     743             : 
     744             :     /* -------------------------------------------------------------------- */
     745             :     /*      Load the attrib file, and boil white space away from around     */
     746             :     /*      the equal sign.                                                 */
     747             :     /* -------------------------------------------------------------------- */
     748           1 :     char **papszAttrib = CSLLoad(osFilename.c_str());
     749           1 :     if (papszAttrib == nullptr)
     750           0 :         return nullptr;
     751             : 
     752          10 :     for (int i = 0; papszAttrib[i] != nullptr; i++)
     753             :     {
     754           9 :         int iDst = 0;
     755           9 :         char *pszLine = papszAttrib[i];
     756             : 
     757         252 :         for (int iSrc = 0; pszLine[iSrc] != '\0'; iSrc++)
     758             :         {
     759         243 :             if (pszLine[iSrc] != ' ')
     760             :             {
     761         211 :                 pszLine[iDst++] = pszLine[iSrc];
     762             :             }
     763             :         }
     764           9 :         pszLine[iDst] = '\0';
     765             :     }
     766             : 
     767             :     /* -------------------------------------------------------------------- */
     768             :     /*      Create a corresponding GDALDataset.                             */
     769             :     /* -------------------------------------------------------------------- */
     770           2 :     auto poDS = std::make_unique<HKVDataset>();
     771             : 
     772           1 :     poDS->pszPath = CPLStrdup(poOpenInfo->pszFilename);
     773           1 :     poDS->papszAttrib = papszAttrib;
     774             : 
     775           1 :     poDS->eAccess = poOpenInfo->eAccess;
     776             : 
     777             :     /* -------------------------------------------------------------------- */
     778             :     /*      Set some dataset wide information.                              */
     779             :     /* -------------------------------------------------------------------- */
     780           1 :     bool bNative = false;
     781           1 :     bool bComplex = false;
     782           1 :     int nRawBands = 0;
     783             : 
     784           2 :     if (CSLFetchNameValue(papszAttrib, "extent.cols") == nullptr ||
     785           1 :         CSLFetchNameValue(papszAttrib, "extent.rows") == nullptr)
     786             :     {
     787           0 :         return nullptr;
     788             :     }
     789             : 
     790           1 :     poDS->nRasterXSize = atoi(CSLFetchNameValue(papszAttrib, "extent.cols"));
     791           1 :     poDS->nRasterYSize = atoi(CSLFetchNameValue(papszAttrib, "extent.rows"));
     792             : 
     793           1 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     794             :     {
     795           0 :         return nullptr;
     796             :     }
     797             : 
     798           1 :     const char *pszValue = CSLFetchNameValue(papszAttrib, "pixel.order");
     799           1 :     if (pszValue == nullptr)
     800           0 :         bNative = true;
     801             :     else
     802             :     {
     803             : #ifdef CPL_MSB
     804             :         bNative = strstr(pszValue, "*msbf") != NULL;
     805             : #else
     806           1 :         bNative = strstr(pszValue, "*lsbf") != nullptr;
     807             : #endif
     808             :     }
     809             : 
     810           1 :     bool bNoDataSet = false;
     811           1 :     double dfNoDataValue = 0.0;
     812           1 :     pszValue = CSLFetchNameValue(papszAttrib, "pixel.no_data");
     813           1 :     if (pszValue != nullptr)
     814             :     {
     815           0 :         bNoDataSet = true;
     816           0 :         dfNoDataValue = CPLAtof(pszValue);
     817             :     }
     818             : 
     819           1 :     pszValue = CSLFetchNameValue(papszAttrib, "channel.enumeration");
     820           1 :     if (pszValue != nullptr)
     821           1 :         nRawBands = atoi(pszValue);
     822             :     else
     823           0 :         nRawBands = 1;
     824             : 
     825           1 :     if (!GDALCheckBandCount(nRawBands, TRUE))
     826             :     {
     827           0 :         return nullptr;
     828             :     }
     829             : 
     830           1 :     pszValue = CSLFetchNameValue(papszAttrib, "pixel.field");
     831           1 :     if (pszValue != nullptr && strstr(pszValue, "*complex") != nullptr)
     832           0 :         bComplex = true;
     833             :     else
     834           1 :         bComplex = false;
     835             : 
     836             :     /* Get the version number, if present (if not, assume old version. */
     837             :     /* Versions differ in their interpretation of corner coordinates.  */
     838             : 
     839           1 :     if (CSLFetchNameValue(papszAttrib, "version") != nullptr)
     840           1 :         poDS->SetVersion(static_cast<float>(
     841           1 :             CPLAtof(CSLFetchNameValue(papszAttrib, "version"))));
     842             :     else
     843           0 :         poDS->SetVersion(1.0);
     844             : 
     845             :     /* -------------------------------------------------------------------- */
     846             :     /*      Figure out the datatype                                         */
     847             :     /* -------------------------------------------------------------------- */
     848           1 :     const char *pszEncoding = CSLFetchNameValue(papszAttrib, "pixel.encoding");
     849           1 :     if (pszEncoding == nullptr)
     850           0 :         pszEncoding = "{ *unsigned }";
     851             : 
     852           1 :     int nSize = 1;
     853           1 :     if (CSLFetchNameValue(papszAttrib, "pixel.size") != nullptr)
     854           1 :         nSize = atoi(CSLFetchNameValue(papszAttrib, "pixel.size")) / 8;
     855             : #if 0
     856             :     int nPseudoBands;
     857             :     if( bComplex )
     858             :         nPseudoBands = 2;
     859             :     else
     860             :         nPseudoBands = 1;
     861             : #endif
     862             : 
     863             :     GDALDataType eType;
     864           1 :     if (nSize == 1)
     865           1 :         eType = GDT_Byte;
     866           0 :     else if (nSize == 2 && strstr(pszEncoding, "*unsigned") != nullptr)
     867           0 :         eType = GDT_UInt16;
     868           0 :     else if (nSize == 4 && bComplex)
     869           0 :         eType = GDT_CInt16;
     870           0 :     else if (nSize == 2)
     871           0 :         eType = GDT_Int16;
     872           0 :     else if (nSize == 4 && strstr(pszEncoding, "*unsigned") != nullptr)
     873           0 :         eType = GDT_UInt32;
     874           0 :     else if (nSize == 8 && strstr(pszEncoding, "*two") != nullptr && bComplex)
     875           0 :         eType = GDT_CInt32;
     876           0 :     else if (nSize == 4 && strstr(pszEncoding, "*two") != nullptr)
     877           0 :         eType = GDT_Int32;
     878           0 :     else if (nSize == 8 && bComplex)
     879           0 :         eType = GDT_CFloat32;
     880           0 :     else if (nSize == 4)
     881           0 :         eType = GDT_Float32;
     882           0 :     else if (nSize == 16 && bComplex)
     883           0 :         eType = GDT_CFloat64;
     884           0 :     else if (nSize == 8)
     885           0 :         eType = GDT_Float64;
     886             :     else
     887             :     {
     888           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     889             :                  "Unsupported pixel data type in %s.\n"
     890             :                  "pixel.size=%d pixel.encoding=%s",
     891           0 :                  poDS->pszPath, nSize, pszEncoding);
     892           0 :         return nullptr;
     893             :     }
     894             : 
     895             :     /* -------------------------------------------------------------------- */
     896             :     /*      Open the blob file.                                             */
     897             :     /* -------------------------------------------------------------------- */
     898           1 :     osFilename = CPLFormFilenameSafe(poDS->pszPath, "image_data", nullptr);
     899           1 :     if (VSIStat(osFilename.c_str(), &sStat) != 0)
     900           0 :         osFilename = CPLFormFilenameSafe(poDS->pszPath, "blob", nullptr);
     901           1 :     if (poOpenInfo->eAccess == GA_ReadOnly)
     902             :     {
     903           1 :         poDS->fpBlob = VSIFOpenL(osFilename.c_str(), "rb");
     904           1 :         if (poDS->fpBlob == nullptr)
     905             :         {
     906           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     907             :                      "Unable to open file %s for read access.",
     908             :                      osFilename.c_str());
     909           0 :             return nullptr;
     910             :         }
     911             :     }
     912             :     else
     913             :     {
     914           0 :         poDS->fpBlob = VSIFOpenL(osFilename.c_str(), "rb+");
     915           0 :         if (poDS->fpBlob == nullptr)
     916             :         {
     917           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     918             :                      "Unable to open file %s for update access.",
     919             :                      osFilename.c_str());
     920           0 :             return nullptr;
     921             :         }
     922             :     }
     923             : 
     924             :     /* -------------------------------------------------------------------- */
     925             :     /*      Build the overview filename, as blob file = "_ovr".             */
     926             :     /* -------------------------------------------------------------------- */
     927           2 :     std::string osOvrFilename(osFilename);
     928           1 :     osOvrFilename += "_ovr";
     929             : 
     930             :     /* -------------------------------------------------------------------- */
     931             :     /*      Define the bands.                                               */
     932             :     /* -------------------------------------------------------------------- */
     933           1 :     const int nPixelOffset = nRawBands * nSize;
     934           1 :     const int nLineOffset = nPixelOffset * poDS->GetRasterXSize();
     935           1 :     int nOffset = 0;
     936             : 
     937           2 :     for (int iRawBand = 0; iRawBand < nRawBands; iRawBand++)
     938             :     {
     939             :         auto poBand = std::make_unique<HKVRasterBand>(
     940           1 :             poDS.get(), poDS->GetRasterCount() + 1, poDS->fpBlob, nOffset,
     941           1 :             nPixelOffset, nLineOffset, eType, bNative);
     942           1 :         if (!poBand->IsValid())
     943           0 :             return nullptr;
     944             : 
     945           1 :         if (bNoDataSet)
     946           0 :             poBand->SetNoDataValue(dfNoDataValue);
     947           1 :         poDS->SetBand(poDS->GetRasterCount() + 1, std::move(poBand));
     948           1 :         nOffset += GDALGetDataTypeSizeBytes(eType);
     949             :     }
     950             : 
     951           1 :     poDS->eRasterType = eType;
     952             : 
     953             :     /* -------------------------------------------------------------------- */
     954             :     /*      Process the georef file if there is one.                        */
     955             :     /* -------------------------------------------------------------------- */
     956           1 :     osFilename = CPLFormFilenameSafe(poDS->pszPath, "georef", nullptr);
     957           1 :     if (VSIStat(osFilename.c_str(), &sStat) == 0)
     958           1 :         poDS->ProcessGeoref(osFilename.c_str());
     959             : 
     960             :     /* -------------------------------------------------------------------- */
     961             :     /*      Initialize any PAM information.                                 */
     962             :     /* -------------------------------------------------------------------- */
     963           1 :     poDS->SetDescription(osOvrFilename.c_str());
     964           1 :     poDS->TryLoadXML();
     965             : 
     966             :     /* -------------------------------------------------------------------- */
     967             :     /*      Handle overviews.                                               */
     968             :     /* -------------------------------------------------------------------- */
     969           1 :     poDS->oOvManager.Initialize(poDS.get(), osOvrFilename.c_str(), nullptr,
     970             :                                 TRUE);
     971             : 
     972           1 :     return poDS.release();
     973             : }
     974             : 
     975             : /************************************************************************/
     976             : /*                         GDALRegister_HKV()                           */
     977             : /************************************************************************/
     978             : 
     979        1667 : void GDALRegister_HKV()
     980             : 
     981             : {
     982        1667 :     if (GDALGetDriverByName("MFF2") != nullptr)
     983         282 :         return;
     984             : 
     985        1385 :     GDALDriver *poDriver = new GDALDriver();
     986             : 
     987        1385 :     poDriver->SetDescription("MFF2");
     988        1385 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     989        1385 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Vexcel MFF2 (HKV) Raster");
     990        1385 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/mff2.html");
     991             : 
     992        1385 :     poDriver->pfnOpen = HKVDataset::Open;
     993             : 
     994        1385 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     995             : }

Generated by: LCOV version 1.14